一、持久化
1.redis持久化RDB模式
可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
1)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]
... ...
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]
dir /server/redis/6379
appendonly yes/no
appendfsync always
appendfsync everysec
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]
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
6)AOF持久化高级配置
[root@db01 redis]
no-appendfsync-on-rewrite yes/no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
二、redis数据类型
String: 字符串类型
Hash: 哈希类型
List: 列表类型
Set: 集合类型
Sorted set: 顺序集合类型
1. String: 字符串类型
1)添加数据
127.0.0.1:6379> set k4 v4
OK
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"
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)查询数据
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)删除数据
127.0.0.1:6379> del name1
(integer) 1
127.0.0.1:6379> del name2 name3
(integer) 2
4)修改数据
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"
127.0.0.1:6379> SET name2 dsbqiudao
(integer) 9
127.0.0.1:6379> get name2
"dsbqiudao"
2. Hash: 哈希类型
1)添加数据
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"
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
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)查看数据
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"
127.0.0.1:6379> RPOPLPUSH list list
"banzhang"
127.0.0.1:6379> LRANGE list 0 -1
1) "banzhang"
2) "qiudao"
4)删除数据
127.0.0.1:6379> del list
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"
127.0.0.1:6379> sdiffstore diffkey set1 set2 set3
(integer) 1
127.0.0.1:6379> SMEMBERS diffkey
1) "7"
3)修改数据
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>