一、Redis 的数据操作
1.1 Redis 的 key
redis 的 Key 是字符串类型,但是 Key 中不能包含边界字符,由于 key 不是 binary safe 的字符串,所以想“my key”和“mykey\n”这样的包含空格和换行的 key 是不允许的。
1.2 Redis 的 value
redis 提供五种数据类型:String类型、hash类型、list类型、set类型 及 sorted set类型。
1.2.1 String 类型
Redis 中没有使用 C 语言的字符串表示,而是自定义一个数据结构叫 SDS(simple dynamic string,简单动态字符)。Redis 的字符串是二进制安全的,存入什么数据取出的还是什么数据。String 类型的常用命令有:
-
赋值命令
-
redis 中赋值操作使用 set 命令,语法:
set key value127.0.0.1:6379> select 1 OK 127.0.0.1:6379[1]> keys * (empty list or set) 127.0.0.1:6379[1]> set name tom OK 127.0.0.1:6379[1]> keys * 1) "name" -
如果希望一次性插入多条数据,可以使用 mset 命令,语法:
mset key1 value1 key2 value2 ...127.0.0.1:6379[1]> mset age 18 score 100 sex male OK 127.0.0.1:6379[1]> keys * 1) "score" 2) "age" 3) "name" 4) "sex"
-
-
取值命令
-
redis 中进行取值操作使用 get 命令,语法:
get key。如果给定的键不存在则返回(nil),表示查询结果为空。127.0.0.1:6379[1]> get name "tom" 127.0.0.1:6379[1]> get names (nil) -
如果希望一次性从redis 中获取多个键对应的数据的值,可以使用mget 命令,语法:
mget key1 key2 ...127.0.0.1:6379[1]> mget name sex score 1) "tom" 2) "male" 3) "100" -
在获取对应键的数据时,想为该键指定新的值可以使用 getset 命令,语法:
getset key newValue127.0.0.1:6379[1]> getset score 99 "100" 127.0.0.1:6379[1]> get score "99"
-
-
删除命令
-
在redis 中删除数据使用 del 命令,语法:
del key127.0.0.1:6379[1]> del score (integer) 1 127.0.0.1:6379[1]> get score (nil) -
如果需要直接清空该库中所有数据,可以使用 flushdb 命令,语法:
flushdb127.0.0.1:6379[1]> flushdb OK 127.0.0.1:6379[1]> keys * (empty list or set) -
如果要清空所有库中的所有数据,可以使用 flushall 命令,语法:
flushall127.0.0.1:6379[1]> flushall OK 127.0.0.1:6379[1]> select 0 OK 127.0.0.1:6379> keys * (empty list or set)
-
-
数据递增
-
当存储的字符串是整数时,Redis 提供了一个实用的命令 incr ,其作用是让当前键值递增,并返回递增后的值,语法:
incr key。127.0.0.1:6379> set num 1 OK 127.0.0.1:6379> incr num (integer) 2 127.0.0.1:6379> get num "2" -
上述自增表示每次使用该指令,指定键值都会加1,如果需要按照指定的额数据来进行增长,可以使用 incrby 命令,语法:
incrby key increment。127.0.0.1:6379> incrby num 10 (integer) 12 127.0.0.1:6379> get num "12"
-
-
数值递减
-
当需要对数据进行递减操作时,使用 decr 命令,语法:
decr key127.0.0.1:6379> get num "12" 127.0.0.1:6379> decr num (integer) 11 -
如果希望递减指定的数值,可以使用 decrby 命令,语法:
decrby key decrement127.0.0.1:6379> decrby num 10 (integer) 1 127.0.0.1:6379> get num "1"
-
-
追加数据
-
在 Redis 中可以使用 append 命令向指定数据的末尾追加内容,语法:
append key value127.0.0.1:6379> set name silhouette OK 127.0.0.1:6379> append name " liu" (integer) 14 127.0.0.1:6379> get name "silhouette liu"注意:在插入数据时,字符串的双引号可以省略,但是如果字符串中包含空格的情况,需要加上双引号,否则空格会被忽略。
-
-
获取长度
-
使用 strlen 命令可以返回指定键对应的值的长度,如果键不存在则返回 0。语法:
strlen key127.0.0.1:6379> get name "silhouette liu" 127.0.0.1:6379> strlen key (integer) 0 127.0.0.1:6379> get name "silhouette liu" 127.0.0.1:6379> strlen name (integer) 14
-
1.2.2 Hash 类型
hash 叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其他类型。hash 类型的常用命令有:
-
赋值命令
-
向 hash 中插入数据时,可以使用 hset 命令。语法:
hset key field value127.0.0.1:6379[1]> hset user name silhouette (integer) 1 -
如果希望从 redis 中一次性设置多个字段的值,可以使用 hmset 命令。语法:
kmset key field1 value1 field2 value2 ...127.0.0.1:6379[1]> hmset user stuNo 1 age 18 score 99
-
-
取值命令
-
从 hash 中获取数据使用 hget 命令,语法:
hget key field127.0.0.1:6379[1]> hget user name "silhouette" -
使用hmget 可以一次性获取多个字段的值。语法:
hmget key field1 field2 field3 ....127.0.0.1:6379[1]> hmget user stuNo age score 1) "1" 2) "18" 3) "99" -
可以使用 hgetall 命令,或者指定键的所有字段和字段的值。语法:
hgetall key127.0.0.1:6379[1]> hgetall user 1) "name" 2) "silhouette" 3) "stuNo" 4) "1" 5) "age" 6) "18" 7) "score" 8) "99" -
使用 HKEYS 和 HVALS 命令可以分别用来获取指定键的所有键和所有值。语法:
hkeys key和hvals key127.0.0.1:6379[1]> hkeys user 1) "name" 2) "stuNo" 3) "age" 4) "score" 127.0.0.1:6379[1]> hvals user 1) "silhouette" 2) "1" 3) "18" 4) "99"
-
-
判断字段是否存在
-
判断字段是否存在使用 HEXISTS 命令,如果存在则返回 1,如果不存在则返回 0,语法:
hexists key field127.0.0.1:6379[1]> hexists user name (integer) 1 127.0.0.1:6379[1]> hexists user sex (integer) 0
-
-
获取字段数量
-
获取字段数量使用 HLEN 命令,如果键不存在则返回0,语法:
hlen key127.0.0.1:6379[1]> hlen user (integer) 4 127.0.0.1:6379[1]> hlen key (integer) 0
-
-
删除命令
-
要删除一个或多个字段,使用 hdel 命令,语法:
hdel key field [field ...]127.0.0.1:6379[1]> hgetall user 1) "name" 2) "silhouette" 3) "stuNo" 4) "1" 5) "age" 6) "18" 7) "score" 8) "99" 127.0.0.1:6379[1]> hdel user score age (integer) 2 127.0.0.1:6379[1]> hgetall user 1) "name" 2) "silhouette" 3) "stuNo" 4) "1"
-
-
增加数字
-
如果 hash 中的字段存储的是值是数值类型,可以进行自增操作。语法:
hincrby key field increment127.0.0.1:6379[1]> hget user stuNo "1" 127.0.0.1:6379[1]> hincrby user stuNo 1 (integer) 2 127.0.0.1:6379[1]> hget user stuNo "2"
-
1.2.3 list类型
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某个片段。列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为 O(1),获取越接近两端的元素速度就越快,这意味着即使是一个几千万个元素的列表,获取头部或尾部的10条记录也是极快的,list 类型的常用命令有;
-
添加元素
-
Redis 列表采用双向链表实现,因此在插入数据时可以从两端分别操作,想列表左边增加元素使用 lpush 命令,语法:
lpush key value。向列表右边添加元素使用 rpush 命令,语法:rpush key value。 -
lpush 命令是从左向右插入,从右向左弹出,满足先进先出的队列模式 -
rpush 命令是从右向左插入,从左向右弹出,满足先进先出的队列模式127.0.0.1:6379[1]> select 2 OK 127.0.0.1:6379[2]> lpush nums 1 (integer) 1 127.0.0.1:6379[2]> lpush nums 2 (integer) 2 127.0.0.1:6379[2]> lpush nums 3 (integer) 3 127.0.0.1:6379[2]> lpush nums 4 (integer) 4 127.0.0.1:6379[2]> lpush n ums 5 (integer) 5 127.0.0.1:6379[2]> rpush str a (integer) 1 127.0.0.1:6379[2]> rpush str b (integer) 2 127.0.0.1:6379[2]> rpush str c (integer) 3 127.0.0.1:6379[2]> rpush str d (integer) 4 127.0.0.1:6379[2]> rpush str e (integer) 5
-
-
查看列表
-
LRANGE 命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回 start、stop 之间的所有元素(包括两端的元素),索引从 0 开始。索引可以是负数,如:“-1”代表最右边的一个元素。
127.0.0.1:6379[2]> lrange nums 0 4 1) "5" 2) "4" 3) "3" 4) "2" 5) "1" 127.0.0.1:6379[2]> lrange str 0 4 1) "a" 2) "b" 3) "c" 4) "d" 5) "e"Tips:从以上获取的数据类型结构来看,我们发现从列表的左侧插入数据,在列表中数据以倒叙存放,从列表的右侧里插入数据则是正序存放
-
-
获取数据
-
在列表中可以使用 lpop 和 rpop 分别列表的左侧和右侧弹出一个数据,该数据会首先返回,然后再从原列表中删除。语法:
lpop key或rpop key127.0.0.1:6379[2]> lpop nums "5" 127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "3" 3) "2" 4) "1" 127.0.0.1:6379[2]> rpop nums "1" 127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "3" 3) "2"
-
-
获取列表中元素的个数
-
使用 LLEN 命令可以获取列表中元素的个数。语法:
LLEN key127.0.0.1:6379[2]> llen str (integer) 5
-
-
删除列表中指定的值
-
LREM 命令会删除列表中前 count 个值为 value 的元素,返回实际删除的元素个数。根据count 值的不同,该命令的执行方式会有所不同:语法:
LREM key count value- 当 count > 0 时,LREM 会从列表左边开始删除
- 当 count < 0 时,LREM 会从列表右边开始删除
- 当 count = 0 时,LREM 删除所有值为 value 的元素
127.0.0.1:6379[2]> lrange str 0 -1 1) "a" 2) "b" 3) "c" 4) "d" 5) "e" 127.0.0.1:6379[2]> lrem str 0 a (integer) 1 127.0.0.1:6379[2]> lrange str 0 -1 1) "b" 2) "c" 3) "d" 4) "e"
-
-
获取/设置指定索引的元素值
-
获取指定索引的元素值使用 lindex 命令,语法:
lindex key index127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "3" 3) "2" 127.0.0.1:6379[2]> lindex nums 2 "2" -
设置指定索引的元素值使用 lset 命令,语法:
lset key index value127.0.0.1:6379[2]> lset nums 2 x OK 127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "3" 3) "x"
-
-
只保留列表指定片段
-
List 中使用 ltrim 命令对列表中的数据进行截取操作,只保留指定数据片段。语法:
ltrim key start stop127.0.0.1:6379[2]> ltrim nums 1 2 OK 127.0.0.1:6379[2]> lrange nums 0 -1 1) "3" 2) "x"
-
-
向列表中插入元素
-
向列表中指定位置插入元素可以使用 linsert 命令,语法:
LINAERT key BEFORE|AFTER pivot value127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "x" 127.0.0.1:6379[2]> linsert nums after 4 3 (integer) 3 127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "3" 3) "x"
-
-
将元素从一个列表转移到另一个列表中
-
使用 RPOPLPUSH 命令可以将元素从一个列表转移到另一个列表中。语法:
RPOPLPUSH source destination127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "3" 3) "x" 127.0.0.1:6379[2]> lrange num1 0 -1 (empty list or set) 127.0.0.1:6379[2]> rpoplpush nums num1 "x" 127.0.0.1:6379[2]> lrange num1 0 -1 1) "x" 127.0.0.1:6379[2]> lrange nums 0 -1 1) "4" 2) "3"
-
1.2.4 set 类型
集合类型的常用操作是向集合中加入或删除元素,怕奴蛋某个元素是否存在等。由于集合类型的 redis 内部是使用值为空的散列表实现,所以这些操作的时间复杂度都为 O(1)。redis 还提供了多个集合之间的交集、并集、差集的运算。
-
增加/删除元素
-
集合中添加数据使用 sadd 命令,语法:
sadd key member [member ...]127.0.0.1:6379[2]> select 3 OK 127.0.0.1:6379[3]> sadd num1 10 20 (integer) 2 127.0.0.1:6379[3]> sadd num2 10 20 30 40 (integer) 4
-
-
获取集合中的所有元素
-
获取集合中的所有元素使用 SMEMBERS 命令,语法:
smembers key127.0.0.1:6379[3]> smembers num1 1) "10" 2) "20"
-
-
集合的差集运算
-
属于 A 并且不属于 B 的元素构成的集合,使用 SDIFF 命令,语法:
sdiff key [key ...]127.0.0.1:6379[3]> sdiff num2 num1 1) "30" 2) "40"
-
-
集合的交集运算
-
属于 A 并且也属于 B 的元素构成的集合,使用SINTER 命令,语法:
sinter key [key ...]127.0.0.1:6379[3]> sinter num1 num2 1) "10" 2) "20"
-
-
集合的并集运算
-
属于 A并且或者属于 B 的元素构成的集合,使用 SUNION 命令,语法:
sunoin key [key ...]127.0.0.1:6379[3]> sunion num1 num2 1) "10" 2) "20" 3) "30" 4) "40"
-
-
获得集合中元素的个数
-
获得集合中元素的个数,使用SCARD 命令,如果集合不存在则返回 0,语法:
scard key127.0.0.1:6379[3]> scard num2 (integer) 4
-
-
从集合中弹出一个元素
-
从集合中
随机的弹出一个元素使用 spop 命令,语法:spop key127.0.0.1:6379[3]> sadd num3 10 20 30 40 50 (integer) 5 127.0.0.1:6379[3]> smembers num3 1) "10" 2) "20" 3) "30" 4) "40" 5) "50" 127.0.0.1:6379[3]> spop num3 "20" 127.0.0.1:6379[3]> spop num3 "50"
-
1.2.5 sorted set 类型
在集合类型的基础上有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。
在某些方面有序集合和列表类型有些相似。
- 两者都是有序的
- 两者都可以获得某一范围的元素
但是,两者有着很大区别:
- 列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
- 有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快
- 列表中不能简单的调用某个元素的位置,但是有序集合可以(通过更改分数实现)
- 有序集合要比列表类型更耗内存
sorted set 类型的常用命令有:
-
增加元素
-
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含执勤啊已经存在的元素。语法:
zadd key score member [score member]127.0.0.1:6379[4]> select 4 OK 127.0.0.1:6379[4]> zadd score 80 tom 90 lucy 85 silhouette (integer) 3
-
-
获取排名在某个范围的元素列表
-
获得排名子某个范围的元素列表使用 zrange 命令,语法:
zrange key start stop [withscores]127.0.0.1:6379[4]> zrange score 0 -1 1) "tom" 2) "silhouette" 3) "lucy" 127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "tom" 2) "80" 3) "silhouette" 4) "85" 5) "lucy" 6) "90"
-
-
删除元素
-
移除有序集 key 中的一个或多个成员使用 zrem 命令,不存在的成员将被忽略。语法:
zrem key member[member ...]127.0.0.1:6379[4]> zrange score 0 -1 1) "tom" 2) "silhouette" 3) "lucy" 127.0.0.1:6379[4]> zrem score tom (integer) 1 127.0.0.1:6379[4]> zrange score 0 -1 1) "silhouette" 2) "lucy"
-
-
获得指定分数范围的元素
-
获得指定分数范围的元素使用 zrangebyscore 命令,语法:
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]127.0.0.1:6379[4]> zrangebyscore score 90 100 withscores 1) "lucy" 2) "90" 127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "silhouette" 2) "85" 3) "lucy" 4) "90" 127.0.0.1:6379[4]> zrangebyscore score 90 100 1) "lucy" 127.0.0.1:6379[4]> zrangebyscore score 90 100 withscores 1) "lucy" 2) "90"
-
-
增加某个元素的分数,返回值是更改后的分数
-
增加某个元素的分数,返回值是更改后的分数,使用 zincrby 命令,语法:
ZINCRBY key increment member127.0.0.1:6379[4]> zincrby score 4 silhouette "89" 127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "silhouette" 2) "89" 3) "lucy" 4) "90"
-
-
获取集合中元素的数量
-
获取集合中元素的数量使用 ZCARD 命令,语法:
ZCARD key127.0.0.1:6379[4]> zcard score (integer) 2
-
-
获得指定分数范围内的元素个数
-
获取指定分数范围内的元素个数使用 ZOUNT 命令,语法:
ZCOUNT key min max127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "silhouette" 2) "89" 3) "lucy" 4) "90" 127.0.0.1:6379[4]> zcount score 90 100 (integer) 1
-
-
按照排名范围删除元素
-
按照排名范围删除元素使用 ZREMRANGEBYRANK 命令,语法:
ZREMRANGEBYRANK key start stop127.0.0.1:6379[4]> zadd score 80 tom 90 lucy 85 silhouette (integer) 3 127.0.0.1:6379[4]> zremrangebyrank score 0 1 (integer) 2 127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "lucy" 2) "90"
-
-
按照分数范围删除元素
-
按照分数范围删除元素使用 ZREMRANGEBYSCORE ,语法:
ZREMRANGEBYSCORE key start stop127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "lili" 2) "75" 3) "tom" 4) "80" 5) "silhouette" 6) "85" 7) "lucy" 8) "90" 127.0.0.1:6379[4]> zremrangebyscore score 0 79 (integer) 1 127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "tom" 2) "80" 3) "silhouette" 4) "85" 5) "lucy" 6) "90"
-
-
获取元素的排名
-
获取元素的排名使用 ZRANK 命令,语法:
ZRANK key member127.0.0.1:6379[4]> zrange score 0 -1 withscores 1) "tom" 2) "80" 3) "silhouette" 4) "85" 5) "lucy" 6) "90" 127.0.0.1:6379[4]> zrank score silhouette (integer) 1 127.0.0.1:6379[4]> zrank score lucy (integer) 2 127.0.0.1:6379[4]> zrank score tom (integer) 0 -
获取元素的倒序排名使用 ZREVRANK 命令,语法:
ZREVRANK key member127.0.0.1:6379[4]> zrank score lucy (integer) 2 127.0.0.1:6379[4]> zrevrank score lucy (integer) 0
-
1.3 redis 的其他命令
1.3.1 设置 key 的生存时间
redis 在实际使用过程中更多的用做缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁。
EXPIRE key seconds 设置key的生存时间(单位:秒)key在多少秒后会自动删除
TTL key 查看key生于的生存时间
PERSIST key 清除生存时间
PEXPIRE key milliseconds 生存时间设置单位为:毫秒
1.3.2 返回满足给定pattern 的所有 key
-
redis 中可以使用 pattern 来获取键的名称
127.0.0.1:6379[4]> keys * 1) "score" 2) "scores" 127.0.0.1:6379[4]> keys score* 1) "score" 2) "scores"
1.3.3 重命令 key
-
重命名 key 使用 rename 命令,语法:
rename name newname127.0.0.1:6379[4]> keys * 1) "score" 2) "scores" 127.0.0.1:6379[4]> rename scores demo OK 127.0.0.1:6379[4]> keys * 1) "score" 2) "demo"
1.3.4 返回值类型
-
使用 type 命令可以返回指定键的值的类型,语法:
type key127.0.0.1:6379[4]> type score zset 127.0.0.1:6379[4]> set name lili OK 127.0.0.1:6379[4]> type name string
1.3.5 获取服务器的信息和统计
-
获取服务器的信息和统计使用 info 命令
127.0.0.1:6379[4]> info # Server redis_version:3.0.0 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:623444a9491ce503 redis_mode:standalone os:Linux 3.10.0-1062.el7.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll gcc_version:4.8.5 process_id:1576 run_id:2887fee0240c056f048965cf62410d4ba5df2dca tcp_port:6379 uptime_in_seconds:12794 uptime_in_days:0 hz:10 lru_clock:15313912 config_file:/usr/local/redis/etc/redis.conf # Clients connected_clients:1 client_longest_output_list:0 client_biggest_input_buf:0 blocked_clients:0 # Memory used_memory:816152 used_memory_human:797.02K used_memory_rss:7442432 used_memory_peak:816616 used_memory_peak_human:797.48K used_memory_lua:35840 mem_fragmentation_ratio:9.12 mem_allocator:jemalloc-3.6.0 # Persistence loading:0 rdb_changes_since_last_save:3 rdb_bgsave_in_progress:0 rdb_last_save_time:1592371760 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:0 rdb_current_bgsave_time_sec:-1 aof_enabled:0 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:-1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok # Stats total_connections_received:1 total_commands_processed:190 instantaneous_ops_per_sec:0 total_net_input_bytes:7121 total_net_output_bytes:5012 instantaneous_input_kbps:0.00 instantaneous_output_kbps:0.00 rejected_connections:0 sync_full:0 sync_partial_ok:0 sync_partial_err:0 expired_keys:0 evicted_keys:0 keyspace_hits:106 keyspace_misses:9 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:805 migrate_cached_sockets:0 # Replication role:master connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 # CPU used_cpu_sys:8.06 used_cpu_user:3.69 used_cpu_sys_children:0.01 used_cpu_user_children:0.01 # Cluster cluster_enabled:0 # Keyspace db0:keys=2,expires=0,avg_ttl=0 db1:keys=1,expires=0,avg_ttl=0 db2:keys=3,expires=0,avg_ttl=0 db3:keys=3,expires=0,avg_ttl=0 db4:keys=3,expires=0,avg_ttl=0 -
如果只需要获取某一个方面的信息,可以使用 info 加上需要的信息名称即可。示例:info CPU
127.0.0.1:6379[4]> info Keyspace # Keyspace db0:keys=2,expires=0,avg_ttl=0 db1:keys=1,expires=0,avg_ttl=0 db2:keys=3,expires=0,avg_ttl=0 db3:keys=3,expires=0,avg_ttl=0 db4:keys=3,expires=0,avg_ttl=0
1.3.6 推出连接
- 使用
quit命令表示推出
二、Redis的持久化
2.1 redis 的持久化
redis 的高性能是由于其将所有数据都存储在了内存中,为了使redis 在重启之后仍能保持数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
redis 支持两种方式的持久化,一种是RDB 方式,一种是AOF方式,可以单独使用其中一种或将两种结合使用。
2.2 RDB 持久化
RDB 方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时 redis 会自动将内存中的数据进行快照持久化到硬盘。
RDB 是Redis 默认采用的持久化方式,在 redis.conf 配置文件中默认有此下配置:
# save开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是"或"的关系
# 表示 15分钟(900秒)内至少1 个键被更改则进行快照
save 900 1
# 表示 5分钟(300秒)内至少10个键被修改则进行快照
save 300 10
#
save 60 10000
Redis 启动后会读取 RDB 快照文件,将数据从硬盘载入到内存中。根据数据量大小与结构和服务性能不同,这个时间也不同。通常将记录一千万个字符串型键、大小为 1GB 的快照文件载入到内存中需要花费 20~ 30秒。
如果开启了 RDB ,Redis 默认会在启动当前服务的目录自动创建数据文件。通过 RDB 方式实现持久化,一旦 redis 异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场景,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用 AOF 方式进行持久化。
注意:由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外当机的话,就会 丢失最后一次快照后的所有数据修改。
2.3 AOF 持久化
默认情况下 redis 没有开启 AOF( append only file) 方式的持久化,可以通过 appendonly 参数开启:appendonly yes
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令, Redis 就会将该命令写入硬盘中的 AOF 文件。AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是appendonly.aof,可以通过 appendfilename 参数修改:appendfilename appendonly.aof
这种日志追加方式 redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认appendonly.aof)。当redis 重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于操作系统会在内核中缓存 write 做过的修改,所以可能不是立即写到磁盘上。这样的持久护额还是有可能会丢失部分修改。不过我们可以通过配置文件告诉 redis 我们想要通过fsync函数强制操作系统写入到磁盘的时机。有三种方式如下:(默认是:每秒 fsync 一次)
# 启用日志追加持久化方式
appendonly yes
# 每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全 的持久化,不推荐使用
# appendfsync always
# 每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折 中,推荐
appendfsync everysec
# 完全依赖操作系统,性能最好,持久化没保证
#appendfsync no
使用 AOF 方式同时带来另一个问题:持久化文件会变得越来越大。例如我们调用 incr test 命令 100次,文件中必须全部保存 100 条命令,其实有 99条 都是多余的。因为要恢复数据库状态其实文件中保存一条 set test 100 就够了。为了压缩这种持久化方式的日志文件,redis 提供了bgrewriteaof命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的持久化日志文件。