Redis必知必会
数据类型
基本类型
字符串类型
可用于计数,SMS验证码等
1 | 127.0.0.1:6379> set name jack |
一般对象用string/json存储,对象中某些频繁变化的属性抽出来用hash存储
可用于购物车系统:以用户id为key,商品id为field,商品数量为value,恰好构成了购物车的3个要素,如下图所示。
可用于秒杀系统
秒杀开始前,将商品库存生成一个对应长度的list,每次购买进行弹出。
并且,秒杀进行时,可以在用户下单时向某个list存入商品编号,异步逐条处理(类似消息队列)
用来保存多个字符串,无序不可重复
可用于用户奖池抽奖系统
1 | 127.0.0.1:6379> SADD act user1 user2 user3 |
用来保存多个字符串,有序不可重复
可用于排行榜系统,或者历史消息系统(以消息作为键,时间戳作为值)
特殊类型
顾名思义,用来存储bit的map类型,只有0和1两个值
可用于签到打卡系统:存储365天的状态只需要365bit(45.62字节)
1 | 127.0.0.1:6379> setbit sign 1 1 |
可以推算地理位置: 两地之间的距离, 根据半径查找附近的位置
1 | 127.0.0.1:6379> GEOADD cities 116.404269 39.91582 "beijing" 121.478799 31.235456 "shanghai" |
事务
Redis事务的命令主要是:multi(开启事务) exec(执行事务) discard(丢弃事务)
以及监视命令:watch(事务执行之前key被其他命令所改动,那么事务将被打断) unwatch(取消监视)
1 | 127.0.0.1:6379> multi |
如果在 set b 处失败,set a 已成功不会回滚,set c 还会继续执行。
ACID
- 原子性(Atomicity):单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,不支持事务回滚机制(rollback),所以 Redis 事务的执行并不是原子性的。
- 一致性(Consistency):事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
- 隔离性(Isolation):因为Redis使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证, 在执行事务期间不会对事务进行中断,因此,Redis的事务总是以串行的方式运行的,并且 事务也总是具有隔离性的。
- 持久性(Durability):当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,数据不会丢失,才具有持久性。RDB和内存模式下是不具有持久性的。
布隆过滤器
布隆过滤器(Bloom Filter)是由Howard Bloom在1970年提出的一种比较巧妙的概率型数据结构,它可以告诉你某种东西一定不存在或者可能存在。当布隆过滤器说,某种东西存在时,这种东西可能不存在;当布隆过滤器说,某种东西不存在时,那么这种东西一定不存在。
https://www.cnblogs.com/heihaozi/p/12174478.html
淘汰策略
策略 | 描述 |
---|---|
noeviction | 不进行删除,达到最大内存时,直接返回错误信息【默认】 |
volatile-lru | 从已设置过期的数据中随机挑选最近最少使用(less recently used)的多个key进行数据淘汰 |
volatile-ttl | 从已设置过期的数据中挑选即将要过期的数据进行淘汰 |
volatile-random | 从已设置过期的数据中随机淘汰数据 |
allkeys-lru | 从数据集中挑选最近最少使用的数据淘汰 |
allkeys-random | 从数据集中任意选择数据淘汰 |
持久化
Redis 提供了2个不同形式的持久化方式:
RDB (Redis DataBase) 和 AOF (Append Of File)
RDB(快照形式保存)
RDB在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot(快照),它恢复时是将快照文件直接读到内存里。
关于fork
- Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,等持久化过程结束,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
- 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
RDB的缺点是 最后一次持久化后的数据可能丢失。 - 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”,一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
使用方式
配置
- 可以配置文件名称
dbfilename dump.rdb
,默认为dump.rdb
- rdb文件的保存路径也可以修改
dir /myredis
,默认为Redis启动时命令行所在的目录下。 - rdb的保存策略
1
2
3
4
5
6# 900秒内执行1次set操作 则进行持久化
save 900 1
# 300秒内执行10次set操作 则进行持久化
save 300 10
# 60秒内执行1万次set操作 则进行持久化
save 60 10000
手动保存
save
直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。bgsave
fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。 Redis 服务器在bgsave执行期间仍然可以继续处理客户端的请求。
AOF(日志形式记录)
AOF以 日志 的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
使用方式
- AOF默认不开启,需要手动在配置文件中配置
appendonly yes
- 可以配置文件名称
appendfilename "appendonly.aof"
,默认为appendonly.aof
- AOF文件的保存路径同RDB的路径一致。
如遇到AOF文件损坏,可通过redis-check-aof --fix appendonly.aof
进行恢复
同步频率设置
AOF同步频率和简单来说就是持久化的频率,和rdb一样也可以通过修改配置来修改同步频率。
appendfsync always
:始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好appendfsync everysec
:每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。appendfsync no
:redis不主动进行同步,把同步时机交给操作系统。
重写机制
如果持续往磁盘中aof文件中写入日志记录,随着时间增长,aof的文件也就变得巨大,为解决这个问题,redis提供了重写的机制:抵消掉无用指令,只留下精简的指令来就可以恢复当前内存中数据。
举例,比如set a a
然后set a aa
。那么redis重写之后在aof文件中只会留下set a aa
这个指令,因为前面的操作对于恢复当前内存数据没有帮助。
- 手动重写:
BGREWRITEAOF
- 配置文件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
redis会记录上次重写的aof文件的大小,当aof文件的大于等于上次重写文件大小的100%,同时重写时候文件的大小最少是64mb,防止重写的aof文件偏小而频繁进行重写。
RDB&AOF混合使用
通过aof-use-rdb-preamble yes/no
配置
- rdb负载在某个时刻把内存整个数据dump到磁盘中,aof记录redis从dump开始,对redis key的更改操作。
- 在数据进行重写的时候,redis会把rdb文件先进行重写进aof文件,然后把剩下的aof文件追加到重写的aof文件后面。
高可用
主从复制
作用 |
描述 |
---|---|
数据冗余 | 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。 |
故障恢复 | 当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。 |
负载均衡 | 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。 |
高可用基石 | 除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。 |
使用
主从复制的开启,完全是在从节点发起的;不需要我们在主节点做任何事情。
从节点开启主从复制,有3种方式:
配置文件
- 复制一个redis的从服务器,在配置文件中配置端口(搜索
port 6379
改成其他端口)port 6380
- 修改
slaveof <masterip> <masterport>
slaveof 127.0.0.1 6379
- 启动主服务器
- 在从服务器目录使用配置文件启动即可实现主从复制,windows版命令:
redis-server redis.windows.conf
- 复制一个redis的从服务器,在配置文件中配置端口(搜索
启动命令
redis-server --slaveof <masterip> <masterport>
客户端命令
Redis服务器启动后,直接通过客户端执行命令:slaveof <masterip> <masterport>
,则该Redis实例成为从节点。
断开复制:从节点执行slaveof no one
即可断开复制。需要注意的是,从节点断开复制后,不会删除已有的数据,只是不再接受主节点新的数据变化。
查询状态:使用replication
指令以查看主从状态
哨兵模式(Sentinel)
哨兵是一个独立的进程,作为进程,它会独立运行。其原理是 哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
示意图
故障切换(failover)的过程:
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。(原文地址)
RedisCluster
RedisCluster没有使用一致性哈希,而是引入了哈希槽(hash slot)的概念。RedisCluster中有16384(2^14)个哈希槽,每个key通过CRC16校验后对16383取模来决定放置哪个槽。Cluster中的每个节点负责一部分哈希。
使用方式
首先为每个redis-server配置上
cluster-enabled yes
在redis5.0之前,redis官方提供了ruby脚本来搭建集群,但因该工具是用ruby开发的,所以需要准备相关的依赖环境。
1
2
3
4
5# ruby环境
yum install ruby (CentOS)
apt-get install ruby-full (Debian/Ubuntu)
# 创建集群
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:7006在redis5.0之后,可以直接使用redis-cli进行集群搭建
1
2
3
4# 查看帮助
redis-cli --cluster help
# 创建集群
redis-cli --cluster create --cluseter-replias 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:7006连接到一个节点
redis-cli -c -h 127.0.0.1 -p 7001
查看集群所有节点
cluster nodes
得到槽和节点的映射关系cluster slots
设置值,并感受重定向
1
2
3
4127.0.0.1:7001> set name a
-> Redirected to slot [5798] located at 127.0.0.1:7002
OK
127.0.0.1:7002> 切换到了7002服务器
选举机制
- 当一个master节点挂掉,其slave会被选举为所属哈希槽的master节点。
如果该节点没有slave,会导致整个集群不可用(可用哈希槽不全) - 这也是为什么redis搭建集群最少需要6台服务器:
投票容错机制要求超过半数节点认为某个节点挂了该节点才是挂了,所以2个节点无法构成集群。
而要保证集群的高可用,需要每个节点都有从节点,也就是备份节点,所以Redis集群至少需要6台服务器。
集群扩容/缩容
集群加入节点
1
2
3
4
5
6
7
8
9
10
11
12
13# 添加master节点
redis-cli --cluster add-node 127.0.0.1:7007 [任意集群服务器地址]
# 添加slave节点
redis-cli --cluster add-node 127.0.0.1:7008 [任意集群服务器地址] --cluster-slave -cluster-master-id [主节点id]
# 分配哈希槽
redis-cli --cluster reshard [任意集群服务器地址]
# 从其他主节点中分配多少哈希槽出来
How many slots do you want to move (from 1 to 16384?) 1500
# 将分配出来的哈希槽给定的master
What is then receiving node ID? [主节点id]
# 从哪些节点进行分配?'all'所有,'done'结束
Please enter all then soure node IDs.
Source node #1: all此时127.0.0.1:7007的哈希槽为 0-498 5461-5961 10922-11422
并且,不仅槽位分配过来,槽位上的数据也跟着过来了集群移除节点
1
2
3
4
5
6
7# 从某个节点中分配哈希槽出来
redis-cli --cluster reshard [任意集群服务器地址]
--cluster-from 127.0.0.1:7007
--cluster-to [主节点1-id] [主节点2-id] [主节点3-id]
--cluster-slots 1500
# 移除master节点
redis-cli --cluster add-node [任意集群服务器地址] [要移除的主节点id]