有序集合

概念

属于集合,保留了集合中的特点,数据不允许重复写入,但是在set的基础上实现了数据的有序化,通过score(分值)实现数据的有序化,写入到有序集合中的字符串,叫做一个元素,一个元素有且仅有一个分值与之对应,分值只能是数值,但是元素可以是字符串

元素不会重复,但是分值可以重复

建立的语法

zadd key score nember [score member] [nx|xx] [ch] [incr]
nx:不存在才能设置成功
xx:存在才能设置成功
ch:表示进行操作的元素和分值发生变化的个数
incr:对指定元素的分值进行增加

127.0.0.1:6379> zadd userranking 80 cyj 81 pjf 82 mp 83 fy 84 cy 84 wd
(integer) 6
127.0.0.1:6379> zadd userranking incr 1 mp        #82增加1
"83"
127.0.0.1:6379> zadd userranking 80 cyj 81 pjf 82 mp 83 fy 84 cy 84 wd
(integer) 0            #写入重复值会报没有改变
127.0.0.1:6379> zadd userranking1 xx 80 cyj 81 pjf 82 mp 83 fy 84 cy 84 wd
(integer) 0   #加xx,这些元素值在userranking1中不存在,所以没有改变
127.0.0.1:6379> zadd userranking ch 84  mp
(integer) 1            #有变化
127.0.0.1:6379> zadd userranking 87  mp
(integer) 0        #不加ch没有变化
127.0.0.1:6379> zrange userranking 0 -1   #查看字符串
1) "cyj"
2) "pjf"
3) "fy"
4) "cy"
5) "mp"
6) "wd"
127.0.0.1:6379> zrange userranking 0 -1 withscores   #查看字符串分值
 1) "cyj"
 2) "80"
 3) "pjf"
 4) "81"
 5) "fy"
 6) "83"
 7) "cy"
 8) "84"
 9) "mp"
10) "84"
11) "wd"
12) "84"

对有序集合中的值的统计

zcard key #计算成员的个数

127.0.0.1:6379> zcard userranking    #不包含分值
(integer) 6

zscore key member #求某个成员的分数

127.0.0.1:6379> zscore userranking mp   #找到
"84"

zrank key member #由低到高,升序查看

127.0.0.1:6379> zrank userranking mp
(integer) 4

zrevrank key member #由高到低,降序查看

127.0.0.1:6379> zrevrank userranking mp
(integer) 1

删除成员

zrem key member [member...]

127.0.0.1:6379> zrem userranking cy
(integer) 1
127.0.0.1:6379> zrange userranking 0 -1
1) "cyj"
2) "pjf"
3) "fy"
4) "mp"
5) "wd"

查看制定排名范围的成员

zrange key start end [withscores] 升序查看

127.0.0.1:6379> zrange userranking 0 -1 withscores
 1) "cyj"
 2) "80"
 3) "pjf"
 4) "81"
 5) "fy"
 6) "83"
 7) "mp"
 8) "84"
 9) "wd"
10) "84"

zrevrange key start end [withscores] 降序查看

127.0.0.1:6379> zrevrange userranking 0 -1 withscores
 1) "wd"
 2) "84"
 3) "mp"
 4) "84"
 5) "fy"
 6) "83"
 7) "pjf"
 8) "81"
 9) "cyj"
10) "80"

查看指定分值范围内的成员

zrangebyscore key min max [withscores] [limit offset count] #升序查看

127.0.0.1:6379> zrangebyscore userranking 80 83 withscores   #升序查看80-83
1) "cyj"
2) "80"
3) "pjf"
4) "81"
5) "fy"
6) "83"
127.0.0.1:6379> zrangebyscore userranking (80 83 withscores  #表示不包含80包含83
1) "pjf"
2) "81"
3) "fy"
4) "83"
127.0.0.1:6379> zrangebyscore userranking (80 (83 withscores #表示不包含80和83,但是在80-83之间,包括小数点
1) "pjf"
2) "81"
127.0.0.1:6379> zrangebyscore userranking (80 +inf withscores #+inf表示正无穷,显示大于80的
1) "pjf"
2) "81"
3) "fy"
4) "83"
5) "mp"
6) "84"
7) "wd"
8) "84"
127.0.0.1:6379> zrangebyscore userranking -inf 80 withscores    #小于80的,-inf表示负无穷
1) "cyj"
2) "80"

