能够存储数据、管理数据的一种软件。根据数据不同的特点,选择不同的数据库进行存储。一个项目中也可以使用多个数据库。
Redis就是一个数据库,但是与传统数据库不同的是,他的数据是存在内存上的,因此读写速度快。
单机数据库时代:一个应用对应一个数据库实例。
Memcached时代(缓存时代):缓存可以解决访问效率,但是数据量的增大是他的问题;水平切分时代:分不同的数据库实例。
读写分离时代:实例进行读写分离,有的读,有的写。
分表分库时代(集群):【web2.0上,用户可以参与进来,不是给什么看什么。数据量大了】、
nosql(非关系型数据库)时代:(上面的时代都离不开表,关系型数据库都是以表为单位存储数据的,表和表有关系)
非关系型数据库:彻底改变底层存储机制。不再采用关系数据模型,聚合数据结构存储数据。常见的非关系型数据库有:redis、mongoDB、HBase等。
⚠️:程序是用来处理数据的,处理完之后存储起来。
远程字典服务器:是一个用C语言编写的、开源的、基于内存运行(数据大部分时间都是存在内存中的)并支持持久化的、高性能的NoSQL数据库。
redis(通常叫做缓存数据库)数据大部分时间存储在内存中、适合存储频繁访问、数据量比较小的数据。对于缓存而言,redis又可以持久化
Redis特点:
1)支持数据持久化;
Redis将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
Redis是内存数据库,数据存储在内存中,加快读取速度同时数据安全性产生了问题,即当redis所在服务器发生宕机后,redis数据库中的数据将会全部丢失。为了解决这个问题,redis提供了持久化功能——RDB和AOF(appened only file)
2)支持多种数据结构;
五种数据结构:不仅支持简单的key-value、list、hash、set、zset。
3)支持数据备份;
启动Redis服务
redis服务一般是在服务器上安装的,通过redis客户端去连接服务端。
redis默认安装在了/opt/homebrew/opt/目录下或者/opt/homebrew/Cellar/redis/6.2.6或/opt/homebrew/opt/redis@6.2
配置文件在:/opt/homebrew/etc/redis.conf。
1)后台启动
/ % redis-server &
2)前台启动
/ % redis-server
3)启动redis服务时,指定配置文件(可以指定一些参数)
/ % redis-server /opt/homebrew/etc/redis.conf
// redis服务端启动成功界面:
% redis-server
31144:C 17 Apr 2022 11:46:26.518 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
31144:C 17 Apr 2022 11:46:26.518 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=31144, just started
31144:C 17 Apr 2022 11:46:26.518 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
31144:M 17 Apr 2022 11:46:26.518 * Increased maximum number of open files to 10032 (it was originally set to 256).
31144:M 17 Apr 2022 11:46:26.518 * monotonic clock: POSIX clock_gettime
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 31144
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
31144:M 17 Apr 2022 11:46:26.519 # Server initialized
31144:M 17 Apr 2022 11:46:26.519 * Loading RDB produced by version 6.2.6
31144:M 17 Apr 2022 11:46:26.519 * RDB age 537 seconds
31144:M 17 Apr 2022 11:46:26.519 * RDB memory usage when created 0.98 Mb
31144:M 17 Apr 2022 11:46:26.519 # Done loading RDB, keys loaded: 0, keys expired: 0.
31144:M 17 Apr 2022 11:46:26.519 * DB loaded from disk: 0.001 seconds
31144:M 17 Apr 2022 11:46:26.519 * Ready to accept connections
关闭redis服务
1)通过kill命令:
ps -ef | grep redis
kill -9 pid
2)通过redis命令关闭
redis-cli shutdown
redis客户端
是一个程序,通过网络连接redis服务器,实现和redis服务器的交互。
作用:向服务端发送命令,同时显示Redis服务器的处理结果。
redis-cli是redis自带的客户端
// redis的客户端界面:
// 从这里可以得出:1)redis客户端连接的是127.0.0.1(本机)这台机器上的6379的redis服务;2)默认连接本机上的redis服务(一个redis服务一个端口号)
% redis-cli
127.0.0.1:6379>
// 连接127.0.0.1(本机)的指定端口上的redis服务
% redis-cli -p 端口号
// 连接指定主机(-h host)的指定端口的redis服务
% redis-cli -h ip地址 -p 端口号
退出客户端
# 方式一:
% redis-cli
127.0.0.1:6379> exit
%
# 方式二:
% redis-cli
127.0.0.1:6379> quit
%
redis-benchmark
# 输入ping返回PONG表示正常运行
127.0.0.1:6379> ping
PONG
# 显示redis服务的所有统计信息
127.0.0.1:6379> info
# 也可以查看redis服务指定的统计信息
127.0.0.1:6379> info CPU
# CPU
used_cpu_sys:6.352107
used_cpu_user:8.015013
used_cpu_sys_children:0.003280
used_cpu_user_children:0.001092
数据库实例 :相当于mysql中的,安装一个数据库,可以在里面创建多个数据库实例,eg:study、sys(mysql自己创建和维护的数据库实例)等。
⚠️:redis的数据库实例不能自己创建和修改,只能使用redis服务创建好的数据库实例:默认情况下,redsi会自动创建16个数据库实例,对这些数据库实例进行编号,从0开始(没有名称,只有编号)开发人员使用时,通过编号使用数据库。可以通过配置文件,指定redis自动创建的数据库个数。redis每一个数据库实例,本身占用的存储空间很少,不会浪费太多存储空间。
默认情况下,redis客户端连接是第0号数据库;
切换数据库实例(数据库)
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]>
查看当前数据中,数据条数(所有key的数量)
127.0.0.1:6379> dbsize
(integer) 5
查看当前数据库实例中所有的key
127.0.0.1:6379> keys *
1) "k1"
2) "myhash"
3) "key:__rand_int__"
4) "counter:__rand_int__"
5) "mylist"
清空数据库实例
127.0.0.1:6379> flushdb
OK
27.0.0.1:6379> keys *
(empty array)
# 清空所有数据库实例中的数据⚠️:慎重使用
127.0.0.1:6379> flushall
查看redis中的配置信息
# 查看配置文件信息
127.0.0.1:6379> config get *
127.0.0.1:6379> config get port
1) "port"
2) "6379"
# 设置配置文件参数
127.0.0.1:6379> config set port 6380
OK
127.0.0.1:6380>
程序使用来处理数据的,Redis是用来存储数据的;程序处理完的数据要存储到redis中,不同特点的数据要存储在Redis中不同类型的数据结构中。
string:单key单value
list:单key多value(有序可重复)
hash:适合存储对象
set:无序无重复
zset:sorted set,有序集合
使用redis命令往五种数据结构中存储数据
根据数据结构不同的特点设计了不同的命令。
1.查看数据库中的key: keys pattern
通配符:* :匹配0个或多个字符
?:匹配一个字符
[ ] :匹配中括号中的一个字符
2.判断key在数据库中是否存在
exists key # 如果存在返回1,不存在返回0
exists key [key key key] # 判断多个key是否存在
3.移动指定指定key到指定数据库
move key index
4.查看指定key的剩余生存时间:ttl
返回-1:key没有设置生存时间
返回-2:key不存在
5.设置key的最大生存时间
expire key seconds
expire key2 20
6.查看key的数据类型
type key
7.重命名key:rename key newkey
8.删除key:del key [key key…] 删除指定的key返回值是实际删除的key的数量。
String数据结构是简单的key-value对,value不仅可以是String,也可以是数字
常规key-value缓存应用;常规计数:微博数,粉丝数等。
保存数据:set key value ⚠️:如果之前存在的key会被覆盖
获取数据:get key
追加字符串:apend key value # 返回追加之后字符串长度;不存在创建一个key
获取字符串长度:strlen key
字符串数值加1运算:incr key # 返回加1之后的数据;不存在创建一个key;key是字符串则报错。
字符串减1运算:decr key
字符串数值减offset运算:decrby key offset
字符串数值加offset运算:incrby key offset
获取字符串key中的子字符串:getrange key startIndex endIndex
用value覆盖下表从StratIndex开始的字符串:setrange key startIndex value
设置字符串的同时设置它的最大生命周期:setex key seconds value
设置string类型的数据value到redis中,当key不存在设置成功,存在设置失败。:setnx key value
批量设置string类型数据到redis中:mset key1 value1 key2 value2 …
批量设置sring类型的数据到redis中,所有key都不在现有库中设置成功,否则全部失败。:msetnx key1 value1 key2 value2…
127.0.0.1:6379> set zsage 20 # 设置key
OK
127.0.0.1:6379> get zsage # 获取key的value
"20"
127.0.0.1:6379> append zsage 1
(integer) 3
127.0.0.1:6379> get zsage
"201"
127.0.0.1:6379> strlen zsage
(integer) 3
127.0.0.1:6379> incr zsage
(integer) 202
127.0.0.1:6379> decr zsage
(integer) 201
127.0.0.1:6379> decrby zsage 10
(integer) 191
127.0.0.1:6379> incrby zsage 10
(integer) 201
127.0.0.1:6379> getrange zsage 0 1
"20"
127.0.0.1:6379> setrange zsage 1 314
(integer) 4
127.0.0.1:6379> get zsage
"2314"
127.0.0.1:6379> setex k2 40 313
OK
127.0.0.1:6379> ttl k2
(integer) 27
127.0.0.1:6379> setnx k2 20
(integer) 1
127.0.0.1:6379> setnx k2 20
(integer) 0
127.0.0.1:6379> get k2
"20"
127.0.0.1:6379> setnx k2 101
(integer) 0
127.0.0.1:6379> get k2
"20"
127.0.0.1:6379> mset k3 v3 k4 v4
OK
127.0.0.1:6379> keys *
1) "zsage"
2) "k4"
3) "k3"
4) "k2"
127.0.0.1:6379> set k1 v1 k2 v2
(error) ERR syntax error
127.0.0.1:6379> msetnx k1 v1 k2 v2
(integer) 0
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k2
"20"
Redis hash是string类型的field(字段)和value(值)的映射表。
hash适合存储对象,比如可以使用hash数据结构存储用户信息,商品信息等。
# hdel
127.0.0.1:6379[3]> hset myhash field1 "foo"
(integer) 1
127.0.0.1:6379[3]> hdel myhash field1
(integer) 1
127.0.0.1:6379[3]> hdel myhash field1
(integer) 0
# hexists
127.0.0.1:6379[3]> hset myhash field1 "foo"
(integer) 1
127.0.0.1:6379[3]> hdel myhash field1
(integer) 1
127.0.0.1:6379[3]> hdel myhash field1
(integer) 0
127.0.0.1:6379[3]> hset myhash field1 "foo"
(integer) 1
127.0.0.1:6379[3]> hexists myhash field1
(integer) 1
127.0.0.1:6379[3]> exists myhash # 仅仅判断是否有这个key,不能判断是否有这个field
(integer) 1
127.0.0.1:6379[3]> hdel myhash field1
(integer) 1
# hget
127.0.0.1:6379[3]> hset site redis "redis.com"
(integer) 1
127.0.0.1:6379[3]> hget site redis
"redis.com"
# hgetall
# 同时返回字段和字段值
127.0.0.1:6379[3]> hgetall site
1) "redis"
2) "redis.com"
3) "field2"
4) "Hello"
5) "field3"
6) "World"
# hincrby
127.0.0.1:6379[3]> hgetall myhash
1) "field1"
2) "5"
3) "field2"
4) "1"
5) "field3"
6) "9"
127.0.0.1:6379[3]> hincrby myhash field1 3
(integer) 8
127.0.0.1:6379[3]> hget myhash field1
"8"
# hincrbyfloat
127.0.0.1:6379[3]> hset myhash2 field3 10.50
(integer) 1
127.0.0.1:6379[3]> hincrbyfloat myhash2 field3 0.1
"10.59999999999999964"
127.0.0.1:6379[3]> hincrbyfloat myhash2 field3 -5
"5.59999999999999964"
127.0.0.1:6379[3]> hset mykey field 5.0e3
(integer) 1
127.0.0.1:6379[3]> hget mykey field
"5.0e3"
127.0.0.1:6379[3]> hincrbyfloat mykey field 2.0e2
"5200"
# hkeys
127.0.0.1:6379[3]> keys * # 获取所有key
1) "site"
2) "myhash"
3) "mykey"
4) "myhash2"
127.0.0.1:6379[3]> hkeys myhash2 # 获取一个hash表中的所有字段
1) "field1"
2) "field3"
# hscan
127.0.0.1:6379[3]> hmset sites google google.com runoob runoob.com weibo weibo.com 4 taobao.com
OK
127.0.0.1:6379[3]> hscan sites 0 match run*
1) "0"
2) 1) "runoob"
2) "runoob.com"
Redis列表是字符串列表,按照插入顺序排序。可以添加一个元素到列表头部(左边)或者尾部(右边)。{有序可重复}
lset key index value # 通过索引设置列表元素的值
lpush key value1 [value2] # 将一个或多个值插入到列表头部
lpushx key value1 # 将一个值插入列表头部
rpop key # 移除列表的最后一个元素,返回值为移除的元素
rpush key value1 [value2] # 在列表中添加一个或多个值
rpushx key value # 为已存在的列表添加值
rpoplpush source destination # 移除最后一个元素,将该元素添加到另一个列表并返回
brpoplpush source destination timeout # 移除最后一个元素,将该元素添加到另一个列表并返回;如果列表中没有元素会阻塞列表直到等待超时或发现可弹出元素为止。⛴
llen key # 获取列表长度
lrange key start stop # 获取列表指定范围内的元素
lpop key # 移出并获取列表第一个元素
blpop key1 [key2] timeout # 移出并获取列表的第一个元素,如果没有元素会阻塞列表直到等待超时或发现有可弹出元素为止
brpop key1 [key2] timeout # 移出并获取列表的最后一个元素,如果没有元素会阻塞列表直到等待超时或发现有可弹出元素为止
lrem key count value # 移除列表元素
ltrim key start stop # 对一个列表进行修剪(trim),就是让列表保持指定区间内的元素,不在指定区间之内的元素被删除
lindex key index # 通过索引获取列表中的元素
linsert key before|after pivot value # 在列表前或者后插入元素
# blpop 弹出第一个元素 brpop 弹出最后一个元素
127.0.0.1:6379[3]> rpush list2 100 200 300 400 500
(integer) 5
127.0.0.1:6379[3]> blpop list2 10 # 弹出并返回列表第一个元素,否则操作会被阻塞,等待10秒返回nil
1) "list2"
2) "100"
127.0.0.1:6379[3]> brpop list2 5
1) "list2"
2) "500"
# brpoplpush
127.0.0.1:6379[3]> brpoplpush list2 list3 5 # 弹出list2中的最后一个元素,压入list3中的第一个位置,否则5秒钟后返回
"400"
127.0.0.1:6379[3]> brpoplpush list4 list5 7
(nil)
(7.03s)
# lrange llen lindex
127.0.0.1:6379[3]> lrange list2 0 -1
1) "200"
2) "300"
127.0.0.1:6379[3]> llen list2
(integer) 2
127.0.0.1:6379[3]> lindex list2 0
"200"
# lrange lpop blpop
127.0.0.1:6379[3]> lrange list2 0 -1
1) "90"
2) "80"
3) "70"
4) "60"
5) "50"
6) "40"
7) "30"
8) "20"
9) "10"
10) "200"
11) "300"
# lpop blpop区别,如果不能正常弹出blpop有超时时间参数
127.0.0.1:6379[3]> lpop list2
"90"
127.0.0.1:6379[3]> blpop list2 5
1) "list2"
2) "80"
# linsert
127.0.0.1:6379[3]> lrange list3 0 -1
1) "400"
2) "Hello"
3) "world"
127.0.0.1:6379[3]> linsert list3 before world There
(integer) 4
127.0.0.1:6379[3]> lrange list3 0 -1
1) "400"
2) "Hello"
3) "There"
4) "world"
特点:
Redis的Set是无序集合。集合成员是唯一的。
一个key对应多个value
集合对象的编码可以是hashtable或者intset
Redis中集合是通过哈希表实现的,所以添加、删除、查看的复杂度都是o(1)。
命令:
sadd key member1 [memeber2] # 向集合中添加一个或多个元素
scard key # 获取元素的成员数
sdiff key1 [key2] # 返回第一个集合和其他集合的差异
sdiffstore destination key1 [key2] # 返回给定所有集合的差集并存储在destination中
sinter key1 [key2] # 返回给定所有集合的交集
sismember key member # 判断member元素是否是集合key的成员
smembers key # 返回集合中的所有成员
smove source destination member # 将member元素从source移动到destination集合
spop key # 移除并返回集合中的一个随机元素
srandmember key [count] # 返回集合中一个或多个随机成员
srem key member1 [member2] # 移除集合中一个或多个成员
sunion key1 [key2] # 返回所有给定集合的并集
sunionstore destination key1 [key2] # 所有给定集合的并集存储在destination中
sscan key cursor [MATCH pattern] [COUNT count] # 迭代集合中的元素。cursor:游标;pattern:匹配模式;count:指定从数据集中返回多少元素,默认10
127.0.0.1:6379> sadd k1 v1 v2 v3 v4
(integer) 4
127.0.0.1:6379> scard k1
(integer) 4
127.0.0.1:6379> sadd k2 v1 v2 v3
(integer) 3
127.0.0.1:6379> sdiff k1 k2
1) "v4"
127.0.0.1:6379> sdiffstore k3 k1 k2
(integer) 1
127.0.0.1:6379> sinter k1 k3
1) "v4"
127.0.0.1:6379> smembers k3
1) "v4"
127.0.0.1:6379> smembers k1
1) "v3"
2) "v2"
3) "v1"
4) "v4"
127.0.0.1:6379> smembers k2
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379> sismember k1 v3
(integer) 1
127.0.0.1:6379> sadd k1 zhangsan
(integer) 1
127.0.0.1:6379> smembers k1
1) "v3"
2) "v2"
3) "v1"
4) "v4"
5) "zhangsan"
127.0.0.1:6379> smove k1 k2 zhangsan
(integer) 1
127.0.0.1:6379> smembers k2
1) "zhangsan"
2) "v3"
3) "v2"
4) "v1"
127.0.0.1:6379> spop k2
"v2"
127.0.0.1:6379> srandmember k1 2
1) "v3"
2) "v1"
127.0.0.1:6379> srem k1 v1 v3
(integer) 2
127.0.0.1:6379> smembers k1
1) "v2"
2) "v4"
127.0.0.1:6379> sunion k1 k2
1) "zhangsan"
2) "v3"
3) "v2"
4) "v4"
5) "v1"
127.0.0.1:6379> smembers k1
1) "v2"
2) "v4"
127.0.0.1:6379> smembers k2
1) "zhangsan"
2) "v3"
3) "v1"
127.0.0.1:6379> sunionstore k4 k1 k2
(integer) 5
127.0.0.1:6379> smembers k4
1) "zhangsan"
2) "v3"
3) "v2"
4) "v4"
5) "v1"
127.0.0.1:6379> keys *
1) "k2"
2) "k4"
3) "k3"
4) "k1"
# sscan
127.0.0.1:6379[2]> smembers myset1
1) "taobao"
2) "runoob"
3) "googel"
127.0.0.1:6379[2]> sscan myset1 0 match r*
1) "0"
2) 1) "runoob"
127.0.0.1:6379[2]> smembers myset1
1) "taobao"
2) "runoob"
3) "rootroot"
4) "googel"
127.0.0.1:6379[2]> sscan myset1 0 match r*
1) "0"
2) 1) "runoob"
2) "rootroot"
特点:
Redis有序集合和集合一样也是string类型的元素的集合,且不允许重复的成员
不同的是每个元素都会关联一个double类型的分数。redis通过这个分数为集合中的成员进行大小排序。
有序集合中的元素是唯一的,但分数(score)可以重复。
集合是通过哈希表实现的,因此添加、删除、查看的复杂度都是o(1)。
命令:
zadd key score1 member1 [ score2 member2] # 向有序集合中添加一个或多个成员,或更新以存在成员的分数
zcard key # 获取有序集合的成员数
zcount key min max # 计算指定分数区间的成员数
zincrby key increment member # 有序集合中指定成员的分数加上增量increment
zinterstore destination numkeys key [key …] # 计算给定的一个或多个有序集的交集并将结果存储在新的有序集合destination中
zlexcount key min max # 在有序集合中计算指定字典区间内成员数量
zrange key start stop [WITHSORES] # 通过索引区间返回有序集合指定区间的成员
zrangebylex key min max [limit offset count] # 通过字典区间返回有序集合的成员
zrangebyscore key min max [withscores] [limit] # 通过分数返回有序集和指定区间的成员
zrank key member # 返回有序集合中指定成员的索引
zrem key member [member…] # 移除有序集合中一个或多个元素
zremrangebylex key min max # 移除有序集合中给定的字典区间的成员。
zremrangebyrank key start stop # 移除指定排名区间的所有成员
zrevrange key start stop [withscores] # 返回有序集中,指定区间内的成员
zrevrangebyscore key max min [withscores] # 返回有序集和中指定成员的排名,有序集成员按分数值递减(从大到小)排序
zrevrank key member # 返回有序集中成员按分数值递减(从大到小 降序)排序的排名。
zscore key member # 返回有序集中,成员的分数值。成员不存在返回nil
zunionstore destination numkeys key [key…] [weights] # 计算给定有序集的并集,key的数量由numkeys指定,将得到的并集存储到destination中。默认结果集中的成员的分数值是所有给定集下该元素的分数值之和。
zscan key cursor [match pattern] [count count] # 迭代有序集和中的元素(包括元素和元素分值)cursor:游标;pattern:匹配的模式;count:指定从数据集里返回多少元素,默认为10.
# zrangebyscore
127.0.0.1:6379[2]> zadd salary 2500 jack
(integer) 1
127.0.0.1:6379[2]> zadd salary 5000 tom 12000 peter
(integer) 2
127.0.0.1:6379[2]> zrangebyscore salary -inf +inf # 显示整个有序集
1) "jack"
2) "tom"
3) "peter"
127.0.0.1:6379[2]> zrangebyscore salary -inf +inf withscores # 显示整个有序集及成员的score值
1) "jack"
2) "2500"
3) "tom"
4) "5000"
5) "peter"
6) "12000"
127.0.0.1:6379[2]> zrangebyscore salary -inf 5000 withscores # 显示工资 <= 5000的所有成员及score值
1) "jack"
2) "2500"
3) "tom"
4) "5000"
127.0.0.1:6379[2]> zrangebyscore salary (5000 40000 # 显示工资大于5000小于等于40000的成员
1) "peter"
# zrank
127.0.0.1:6379[2]> zrange salary 0 -1 withscores
1) "jack"
2) "2500"
3) "tom"
4) "5000"
5) "peter"
6) "12000"
127.0.0.1:6379[2]> zrank salary tom # 显示tom的薪水排名,第二
(integer) 1
# zremrangelex
127.0.0.1:6379[2]> zadd myzset 0 foo 0 zap 0 alpha 0 alpha
(integer) 3
127.0.0.1:6379[2]> zrange myzset 0 -1
1) "a"
2) "aaaa"
3) "alpha"
4) "b"
5) "c"
6) "e"
7) "f"
8) "foo"
9) "g"
10) "zap"
127.0.0.1:6379[2]> zremrangebylex myzset [alpha [omega
(integer) 7
127.0.0.1:6379[2]> zrange myzset 0 -1
1) "a"
2) "aaaa"
3) "zap"
# zremrangebyrank
127.0.0.1:6379[2]> zrange salary 0 -1
1) "jack"
2) "tom"
3) "peter"
127.0.0.1:6379[2]> zremrangebyrank salary 0 1
(integer) 2
127.0.0.1:6379[2]> zrange salary 0 -1
1) "peter"
# zremrangebyscore
127.0.0.1:6379[2]> zrange salary 0 -1
1) "Tom"
2) "Jack"
3) "peter"
127.0.0.1:6379[2]> zremrangebyscore salary 1500 5000
(integer) 2
127.0.0.1:6379[2]> zrange salary 0 -1
1) "peter"
# zrange zrevrange
127.0.0.1:6379[2]> zrange salary 0 -1 withscores # 升序排列
1) "tom"
2) "4000"
3) "jack"
4) "5000"
5) "peter"
6) "12000"
127.0.0.1:6379[2]> zrevrange salary 0 -1 withscores # 降序排列
1) "peter"
2) "12000"
3) "jack"
4) "5000"
5) "tom"
6) "4000"
# zrevrangebyscore
127.0.0.1:6379[2]> zrevrangebyscore salary +inf -inf # 逆序排列所有成员
1) "peter"
2) "jack"
3) "tom"
4) "joe"
127.0.0.1:6379[2]> zrevrangebyscore salary 10000 2000 # 逆序排列薪水介于10000 和 2000 之间的成员
1) "jack"
2) "tom"
3) "joe"
# zrevrank
127.0.0.1:6379[2]> zrange salary 0 -1 withscores
1) "joe"
2) "3500"
3) "tom"
4) "4000"
5) "jack"
6) "5000"
7) "peter"
8) "12000"
127.0.0.1:6379[2]> zrevrange salary 0 -1 withscores
1) "peter"
2) "12000"
3) "jack"
4) "5000"
5) "tom"
6) "4000"
7) "joe"
8) "3500"
# zscore
127.0.0.1:6379[2]> zscore salary tom
"4000"
# zunionscore
127.0.0.1:6379[2]> zrange zset1 0 -1 withscores
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3" 127.0.0.1:6379[2]> zadd zset2 1 one 2 two
(integer) 2
127.0.0.1:6379[2]> zunionstore out 2 zset1 zset2 weights 2 3
(integer) 3
127.0.0.1:6379[2]> zrange out 0 -1 withscores
1) "one"
2) "5"
3) "three"
4) "6"
5) "two"
6) "10"
# zscan
127.0.0.1:6379[2]> zadd site 1 google 2 runoob 3 taobao 4 weibo
(integer) 4
127.0.0.1:6379[2]> zscan site 0 match r*
1) "0"
2) 1) "runoob"
2) "2"
redis.conf
port:指定redis监听端口,默认6379
bind:绑定主机地址(只要是redis服务所在主机上任何一个ip)
⚠️:配置了port和bind,redis客户端连接redis服务时,必须指定端口和ip
# 连接客户端:
redis-cli -h 10.4.216.251 -p 6380
# 关闭redis服务
redis-cli -h 10.4.216.251 -p 6380 shutdown
tcp-keepalive:TCP连接保活策略。
loglevel:配置日志级别。开发阶段设置成debug,生产阶段通常设置为notice/warning
logfile:指定日志文件。
databases:配置redis服务默认创建的数据库实例个数,默认16.
requirepass:设置密码 -a(auth),默认不使用。此参数必须在protected-mode=yes时才能起作用。
redis-cli -h 10.4.216.251 -p 6380 -a 123456
在指定时间间隔内,redis服务执行指定次数的写操作之后,会触发一次持久化操作(将内存中的数据持久化到磁盘中)这种持久化策略默认开启。
save 3600 1 # 在3600秒(60分钟,一个小时)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照
save 300 100 # 在300秒(5分钟)之后,如果至少有100个key发生变化,Redis就会自动触发BGSAVE命令创建快照
save 60 10000 # 在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照
AOF采用操作日志记录进行每一次写操作。
AOF相较RDB,AOF的实时性更好,因此成为主流的持久化方案。默认Redis没有开启AOF(append only file)方式的持久化,效率较RDB低。通过appendonly参数可以开启:
appendonly yes
开启AOF持久化之后,每执行一条会改变redis中数据的命令,Redis就会将该命令写入硬盘中的AOF文件中。AOF文件和RDB文件的保存位置相同,都是通过dir参数设置,默认文件名为“appendonly.aof”
redis配置文件中三种不同的AOF持久化:
appendfsync always # 每次数据修改都会写入.aof文件中,效率严重下降
appendfsync everysec # 每秒钟同步一次,显式的将多个写命令同步到硬盘中
appendfsync no # 让操作系统决定,何时进行同步
为了兼顾数据和写入性能,用户可以考虑使用appendfsync everysec,让redsi每秒同步一次aof文件,性能几乎不会受到影响。
Redis 4.0对持久化进行了优化:
支持RDB和AOF的混合持久化(默认关闭,可以使用aof-use-rdb-preamble开启)
开启混合持久化,AOF重写的时候就直接将RDB的内容写到AOF文件的开头。这样做结合了RDB和AOF的优点,快速加载的同时避免过多数据的丢失。缺点:AOF里的RDB部分的压缩格式不再是AOF格式,可读性差。
AOF重写:
AOF重写产生的新的AOF文件保存数据和原有的AOF文件保存的数据库状态一样,但体积更小。这里的重写这个名字是有歧义的,这个功能是对数据库中的键值对读取的。程序对AOF文件没有操作。
只要是数据库,就会有事务
事务:一组命令放在一块执行,保证操作的原子性。这多个命令要么同时成功,要么同时失败
Redis事务:可以一次执行多个命令,把命令序列化,然后一起执行,保证了事务的部分原子性
部分原子性:a)一组命令中,压入队列过程中发生错误,事务中所有命令不执行
b)压入队列正常,在执行事务时发生错误,只会影响错误命令,不会影响事务的其他命令的 执行。这就是Redis事务的部分原子性。
Redis事务(transaction)带有一下三种保证:
# 开始事务
127.0.0.1:6379> multi
OK
# 命令入队
127.0.0.1:6379(TX)> set book-name "Mastering C++ in 21 days"
QUEUED
127.0.0.1:6379(TX)> get book-name
QUEUED
127.0.0.1:6379(TX)> sadd tag "C++" "Programing" "Mastering Series"
QUEUED
127.0.0.1:6379(TX)> smembers tag
QUEUED
# 执行事务
127.0.0.1:6379(TX)> exec
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "Programing"
3) "C++"
# 开始事务
127.0.0.1:6379> multi
OK
# 命令入队
127.0.0.1:6379(TX)> set a aaa
QUEUED
127.0.0.1:6379(TX)> set b bbb
QUEUED
127.0.0.1:6379(TX)> set c ccc
QUEUED
# 执行事务
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
discard命令:
清除所有已经压入队列的命令,并且结束整个事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> ping
QUEUED
127.0.0.1:6379(TX)> set greeting hello
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> exists greeting
(integer) 0
watch命令:
监控某一个或多个键,当事务执行之前,此键的值发生变化,则事务放弃执行;否则,正常执行。(监控一个变量,看别的事务有没有更改这个变量,如果别的事务更改了这个变量,则放弃执行)
Redis是缓存数据库,但是他提供这种机制。
# 第一个redis-cli客户端
127.0.0.1:6379> watch version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby balance 50
QUEUED
127.0.0.1:6379(TX)> decrby balance2 50
QUEUED
127.0.0.1:6379(TX)> incr version
QUEUED
# 第二个redis-cli客户端
127.0.0.1:6379> incr version
(integer) 2
# 第一个redis-cli客户端
127.0.0.1:6379(TX)> exec
(nil) # 返回nil表示事务执行放弃
127.0.0.1:6379> mget balance balance2
1) "100"
2) "200"
127.0.0.1:6379> get version
"2"
unwatch命令:
放弃监所有的键
# 第一个redis-cli客户端
127.0.0.1:6379> watch version
OK
127.0.0.1:6379> unwatch version
(error) ERR wrong number of arguments for 'unwatch' command
127.0.0.1:6379> unwatch version
(error) ERR wrong number of arguments for 'unwatch' command
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby balance 50
QUEUED
127.0.0.1:6379(TX)> incrby balance2 50
QUEUED
# 第二个redis-cli客户端
127.0.0.1:6379> incr version
(integer) 3
# 第一个redis-cli客户端
127.0.0.1:6379(TX)> exec
1) (integer) 50
2) (integer) 250
事务小结
1)单独的隔离操作
事务中的所有命令都会序列化、顺序的执行。事务在执行过程中不会被其他客户端发来的请求打断。除非使用watch命令监控某些键。
2)不保证事务的原子性
redis同一个事务如果一条命令执行失败,其他命令有可能继续执行。redis事务没有回滚。Redis已经在内部进行功能的简化,确保运行速度,因为Redis不需要事务回滚的能力。
Redis客户端之间消息通信
redis客户端订阅频道,消息发布者往频道上发布消息,所有订阅此频道的客户端都可以接收到此信息。
subscribe:订阅一个或多个频道(channel)
psubscrible:订阅一个或多个频道的消息,频道名支持通配符。
publish:在指定频道上发布消息
# 第一个redis-cli客户端
127.0.0.1:6379> subscribe runoobChat # 订阅者
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "runoobChat"
3) (integer) 1
# 第二个redis-cli客户端
# 在runoobChat频道发布两次消息,订阅者就能接收到信息
127.0.0.1:6379> publish runoobChat "Redis PUBLISH test" # 发布者
(integer) 1
127.0.0.1:6379> publish runoobChat "Learn redis by runoob.com"
(integer) 1
# 第一个redis-cli客户端
127.0.0.1:6379> subscribe runoobChat # 订阅者
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "runoobChat"
3) (integer) 1
1) "message" # 订阅者接收到的发布者发布的信息
2) "runoobChat"
3) "Redis PUBLISH test"
1) "message"
2) "runoobChat"
3) "Learn redis by runoob.com"
主库的写完以后复制到从库。主少从多、主写从读、主写分离。
主机数据更新后,自动同步到从机的master/slave机制。Master以写为主,Slave以读为主。
搭建一主二从的Redis集群
使用Redis官方推荐的Jedis,在Java应用中操作Redis。其实Jedis就是一个Jar包。使用Jedis中的将Redis命令封装成的方法操作Redis。
下载jedis.jar包,放至:classpath下。
import redis.clients.jedis.Jedis;
import java.util.Set;
public class test_jedis {
public static void main(String[] args) {
// 连接本地redis服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
System.out.println("服务正在运行:" + jedis.ping());
// string
jedis.set("k1", "v1");
System.out.println("String测试:" + jedis.get("k1"));
// set
jedis.set("k1", "v1");
jedis.mset("k2", "v2", "k3", "v3");
System.out.println(jedis.get("k2"));
jedis.sadd("myset", "v1", "v2", "10", "8", "7", "6");
Set<String> members = jedis.smembers("myset");
System.out.println(members);
}
}
// 运行结果:
/**
* 连接成功
* 服务正在运行:PONG
* String测试:v1
* v2
* [v2, v3, 7, 10, v1, 8, 6]
*
* Process finished with exit code 0
*/
乐观锁:
悲观锁: