一、持久化

1.redis持久化RDB模式

可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。

1)RDB模式工作方式

#我们配置多长时间写入多少数据之后,redis将数据保存到磁盘的dump.rdb文件中,也可以手动保存

1)默认情况下,Redis保存数据集快照到磁盘,名为dump.rdb的二进制文件。你可以设置让Redis在N秒内至少有M次数据集改动时保存数据集,或者你也可以手动调用SAVE或者BGSAVE命令。

2)在上文中我们已经在配置文件中做过对应的配置:
例如,这个配置会让Redis在每个60秒内至少有1000次键改动时自动转储数据集到磁盘:
save 60 1000

3)当 Redis 需要保存 dump.rdb 文件时,服务器执行以下操作:
Redis 调用 fork() ,同时拥有父进程和子进程。
子进程将数据集写入到一个临时的 RDB 文件中。当子进程完成对新 RDB 文件的写入时, Redis 用新RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

4)这种方式使得 Redis 可以从写时复制机制中获益。

2)配置RDB持久化

[root@db01 6379]# vim /server/redis/6379/redis.conf 
... ...
dir /server/redis/6379
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000

3)RDB持久化优点

1.RDB是一种表示某个即时点的Redis数据的紧凑文件。RDB文件适合用于备份。例如,你可能想要每小时归档最近24小时的RDB文件,每天保存近30天的RDB快照。这允许你很容易的恢复不同版本的数据集以容灾。
2.RDB非常适合于灾难恢复,作为一个紧凑的单一文件,可以被传输到远程的数据中心。
3.RDB最大化了Redis的性能,因为Redis父进程持久化时唯一需要做的是启动(fork)一个子进程,由子进程完成所有剩余工作。父进程实例不需要执行像磁盘IO这样的操作。
4.RDB在重启保存了大数据集的实例时比AOF要快。

4)RDB持久化缺点

1.当你需要在Redis停止工作(例如停电)时最小化数据丢失,RDB可能不太好。你可以配置不同的保存点(save point)来保存RDB文件(例如,至少5分钟和对数据集100次写之后,但是你可以有多个保存点)。然而,你通常每隔5分钟或更久创建一个RDB快照,所以一旦Redis因为任何原因没有正确关闭而停止工作,你就得做好最近几分钟数据丢失的准备了。
2.RDB需要经常调用fork()子进程来持久化到磁盘。如果数据集很大的话,fork()比较耗时,结果就是,当数据集非常大并且CPU性能不够强大的话,Redis会停止服务客户端几毫秒甚至一秒。AOF也需要fork(),但是你可以调整多久频率重写日志而不会有损(trade-off)持久性(durability)。

5)优缺点总结

优点:速度快,适合于用作备份,主从复制也是基于RDB持久化功能实现的。
缺点:会有数据丢失、导致服务停止几秒

2.redis持久化AOF模式

AOF(append only file)只追加文件,记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。

1)配置AOF持久化

#修改配置文件
[root@db01 redis]# vim /server/redis/6379/redis.conf
#是否打开AOF日志功能
dir /server/redis/6379
appendonly yes/no

#每一条命令都立即同步到AOF
appendfsync always
#每秒写一次
appendfsync everysec
#写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到AOF
appendfsync no

2)AOF持久化优点

1.使用AOF Redis会更具有可持久性(durable):你可以有很多不同的fsync策略:没有fsync,每秒fsync,每次请求时fsync。使用默认的每秒fsync策略,写性能也仍然很不错(fsync是由后台线程完成的,主线程继续努力地执行写请求),即便你也就仅仅只损失一秒钟的写数据。
2.AOF日志是一个追加文件,所以不需要定位,在断电时也没有损坏问题。即使由于某种原因文件末尾是一个写到一半的命令(磁盘满或者其他原因),redis-check-aof工具也可以很轻易的修复。
3.当AOF文件变得很大时,Redis会自动在后台进行重写。重写是绝对安全的,因为Redis继续往旧的文件中追加,使用创建当前数据集所需的最小操作集合来创建一个全新的文件,一旦第二个文件创建完毕,Redis就会切换这两个文件,并开始往新文件追加。
4.AOF文件里面包含一个接一个的操作,以易于理解和解析的格式存储。你也可以轻易的导出一个AOF文件。例如,即使你不小心错误地使用FLUSHALL命令清空一切,如果此时并没有执行重写,你仍然可以保存你的数据集,你只要停止服务器,删除最后一条命令,然后重启Redis就可以。

3)AOF持久化缺点

1.对同样的数据集,AOF文件通常要大于等价的RDB文件。
2.AOF可能比RDB慢,这取决于准确的fsync策略。通常fsync设置为每秒一次的话性能仍然很高,如果关闭fsync,即使在很高的负载下也和RDB一样的快。不过,即使在很大的写负载情况下,RDB还是能提供能好的最大延迟保证。
3.在过去,我们经历了一些针对特殊命令(例如,像BRPOPLPUSH这样的阻塞命令)的罕见bug,导致在数据加载时无法恢复到保存时的样子。这些bug很罕见,我们也在测试套件中进行了测试,自动随机创造复杂的数据集,然后加载它们以检查一切是否正常,但是,这类bug几乎不可能出现在RDB持久化中。为了说得更清楚一点:Redis AOF是通过递增地更新一个已经存在的状态,像MySQL或者MongoDB一样,而RDB快照是一次又一次地从头开始创造一切,概念上更健壮。但是,1)要注意Redis每次重写AOF时都是以当前数据集中的真实数据从头开始,相对于一直追加的AOF文件(或者一次重写读取老的AOF文件而不是读内存中的数据)对bug的免疫力更强。2)我们还没有收到一份用户在真实世界中检测到崩溃的报告。

4)AOF持久化优缺点总结

优点:可以最大程度保证数据不丢失
缺点:日志记录量级比较大

3.RDB 和 AOF

1)应该用哪一个

1.一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
2.如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
3.有很多用户单独使用AOF,但是我们并不鼓励这样,因为时常进行RDB快照非常方便于数据库备份,启动速度也较之快,还避免了AOF引擎的bug。
4.个人感触:在企业中,通常都使用RDB来做持久化,因为一般redis是在做MySQL的缓存,就算缓存数据丢失,真实的数据还是在MySQL中,之所以用缓存是为了速度,性能而考虑,所以还是建议使用RDB持久化,相对来说会好一些,除非专门用redis来做一个key:value的数据库,而且数据很重要,那么可以考虑使用AOF

注意:基于这些原因,将来我们可能会统一AOF和RDB为一种单一的持久化模型(长远计划)。

2)AOF重写

1.因为 AOF 的运作方式是不断地将命令追加到文件的末尾,所以随着写入命令的不断增加, AOF 文件的体积也变得越来越大。举个例子,如果你对一个计数器调用了 100 次 INCR ,那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录。然而在实际上,只使用一条 SET 命令已经足以保存计数器的当前值了,其余 99 条记录实际上都是多余的。

2.为了处理这种情况, Redis 支持一种有趣的特性:可以在不断服务客户端的情况下,对 AOF 文件进行重建。执行 BGREWRITEAOF 命令, Redis 将生产一个新的 AOF 文件,这个文件包含重建当前数据集所需的最少命令。

4)备份 Redis 数据

1.Redis 对于数据备份是非常友好的,因为你可以在服务器运行的时候对 RDB 文件进行复制
2.当服务器要创建一个新的 RDB 文件时,它先将文件的内容保存在一个临时文件里面,当临时文件写入完毕时,程序才使用临时文件替换原来的 RDB 文件。
3.这也就是说,无论何时, 复制 RDB 文件都是绝对安全的。

#以下是我们的建议:
1.创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个 RDB 文件备份到另一个文件夹。
2.确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照: 比如说, 你可以保留最近 48 小时内的每小时快照, 还可以保留最近一两个月的每日快照。
3.至少每天一次, 将 RDB 备份到你的数据中心之外, 或者至少是备份到你运行 Redis 服务器的物理机器之外。

5)RDB持久化高级配置

#编辑配置文件
[root@db01 redis]# vim /etc/redis/6379/redis.conf
#后台备份进程出错时,主进程停不停止写入? 主进程不停止容易造成数据不一致
stop-writes-on-bgsave-error yes
#导出的rdb文件是否压缩 如果rdb的大小很大的话建议这么做
rdbcompression yes
#导入rbd恢复时数据时,要不要检验rdb的完整性 验证版本是不是一致
rdbchecksum yes

6)AOF持久化高级配置

#编辑配置文件
[root@db01 redis]# vim /etc/redis/6379/redis.conf
#正在导出rdb快照的过程中,要不要停止同步aof
no-appendfsync-on-rewrite yes/no
#aof文件大小比起上次重写时的大小,增长率100%时重写,缺点:业务开始的时候,会重复重写多次
auto-aof-rewrite-percentage 100
#aof文件,至少超过64M时,重写
auto-aof-rewrite-min-size 64mb

二、redis数据类型

String: 字符串类型
Hash: 哈希类型
List: 列表类型
Set: 集合类型
Sorted set: 顺序集合类型

1. String: 字符串类型

1)添加数据

#添加key
127.0.0.1:6379> set k4 v4
OK

#添加多个key
127.0.0.1:6379> mset k5 v5 k6 v6 k7 v7
OK
127.0.0.1:6379> mset k8 "v8 vvvv" k9 "k9 vvvv"
OK

#先查看在修改
127.0.0.1:6379> GETSET k1 vvv
"v1"
127.0.0.1:6379> GETSET k1 lll
"vvv"

#添加key时设置生存时间
127.0.0.1:6379> set k10 v10 ex 10
OK

#计数器增加值
127.0.0.1:6379> get good
"7"
#计数器增加指定值
127.0.0.1:6379> INCRBY good 1000
(integer) 1007
#计数器减少值
127.0.0.1:6379> DECR good
(integer) 1006
#计数器减少指定值
127.0.0.1:6379> DECRBY good 1000
(integer) 6
#计数器增加指定小数
127.0.0.1:6379> INCRBYFLOAT good 0.1
"6.1"

2)查询数据

#查看key
127.0.0.1:6379> get name2
"qiudao"

#查看长度
127.0.0.1:6379> STRLEN name2
(integer) 6

#查看指定长度的字符
127.0.0.1:6379> GETRANGE name2 0 3
"qiud"

#查看多个值
127.0.0.1:6379> MGET name1 name2 name3
1) "lhd"
2) "qiudao"
3) "dsb"

3)删除数据

#删除key
127.0.0.1:6379> del name1
(integer) 1
#删除多个key
127.0.0.1:6379> del name2 name3
(integer) 2

4)修改数据

#在key后面追加内容
127.0.0.1:6379> APPEND name2 dsb
(integer) 9
127.0.0.1:6379> get name2
"qiudaodsb"

#修改指定字符
127.0.0.1:6379> SETRANGE name2 6 x
(integer) 9
127.0.0.1:6379> get name2
"qiudaoxsb"

#修改整个key值
127.0.0.1:6379> SET name2 dsbqiudao
(integer) 9
127.0.0.1:6379> get name2
"dsbqiudao"

2. Hash: 哈希类型

1)添加数据

#添加teacher的值
127.0.0.1:6379> hset teacher name qiudao
(integer) 1
127.0.0.1:6379> hset teacher age 78
(integer) 1
127.0.0.1:6379> hset teacher sex nv
(integer) 1

#一次添加多个值
127.0.0.1:6379> hmset teacher2 name lhd age 18 height 182
OK

2)查看数据

#查看指定列数据
127.0.0.1:6379> hget teacher name
"qiudao"
127.0.0.1:6379> hget teacher age
"78"
127.0.0.1:6379> hget teacher sex
"nv"

#查看key的所有数据
127.0.0.1:6379> hgetall teacher
1) "name"
2) "qiudao"
3) "age"
4) "78"
5) "sex"
6) "nv"

3)修改数据

#修改数据
127.0.0.1:6379> HSET teacher sex nan
(integer) 0
127.0.0.1:6379> hgetall teacher
1) "name"
2) "qiudao"
3) "age"
4) "78"
5) "sex"
6) "nan"

#修改值增加数值
127.0.0.1:6379> HGETALL teacher
1) "id"
2) "1"
127.0.0.1:6379> HINCRBY teacher id 1
(integer) 2
127.0.0.1:6379> HGETALL teacher
1) "id"
2) "2"

4)删除数据

#删除某列的值
127.0.0.1:6379> HDEL teacher sex
(integer) 1