+inf:表示正无穷
-inf:表示负无穷
(:表示给数据设置开区间

zrevzrangebyscore key max min [withscores] [limit offset count] #降序查看

用法同上

删除指定排名内的升序元素

zremrangebyrank key min max

127.0.0.1:6379> zremrangebyrank userranking 0 1            #删除排序前两个
(integer) 2
127.0.0.1:6379> zrange userranking 0 -1
1) "fy"
2) "mp"
3) "wd"

删除指定分值范围内的元素

zremrangebyscore key min max

127.0.0.1:6379> zremrangebyscore userranking 83 84        #删除分值为83-84的
(integer) 3
127.0.0.1:6379> zrange userranking 0 -1 withscores    #已经全部删没了
(empty list or set)

交集

zinterstore destination numkeys key key... [weights weight] [aggregate sum|min|max]

destination:保存交集或者并集的集合名
numkeys:进行交集或者并集运算的集合数量
weights weight:给集合设置权重,默认为1
aggregate:求完交集或者并集分值按照 和|最小值|最大值进行选择

127.0.0.1:6379> zadd gongzi:11 13000 user1 15000 user2 18000 user3  #创建有序集合
(integer) 3
127.0.0.1:6379> zadd gongzi:12 16000 user1 12000 user4 13000 user5  #创建有序集合
(integer) 3
127.0.0.1:6379> zinterstore gongzi11:12 2 gongzi:11 gongzi:12 weights 1 0.8 aggregate sum
(integer) 1
#计算gongzi:11和gongzi:12两个集合中的交集,gongzi:11中的分值乘以1的权重,gognzi:12中的分值乘以0.8的权重
#起始就是计算user1员工两个月的工资和
127.0.0.1:6379> keys *
1) "gongzi11:12"
2) "gongzi:12"
3) "gongzi:11"
127.0.0.1:6379> zrange gongzi11:12 0 -1 withscores
1) "user1"
2) "25800"

并集

zunionstore destination numkeys key key... [weights weight] [aggregate sum|min|max]

127.0.0.1:6379> zunionstore gongzi11:12b 2 gongzi:11 gongzi:12 weights 1 0.8 aggregate max
(integer) 5
#计算两个集合中每个员工工资的最大值
127.0.0.1:6379> zrange gongzi11:12b 0 -1 withscores
 1) "user4"
 2) "9600"
 3) "user5"
 4) "10400"
 5) "user1"
 6) "13000"
 7) "user2"
 8) "15000"
 9) "user3"
10) "18000"

内部编码

ziplist 压缩列表,当集合的元素小于128个且每个元素都小于64字节使用ziplist压缩列表
skiplist 当不满足ziplist条件,使用skiplist作为内部编码,如果继续使用,ziplist将会影响数据存放效率


redis数据库额外操作

redis中最多可以拥有16个数据库,切换数据库的语法
select dbid(数据库编号0-15)

127.0.0.1:6379> select 15
OK
127.0.0.1:6379[15]> 

刷新数据库

flushall//flushdb #all清空所有库,db删除当前库中数据

持久化

概念

将内存中的数据放到硬盘中实现数据的长久保存,避免因为进程退出导致的数据丢失问题,持久化在redis中分为两种方式rdb(快照)和aof(日志)

rdb:

将当前进程的数据拍成快照,保存到硬盘,触发rdb的方式分为手动和自动

关于配置文件中

dbfilename dump.rdb #设置rdb持久化文件的文件名,是二进制文件
dir ./ #读取和保存持久化文件的路径

==当启动redis时在哪个路径,rdb文件将会出现在哪个路径,我的默认出现在root目录下==

触发方式

命令触发(手动触发)

save #阻塞当前服务器,知道持久化完成后结束阻塞
因为如果数据过多,redis是单线程服务,将什么都不能做

127.0.0.1:6379> save
OK
日志中可以看到
69845:M 18 Dec 2019 15:12:32.600 * DB saved on disk        #数据已保存在硬盘

bgsave #redis执行fork操作创建子进程,rdb持久化过程由子进程负责,完成子进程自动退出(生成环境使用)

