Redis
1.简介
Redis(Remote Dictionary Server):远程字典服务,是一个用C语言开发的高性能key-value类型的非关系型数据库。
特性:
- 工作机制为单线程
- 高性能:每秒10万次的请求
- 支持多数据类型
- string(字符串类型)
- list(列表)
- hash(哈希)
- set(集合)
- sorted_set(有序集合)
- 支持数据持久化
2.应用
- 将热点数据缓存起来,加速查询
- 排行榜
- 验证码
- 分布式session
3.基本操作
3.1 help命令
# 获取命令的使用方式
help 命令名
127.0.0.1:6379> HELP GET
GET key
summary: Get the value of a key
since: 1.0.0
group: string
# help @组名,获取该组的命令的使用方式
help @string # 会返回有关string的所有的命令
127.0.0.1:6379> help @string
APPEND key value
summary: Append a value to a key
since: 2.0.0
help @hash
4.数据类型
4.1 string
存储内容:通常存储字符串,如果字符串以整数的形式存储,可以作为数字进行操作
使用场景:
基本命令:
- 增删改查以及存储内容的长度
# 添加/修改数据
set key value
# 如果key不存在,才创建key
setnx key value
# 获取数据
get key
# 删除数据
del key
# 添加/修改多个数据,是一个原子性操作,要么一起成功,要么一起失败
mset key1 value1 key2 value2 ..
# 如果可以不存在,添加/修改多个数据,是一个原子性操作,要么一起成功,要么一起失败
msetnx k3 v3 k4 v4
# 获取多个数据
mget key1 key2 ..
# 获取key的值,并给key重新赋值,如果key不存在,则返回null,并创建key
getset key value
# 获取存储的字符串的长度
strlen key
# 在字符串尾部追加字符串(如果原始key不存在,则新建一个key)
append key value
- 数值类型的字符串的自增或自减操作
# 值自增1
127.0.0.1:6379> incr num
(integer) 2
# 值增加5
127.0.0.1:6379> incrby num 5
(integer) 8
# 值自减1
127.0.0.1:6379> decr num
(integer) 7
# 值减少5
127.0.0.1:6379> DECRBY num 5
- 数据的时间有效性
# 设置key的过期时间,单位为秒
SETEX key seconds value
# 设置key的过期时间,单位是毫秒
PSETEX key milliseconds value
- 其它操作
# 获取某个范围的字符串
GETRANGE key start end
# 这里是指所有的字符串
127.0.0.1:6379> GETRANGE hello 0 -1
"helloword"
# 获取某个范围的字符串,这里是指[0,2],闭区间
127.0.0.1:6379> GETRANGE hello 0 2
"hel"
# 将指定位置开始的字符串替换
SETRANGE key offset value
127.0.0.1:6379> SETRANGE hello 2 xx
(integer) 9
- 特殊写法
# 复杂写法,使用冒号
MSET user:1:name stone user:2:age 20
4.2 hash
存储内容:存储对象信息
存储结构:一个存储空间保存多个键值对数据
使用场景:购物车、存储对象数据
基本命令:
- 增删改查以及获取hash中field的长度
# 添加/修改数据以及获取字段的数量
hset key field value [field value ...]
# 获取数据
hget key field
# 获取所有数据
hgetall key
# 删除数据
hdel key field [field ...]
# 获取hash中field的数量
hlen key
# 判断hash中是否存在指定的field
HEXISTS key field
- 其它操作
# 给hash列表中指定的field增加指定的数字,如果key不存在会创建一个key,如果field不存在,会将该字段的值在操作之前设置为0
HINCRBY key field increment
# 查询hash列表中所有的field
HKEYS key
# 查询hash列表中所有的field所对应的value
HVALS key
4.3 list
按顺序存储多个数据,底层使用的是双向链表结构存储,但是大量数据查询效率不高
使用场景:关注列表
基本命令:
- 增删改查
# 添加数据,数据放在链表的头部
LPUSH key element [element ...]
# 添加数据,数据放在链表的尾部
RPUSH key element [element ...]
# 获取指定索引范围的数据
lrange key start stop
# 获取指定索引的数据,不存在则返回空
lindex key index
# 获取list的中元素的个数
llen key
# 获取链表的头部的元素并弹出
LPOP key
# 获取链表尾部的元素并弹出
RPOP key
# 给一定的超时时间内弹出非空列表的头元素,如果是空列表将在超时时间内这个客户端将被阻塞
BLPOP key [key ...] timeout
# 给一定的超时时间内弹出非空列表的尾元素,如果是空列表将在超时时间内这个客户端将被阻塞
BRPOP key [key ...] timeout
# 从存于 key 的列表里移除前 count 次出现的值为 value 的元素
LREM key count value
# 在列表的某个value的前或后插入一个元素,这个value必须是元素的值
LINSERT key BEFORE|AFTER pivot value
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
4.4 set
存储大量的数据,且查询效率相对于list更高
使用场景:随机推荐,共同关注
基本命令:
- 增删改查
# 添加成员
SADD key member [member ...]
# 获取全部数据
SMEMBERS key
# 移除数据,移除时如果该数据不存在,则忽略不存在的数据
SREM key member [member ...]
# 获取集合中成员的个数
SCARD key
# 获取某个数据是否是集合的成员
SISMEMBER key member
# 从集合中随机返回几个元素
SRANDMEMBER key [count]
# 从集合随机删除几个元素,并返回该元素
SPOP key [count]
- 其它操作
# 获得指定集合与其它集合的交集元素
SINTER key [key ...]
# 返回一个集合与给定的集合的差集元素
SDIFF key [key ...]
# 返回多个集合的并集元素
SUNION key [key ...]
# 获得指定集合与其它集合的交集元素,并存储到另一个集合
SINTERSTORE destination key [key ...]
# 返回一个集合与给定的集合的差集元素,并存储到另一个集合
SDIFFSTORE destination key [key ...]
# 返回多个集合的并集元素,并存储到另一个集合
SUNIONSTORE destination key [key ...]
# 将一个元素从原集合移动到另一个集合
SMOVE source destination member
4.4 sorted_set
有序的set
使用场景:排行榜
基本命令:
- 增删改查
# 添加数据
# 向有序set集合中添加元素并指定分数
# XX: 如果元素存在,就更新该元素,如果元素不存在,则忽略
# NX: 如果元素不存在,才添加新元素。如果元素已经存在,则忽略
# CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。 所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,ZADD返回值只计算新添加成员的数量。
# INCR: 当ZADD指定这个选项时,元素的操作就等同ZINCRBY命令,对元素的分数进行递增操作。
ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
# 查询指定索引范围的成员数据,可以加上WITHSCORES参数,返回结果时回带上成员的分数,顺序为分数由低到高
ZRANGE key start stop [WITHSCORES]
# 查询指定索引范围的成员数据,可以加上WITHSCORES参数,返回结果时回带上成员的分数,顺序为分数由高到低
ZREVRANGE key start stop [WITHSCORES]
# 删除指定的成员
ZREM key member [member ...]
# 获取某个分数范围内的数据,并且支持分页,分数由低到高
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
# 获取某个分数范围内的数据,并且支持分页,分数由高到低
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
# 删除指定分数范围的数据
ZREMRANGEBYSCORE key min max
# 根据索引范围删除数据
ZREMRANGEBYRANK key start stop
# 获取有序集合中元素的个数
ZCARD key
# 获取指定分数范围内的元素个数
ZCOUNT key min max
# 获取成员的索引,根据分数由低到高
ZRANK key member
# 获取成员的索引,根据分数由高到底低
ZREVRANK key member
# 获取成员的分数
ZSCORE key member
# 修改某个成员的分数
ZINCRBY key increment member
5.通用命令
- key的通用命令
# 删除key
del key
# 判断key是否存在
exists key
# 获取key的类型
type key
# 设置key的过期时间,单位为秒
EXPIRE key seconds
# 设置key的过期时间,单位为毫秒
PEXPIRE key milliseconds
# 获取key得过期时间,单位为秒
ttl key
# 获取key的过期时间,单位为毫秒
pttl key
# 将key切换为永久保存的key
PERSIST key
# 查询匹配pattern的所有key(* 代表任意数量的任意符号,? 匹配任意一个符号 [] 匹配一个指定的符号)
KEYS pattern
# 为key改名
RENAME key newkey
# 为key改名,仅当newkey不存在的时候
RENAMENX key newkey
# 给list,set,sorted_set元素排序
sort key
# 移动key到指定的数据库
MOVE key db
- db的通用命令
# 切换数据库(0-15)
select index
# 查询当前数据库有多少个key
dbsize
# 清空当前数据库
flushdb
# 清空所有数据库
flushall
6.配置文件
- 单位不区分大小写,units are case insensitive so 1GB 1Gb 1gB are all the same.
- 可以包含其它的配置文件
include .\path\to\local.conf
include c:\path\to\other.conf
- 网络配置
# 绑定的本机的ip地址
bind 127.0.0.1
# 保护模式默认开启,防止其它没有配置身份验证的客户端访问
protected-mode yes
# 端口设置,默认为6379
port 6379
- GENERAL(通用配置)
# 后台运行默认是关闭的
daemonize no
# 默认有16个数据库,默认使用的数据库是db 0
databases 16
- SNAPSHOTTING(快照)
# 将数据库保存到磁盘
# 在900秒内,至少有一个key修改了
save 900 1
# 在300秒内,至少有10个key修改了
save 300 10
# 在60秒内,至少有10000个key修改了
save 60 10000
# 持久化失败的时候停止redis的写入,一般情况下,这个应该设置为no
stop-writes-on-bgsave-error yes
# 持久化时的数据压缩
rdbcompression yes
# 检查rdb文件内容的正确性
rdbchecksum yes
# rdb文件的名字
dbfilename dump.rdb
# rdb文件存放的目录
dir ./
- REPLICATION(复制)
- SECURITY(安全)
# 设置redis的登录密码
requirepass 123456
- LIMITS
# 设置客户端同时连接的最大数量,默认是10000
maxclients 10000
# 存数据的最大内存,该值的大小要合适,既要考虑本身,也要考虑slave需要的内存
maxmemory <bytes>
# MAXMEMORY POLICY,当数据达到最大内存的时候采取的策略
# 对设置了过期的key进行LRU算法删除
volatile-lru -> remove the key with an expire set using an LRU algorithm
# 根据LRU算法删除任意的key
allkeys-lru -> remove any key according to the LRU algorithm
# 随机删除设置了过期时间的key
volatile-random -> remove a random key with an expire set
# 随机删除任意key
allkeys-random -> remove a random key, any key
# 删除即将过期的key
volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# 不过期,在写入redis时返回错误,这是默认的策略
noeviction -> don't expire at all, just return an error on write operations
- APPEND ONLY MODE(另一种持久化模式)
# 默认情况下,Redis异步地将数据集转储到磁盘上。这种模式在很多应用中已经足够好了,但是Redis进程的问题或者停电可能会导致几分钟的写丢失(取决于配置的保存点)。它提供了更好的持久性。例如使用默认的data fsync策略(见后面的配置文件)Redis可以损失一秒钟的写在一个戏剧性的事件,如服务器断电,或一个单独的写,如果一些错误的Redis进程本身发生,但操作系统仍然正常运行。
# AOF和RDB持久性可以同时启用,不会出现问题。如果启动时启用了AOF, Redis将加载AOF,即具有更好耐久性保证的文件。
# 默认情况下,aof持久化模式是关闭的
appendonly no
# aof持久化的文件名
appendfilename "appendonly.aof"
# 默认是每秒持久化一次,可能会丢失一秒的数据。no、always、everysec三种模式
appendfsync everysec
# 在重写时不进行追加操作
no-appendfsync-on-rewrite no
# 触发自动重写的阈值
# 当前大小相对于base size所增长的百分比
auto-aof-rewrite-percentage 100
# 当前大小大于最小的文件大小
auto-aof-rewrite-min-size 64mb
7.持久化
7.1 RDB
保存当前数据集的快照
# 将数据库保存到磁盘
# 在900秒内,至少有一个key修改了
save 900 1
# 在300秒内,至少有10个key修改了
save 300 10
# 在60秒内,至少有10000个key修改了
save 60 10000
# 持久化失败的时候停止redis的写入,一般情况下,这个应该设置为no
stop-writes-on-bgsave-error yes
# 持久化时的数据压缩
rdbcompression yes
# 检查rdb文件内容的正确性
rdbchecksum yes
# rdb文件的名字
dbfilename dump.rdb
# rdb文件存放的目录
dir ./
- save指令
由于redis是单线程的工作模式,所有的命令是一个一个执行的。所以save指令会阻塞当前服务器,直到rdb持久化完成,有可能会造成很长时间的阻塞,不建议使用
- bgsave指令,在后台执行rdb持久化,不会造成阻塞
- rdb的触发方式
- 手动触发(执行save和bgsave命令)
- 执行shutdown指令和flushall都会生成dump.rdb文件
- save命令和bgsave命令都可以生成RDB文件。
- 自动触发
- 按照配置文件里的条件触发
- 全量复制
- 手动触发(执行save和bgsave命令)
- 优点
- RDB是一个紧凑压缩的二进制文件,存储效率高
- RDB存储的是在某个时间点的数据快照,适用于数据备份、全量复制等场景
- RDB恢复数据的速度要比AOF快
- 缺点
- 无法实时持久化,宕机丢失数据的可能性很大
- bgsave执行时会fork出一条子进程,会消耗一定内存
7.2 AOF
AOF(Append Only File):以文件的方式记录每次写命令,重启是再重新执行AOF文件中的命令以达到恢复数据的目的,解决了数据持久化的实时性。
7.2.1 AOF的执行步骤
7.2.2 AOF重写
随着命令不断地写入AOF文件,该文件也会越来越大,就会触发AOF的重写机制,重写后得到的新AOF文件包含了恢复当前数据集所需的最少命令集合。重写完成后,Redis就会从旧AOF文件切换到新的AOF文件,并开始对新的AOF文件进行追加操作。
AOF的重写规则
# 默认情况下,Redis异步地将数据集转储到磁盘上。这种模式在很多应用中已经足够好了,但是Redis进程的问题或者停电可能会导致几分钟的写丢失(取决于配置的保存点)。它提供了更好的持久性。例如使用默认的data fsync策略(见后面的配置文件)Redis可以损失一秒钟的写在一个戏剧性的事件,如服务器断电,或一个单独的写,如果一些错误的Redis进程本身发生,但操作系统仍然正常运行。
# AOF和RDB持久性可以同时启用,不会出现问题。如果启动时启用了AOF, Redis将加载AOF,即具有更好耐久性保证的文件。
# 默认情况下,aof持久化模式是关闭的
appendonly no
# aof持久化的文件名
appendfilename "appendonly.aof"
# 默认是每秒持久化一次,可能会丢失一秒的数据。no、always、everysec三种模式
appendfsync everysec
# 在重写时不进行追加操作
no-appendfsync-on-rewrite no
# 触发自动重写的阈值
# aof_current_size相对于aof_base_size所增长的百分比
auto-aof-rewrite-percentage 100
# aof_current_size大于auto-aof-rewrite-min-size
auto-aof-rewrite-min-size 64mb
# 手动重写命令
bgrewriteaof
7.3 RDB和AOF对比
持久化方式 | RDB | AOF |
---|---|---|
占用存储空间 | 会对数据进行压缩,占用小 | 会记录所有的写命令,占用大 |
存储速度 | 慢 | 快 |
恢复速度 | 快 | 慢 |
启动优先级 | 低 | 两种方式都开启,会优先加载AOF文件 |
8.事务
Redis开启事务后,会将所有的命令放到一个队列中,当调用执行命令时,这些命令才会被依次执行
8.1 事务相关的命令
# 此命令用于开启事务,客户端可以继续向服务器发送任意多条命令,这些命令不会立即执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。我们也可以使用DISCARD命令,将队列清空,并放弃执行事务
MULTI
# 提交事务
EXEC
# 清空队列,并放弃事务
DISCARD
# 标记所有的key被监视起来,相当于乐观锁,在事务中执行时,发现被监视的key的被修改了,则事务执行失败。事务执行后被监视的key需要重新监视
WATCH
127.0.0.1:6379> watch k1
OK
# 刷新一个事务中已被监视的key
UNWATCH
8.2 Redis的事务不是原子性的
# 当我们开启事务后,向队列中提交的一系列命令,如果有语法的错误,这时候在提交事务的时候,是会执行失败的,事务是具有原子性的
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> jjjjjj
(error) ERR unknown command 'jjjjjj'
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
# 当我们开启事务后,向队列中提交的一系列命令,如果没有语法错误,但是在事务提交之后,事务的执行过程有的命令会报错,这个时候其它的命令是会正常执行的,事务不具有原子性
127.0.0.1:6379> set k1 sss
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379>
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
9. 数据删除策略
9.1 定时删除
设置过期时间的key会在过期时间达到被立即删除,会占用一定的cpu,影响redis的响应时间和吞吐量
用CPU性能换取存储空间
9.2 惰性删除
数据到达过期时间,不会立即做删除处理,而是等下次访问该数据时,才将该数据删除
用存储空间换取CPU性能
9.3 定期删除
定期进行随机清理,每秒花费固定的CPU资源维护内存
9.4 数据淘汰
# 存数据的最大内存,该值的大小要合适,既要考虑本身,也要考虑slave需要的内存
maxmemory <bytes>
# 每次选取的待删除数据的个数
maxmemory-samples 5
# LRU Least Recently Used 最近最少使用的
# LFU Least Frequently Used 使用次数最少的
volatile-lru -> 删除要过期的key中最近最少使用的
allkeys-lru -> 删除任意最近最少使用的
volatile-lfu -> 删除要过期的key中最少使用的
allkeys-lfu -> 删除任意最少使用的
volatile-random -> 随机删除要过期的key
allkeys-random -> 随机删除任意key
volatile-ttl -> 挑选要过期的key进行删除
noeviction -> 不删除,当内存不够时,写入返回错误
10. 主从复制
- 读写分离:master负责写操作,slave负责读操作,提高服务器的读写负载能力
- 故障恢复:当master处出现问题时,由slave提供服务,实现快速的故障恢复
- 数据冗余:实现数据热备份,是持久化之外的另一种冗余方式
- 高可用基础:基于主从复制,搭建哨兵模式和集群,实现redis的高可用
10.1 手动指定master
# 绑定master,在5.x版本中开始替代SLAVEOF使用
REPLICAOF host port
# 绑定master,在5.x版本中已过时
SLAVEOF host port
10.2 配置文件中指定master
# 在redis.conf中添加如下
replicaof <masterip> <masterport>
10.3 slave断开master
replicaof no one
10.4 授权访问
# master配置文件设置密码
requirepass 123456
# master客户端发送命令设置密码
config set requirepass 123456
# master客户端获取密码
config get requirepass
# 客户端连接
auth password
# slave的配置文件中设置master的密码
masterauth <master-password>
10.5 工作流程
- slave与master建立连接
- 同步master的数据到slave(1-全量复制rdb文件,2-增量复制(写入命令))
11. 哨兵模式
之前的主从复制模式,如果主机宕机,需要人为的操作来将另外一台服务器切换为master,费时费力,还会造成一段时间内服务不可用。哨兵模式可以用来解决该问题。
哨兵可以在后台监控master是否出现故障,如果出现故障会根据投票数在slave里选出一台服务器作为新的master。哨兵的总数量一般为单数
11.1 开启哨兵模式
11.1.1 创建sentinel.conf
# sentinel去监视一个名为mymaster的主机,host为127.0.0.1,端口为6379,主机宕机需获得2个sentinel的同意
sentinel monitor mymaster 127.0.0.1 6379 2
# sentinel认为主机宕机所需要的毫秒数。如果sentinel向主机发送ping命令,如果主机在给定的毫秒数之内没有对ping命令给出回复,或者返回一个错误,那么sentinel认为主机已经宕机
sentinel down-after-milliseconds mymaster 60000
# 在执行故障转移时,同步数据的时间,如果超过改时间,就认定为同步超时
sentinel failover-timeout mymaster 180000
# 在执行故障转移时,最多可以有多少个从机同时对主机的数据进行同步,这个数字越小,完成故障转移所需的时间就越长
sentinel parallel-syncs mymaster 1
11.1.2 启动哨兵
redis-sentinel sentinel.conf
11.1.3 工作原理
- 监控
- 获取各个sentinel的状态
- 获取master的状态
- 获取所有slave的状态
- 通知
- 故障转移
12. 集群
为了实现redis服务的高可用,我们需要搭建redis的集群
12.1 配置文件
在redis.conf中做出如下修改
# 开启cluster
cluster-enabled yes
# cluster的配置文件名,会自动生成
cluster-config-file nodes-6379.conf
# cluster节点服务响应的超时时间
cluster-node-timeout 15000
12.2 创建集群
# 在redis的文件目录下的src目录下,有redis-trib.rb文件,运行该文件安装ruby和rubygems
# 使用create命令 --replicas 1 参数表示为每个主节点创建一个从节点,其他参数是实例的地址集合。
./redis-trib.rb create --replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7000
13.企业级解决方案
13.1 缓存预热
提前将一些热点数据加载到redis缓存中,避免用户在请求数据时,直接查询数据库
13.2 缓存雪崩
定义:短时间内大量key集中过期
解决方案:
-
构建多级缓存架构
- nginx缓存+redis缓存
-
优化mysql耗时操作
-
监控redis服务器性能指标
-
限流、降级
- 短时间内限制一部分请求访问,降低应用服务器的压力
-
LRU与LFU算法的切换
-
数据有效期策略调整,稀释集中到期的key的数量
-
超热数据可以设置为永久key
-
定期维护
13.3 缓存击穿
定义:某个热点数据key过期的瞬间,高并发访问时直接去请求数据库
解决方案:
- 监控访问量,延长热点数据的过期时间
- 定时刷新数据的过期时间
13.4 缓存穿透
定义:高并发查询了数据库中没有的数据,redis中是肯定没有缓存这些数据,从而导致请求直接访问数据库
解决方案:
- 将数据库中查询不到的数据缓存为null
- 布隆过滤器。命中的数据放行,未命中的数据进行拦截
- 实时监控
- key加密,对不符合规则的key进行拦截