#删除整个KEY
127.0.0.1:6379> del teacher
(integer) 1

3.List: 列表类型

1)增加数据

#增加一个或多个值(向左边添加)
127.0.0.1:6379> LPUSH list qiudao
(integer) 1
127.0.0.1:6379> LPUSH list lhd
(integer) 2
127.0.0.1:6379> LPUSH list dsb zengdao

#增加一个或多个值(向右边添加)
127.0.0.1:6379> RPUSH list banzhang xuewei zuzhang

#插入数据到指定地方
127.0.0.1:6379> LINSERT list after qiudao shengwei
(integer) 8

2)查看数据

#查看key的行数
127.0.0.1:6379> LLEN list
(integer) 8

#查看列表所有数据
127.0.0.1:6379> LRANGE list 0 -1
1) "zengdao"
2) "dsb"
3) "lhd"
4) "qiudao"
5) "shengwei"
6) "banzhang"
7) "xuewei"
8) "zuzhang"

#获取指定排列的数据
127.0.0.1:6379> LINDEX list 0
"zengdao"
127.0.0.1:6379> LINDEX list -1
"zuzhang"
127.0.0.1:6379> LINDEX list 3
"qiudao"

#读取列表首部数据,读取一条少一条
127.0.0.1:6379> LRANGE list 0 -1
1) "zengdao"
2) "dsb"
3) "lhd"
4) "qiudao"
5) "shengwei"
6) "banzhang"
7) "xuewei"
8) "zuzhang"
127.0.0.1:6379> LPOP list
"zengdao"
127.0.0.1:6379> LRANGE list 0 -1
1) "dsb"
2) "lhd"
3) "qiudao"
4) "shengwei"
5) "banzhang"
6) "xuewei"
7) "zuzhang"
127.0.0.1:6379> LPOP list
"dsb"
127.0.0.1:6379> LPOP list
"lhd"
127.0.0.1:6379> LRANGE list 0 -1
1) "qiudao"
2) "shengwei"
3) "banzhang"
4) "xuewei"
5) "zuzhang"

#读取列表尾部数据,读取一条少一条
127.0.0.1:6379> RPOP list
"zuzhang"
127.0.0.1:6379> RPOP list
"xuewei"
127.0.0.1:6379> RPOP list
"banzhang"
127.0.0.1:6379> LRANGE list 0 -1
1) "qiudao"
2) "shengwei"

3)修改数据

#修改指定索引位置的数据
127.0.0.1:6379> LSET list 1 banzhang
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "qiudao"
2) "banzhang"

#将一个key尾部数据取出放到另一个key的首部
127.0.0.1:6379> RPOPLPUSH list list
"banzhang"
127.0.0.1:6379> LRANGE list 0 -1
1) "banzhang"
2) "qiudao"

4)删除数据

#删除key
127.0.0.1:6379> del list

#从首部开始,删除两个qiudao,如果数量不够则只删除有的数据
127.0.0.1:6379> LRANGE list 0 -1
1) "banzhang"
2) "qiudao"
3) "banzhang"
4) "xuewei"
127.0.0.1:6379> LREM list 2 qiudao
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "banzhang"
2) "banzhang"
3) "xuewei"

4. Set: 集合类型

在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

1)增加数据

#设置两个集合(集合没办法添加同样的值)
127.0.0.1:6379> sadd set1 1 2 3 5 7
(integer) 5

127.0.0.1:6379> sadd set2 1 3 5 8 9
(integer) 5

2)查询数据

#查询一个集合所有值
127.0.0.1:6379> SMEMBERS set1
1) "1"
2) "2"
3) "3"
4) "5"
5) "7"

#判断集合中是否有某个值
127.0.0.1:6379> SISMEMBER set1 9
(integer) 0

#获取集合中元素的数量
127.0.0.1:6379> SCARD set1
(integer) 5

#随机获取集合中的数
127.0.0.1:6379> SRANDMEMBER set1
"5"

#取交集,取出多个集合中共有的值
127.0.0.1:6379> SINTER set1 set2
1) "1"
2) "3"
3) "5"

#取并集,将多个集合所有值都取出来,并且去重排序
127.0.0.1:6379> SUNION set1 set2 set3
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"