127.0.0.1:6379> bgsave
Background saving started    #后端保存已开启
日志中可以看到
69845:M 18 Dec 2019 15:14:03.082 * Background saving started by pid 69877
父进程69845宣告子进程69877在后端进行保存数据
69877:C 18 Dec 2019 15:14:03.087 * DB saved on disk
子进程宣告数据保存在硬盘
69877:C 18 Dec 2019 15:14:03.087 * RDB: 0 MB of memory used by copy-on-write
子进程宣告在硬盘中写入多少数据(可以节省内存,和父进程共用一个内存,占用内存少)写时复制技术
69845:M 18 Dec 2019 15:14:03.184 * Background saving terminated with success
父进程宣告后端写入成功,子进程自动退出

自动触发

1、在m秒内存在n次修改,自动触发bgsave

关于配置文件中redis.conf
save 900 1 900s内1个更改
save 300 10 10s内300个更改
save 60 10000 60s内10000个更改

日志中关于自动触发的记录

68824:M 18 Dec 2019 14:03:19.075 * 1 changes in 900 seconds. Saving...
68824:M 18 Dec 2019 14:03:19.075 * Background saving started by pid 69137
69137:C 18 Dec 2019 14:03:19.081 * DB saved on disk
69137:C 18 Dec 2019 14:03:19.083 * RDB: 0 MB of memory used by copy-on-write
68824:M 18 Dec 2019 14:03:19.176 * Background saving terminated with success

68824:M 18 Dec 2019 13:48:18.250 * 10 changes in 300 seconds. Saving...
68824:M 18 Dec 2019 13:48:18.253 * Background saving started by pid 68978
68978:C 18 Dec 2019 13:48:18.258 * DB saved on disk
68978:C 18 Dec 2019 13:48:18.260 * RDB: 2 MB of memory used by copy-on-write
68824:M 18 Dec 2019 13:48:18.357 * Background saving terminated with success

2、从服务器第一连接主服务器时,主服务器自动bgsave

3、使用debug reload 进行操作,触发save操作(不对外提供服务可使用),会产生阻塞(不建议使用此操作)

127.0.0.1:6379> debug reload
OK
查看日志
70282:M 18 Dec 2019 15:58:15.685 * DB loaded from disk: 0.001 seconds
70282:M 18 Dec 2019 15:58:15.685 * Ready to accept connections
70282:M 18 Dec 2019 15:58:24.043 * DB saved on disk
70282:M 18 Dec 2019 15:58:24.044 # DB reloaded by DEBUG RELOAD

4、执行shutdown时,如果aof持久没有开启,自动bgsave

127.0.0.1:6379> shutdown        #断开redis连接
not connected> 
查看日志
69845:M 18 Dec 2019 15:46:01.338 # User requested shutdown...
69845:M 18 Dec 2019 15:46:01.338 * Saving the final RDB snapshot before exiting.
69845:M 18 Dec 2019 15:46:01.340 * DB saved on disk
69845:M 18 Dec 2019 15:46:01.340 * Removing the pid file.
69845:M 18 Dec 2019 15:46:01.340 # Redis is now ready to exit, bye bye...

bgsave工作流程

1)当执行bgsave时,父进程准备生成子进程,如果当前有正在执行持久化的子进程,不做任何操作直接返回
2)父进程通过fork操作生成子进程,fork过程数据重量级操作会阻塞很短时间
3)当fork操作结束时,出现background saving started信息之后,父进程不在阻塞,可以继续响应其他指令,持久化过程由子进程负责
4)子进程创建rdb文件,根据父进程内存页生成文件,对原本的dump.rdb进行覆盖操作
5)子进程发送结束信息给父进程表示完成,之后退出,父进程更新信息

如果硬盘写满了怎么办

临时:

127.0.0.1:6379> config set dir /root
#更改rdb文件的保存路径
127.0.0.1:6379> config set dbfilename hahaha.rdb
#更改rdb文件的名字

永久:

127.0.0.1:6379> config set dir /root
#更改rdb文件的保存路径
127.0.0.1:6379> config set dbfilename hahaha.rdb
#更改rdb文件的名字
127.0.0.1:6379> config rewrite   
重写配置文件

rdb的优点

(1)属于一个紧凑压缩的二进制文件,表示在某个时间节点的快照,适用于全量复制和备份场景
(2)rdb持久化得到的文件加载速度要远高于aof文件

rdb的缺点

(1)rdb快照不能达到秒级别备份(fork操作数据重量级操作)
(2)rdb文件以二进制形式存放,存在兼容问题

aof持久化日志

为了解决rdb不能秒级别备份问题,出现了aof持久化日志
aof持久化以独立日志的方式进行记录数据,redis恢复数据实际上是在每次运行redis时读取持久化文件达到恢复数据的目的,且aof文件作为目前的主流持久化方式应用广泛

开启aof

在配置文件中找到appendonly yes

appendonly no 改为 yes
appendfilename "appendonly.aof"            #日志文件名,出现在启动时用户所在目录

aof文件的工作流程

命令写入 文件同步 文件重写 重启加载

(1)所有的命令追加到aof_buf缓冲区
(2)aof缓冲区根据策略向硬盘进行同步
(3)随着aof文件越来越大,定期对aof文件进行重写
(4)重启服务器加载aof文件恢复数据

aof文件采用文本的方式
为什么用文本?
(1)文本具有很好的兼容
(2)文本协议具有很好的可读性,方便直接查看或者修改删除等操作

根据策略向硬盘同步
always m命令写入缓冲区后,直接同步到aof文件,效率低,由硬盘决定
everysec 命令写入缓冲区后,aof文件调用write操作,每秒执行一次,达到秒级别备份
no 命令缓冲区,redis不做向aof文件同步操作,同步操作有操作系统负责,调用时间不固定

appendfsync everysec

重写机制

随着日志越写越多,文件越来越大,redis引用了重写机制,解决问题,重写的文件为什么会变小
(1)删除aof文件中无用的命令,del,hdel,srem
(2)redis中已经过期的数据不会写入文件
(3)多条命令合并成一条,以64个元素作为分界线

触发重写

手动触发

bgrewriteaof

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started        #重写开启
查看日志
70842:M 18 Dec 2019 17:11:30.601 * Background append only file rewriting started by pid 71024
70842:M 18 Dec 2019 17:11:30.683 * AOF rewrite child asks to stop sending diffs.
71024:C 18 Dec 2019 17:11:30.684 * Parent agreed to stop sending diffs. Finalizing AOF...
71024:C 18 Dec 2019 17:11:30.684 * Concatenating 0.00 MB of AOF diff received from parent.
71024:C 18 Dec 2019 17:11:30.684 * SYNC append only file rewrite performed
71024:C 18 Dec 2019 17:11:30.685 * AOF rewrite: 2 MB of memory used by copy-on-write
70842:M 18 Dec 2019 17:11:30.719 * Background AOF rewrite terminated with success
70842:M 18 Dec 2019 17:11:30.720 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
70842:M 18 Dec 2019 17:11:30.720 * Background AOF rewrite finished successfully
工作流程

1、父进程检测是否有子进程进行操作,如果没有则创建子进程,如果有,不做任何操作
2、父进程通过fork操作创建子进程,所有开销等同bgsave
3、fork操作之后,父进程可以继续等待命令,所有修改根据aof的策略继续向硬盘同步,由于拥有写时复制技术,子进程共享父进程的物理内存,redis使用aof重写缓冲区保存新数据,防止aof文件生成时数据丢失
4、子进程生成新的aof文件,写入到硬盘中替换旧aof文件
5、aof完成之后,子进程告知父进程任务完成,父进程更新信息
6、父进程将aof重写缓冲区的文件写入到新aof文件中
7、再次替换老的aof文件,完成重写

写实复制(copy-on-write)COW,父进程和子进程共享页面,而不是子进程复制页面,只要页面共享,则不能修改,如果此时做出修改,会产生一个错误,内核将会把页面复制到新的页面中,并且进行标记为可写,原来的页面依然不可写,当其他程序视图修改时,内核检查执行写进程的页面是否是唯一属主,如果是,将标记该页面可写

自动触发

配置文件中

auto-aof-rewrite-percentage 100 #当前aof文件和上一次重写过后的aof文件的比值,上一次aof文件大小的100倍
auto-aof-rewrite-min-size 64mb #重写过后的aof文件最小体积

评论




正在载入...
PoweredHexo
HostedAliyun
DNSAliyun
ThemeVolantis
UV
PV
BY-NC-SA 4.0