#对比取出前者有后者没有的值
127.0.0.1:6379> SDIFF set1 set2
1) "2"
2) "7"
#将前面两个的结果在于第三个对比取出值
127.0.0.1:6379> SDIFF set1 set2 set3
1) "7"

#3个集和比较,获取独有的元素,并存入diffkey 关联的Set中
127.0.0.1:6379> sdiffstore diffkey set1 set2 set3
(integer) 1
127.0.0.1:6379> SMEMBERS diffkey
1) "7"

3)修改数据

#移动数据,将前面key里的值移动到后面的key中
127.0.0.1:6379> SMOVE set1 set2 7
(integer) 1
127.0.0.1:6379> SMEMBERS set1
1) "1"
2) "2"
3) "3"
4) "5"
127.0.0.1:6379> SMEMBERS set2
1) "1"
2) "3"
3) "5"
4) "7"
5) "8"
6) "9"
127.0.0.1:6379>

4)删除数据

#读取数据即移除
127.0.0.1:6379> SPOP set1
"5"
127.0.0.1:6379> SPOP set1
"1"
127.0.0.1:6379> SPOP set1
"3"
127.0.0.1:6379> SPOP set1
"2"
127.0.0.1:6379> SPOP set1
(nil)
127.0.0.1:6379> SMEMBERS set1
(empty list or set)

#移除指定的值
127.0.0.1:6379> SREM set2 7
(integer) 1
127.0.0.1:6379> SREM set2 8
(integer) 1
127.0.0.1:6379> SREM set2 1 2 3 4 5
(integer) 3
127.0.0.1:6379> SMEMBERS set2
1) "9"

5. Sorted-Set(有序集合)类型操作

排行榜应用,取TOP N操作
这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,这时候就需要我们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。

1)添加数据

#添加一个集合
127.0.0.1:6379> zadd linux7 100 banzhang 99 xuewei 60 zhangyin 20 gcc
(integer) 4

2)查看数据

#获取指定人的分数
127.0.0.1:6379> ZSCORE linux7 gcc
"20"

#获取集合中值的数量
127.0.0.1:6379> ZCARD linux7
(integer) 4

#查看集合成员
127.0.0.1:6379> ZRANGE linux7 0 -1
1) "gcc"
2) "zhangyin"
3) "xuewei"
4) "banzhang"

#查看集合成员及成绩(默认升序)
127.0.0.1:6379> ZRANGE linux7 0 -1 WITHSCORES
1) "gcc"
2) "20"
3) "zhangyin"
4) "60"
5) "xuewei"
6) "99"
7) "banzhang"
8) "100"

#查看集合成员及成绩(使用降序)
127.0.0.1:6379> ZREVRANGE linux7 0 -1 WITHSCORES
 1) "banzhang"
 2) "100"
 3) "xuewei"
 4) "99"
 5) "dongge"
 6) "77"
 7) "zhangyin"
 8) "60"
 9) "gcc"
10) "20"

#按指定范围查询,显示人数
127.0.0.1:6379> ZCOUNT linux7 70 100
(integer) 3

#按指定范围查询,显示具体人名
127.0.0.1:6379> ZRANGEBYSCORE linux7 70 100
1) "dongge"
2) "xuewei"
3) "banzhang"
127.0.0.1:6379>

3)修改数据

#给指定人员增加分数
127.0.0.1:6379> ZINCRBY linux7 100 gcc
"120"
127.0.0.1:6379> ZREVRANGE linux7 0 -1 WITHSCORES
 1) "gcc"
 2) "120"
 3) "banzhang"
 4) "100"
 5) "xuewei"
 6) "99"
 7) "dongge"
 8) "77"
 9) "zhangyin"
10) "60"

4)删除数据

#删除集合中指定值
127.0.0.1:6379> ZREM linux7 gcc
(integer) 1
127.0.0.1:6379> ZREVRANGE linux7 0 -1 WITHSCORES
1) "banzhang"
2) "100"
3) "xuewei"
4) "99"
5) "dongge"
6) "77"
7) "zhangyin"
8) "60"
127.0.0.1:6379>
Copyright © 高程程 all right reserved,powered by Gitbook修订于: 2021-05-18 21:15:03

results matching ""

    No results matching ""