概要
在分布式里面满足CP (一致性、分区容错性)。
性能:对于单纯只有IO操作来说,单线程可以将速度优势发挥到最大,但是Redis也提供了一些简单的计算功能,比如排序、聚合等,对于这些操作,单线程模型实际会严重影响整体吞吐量,CPU计算过程中,整个IO调度都是被阻塞住的。
一 Redis和Memcached对比
1:数据类型:Memcached单个key-value大小有限,一个value最大只支持1MB,而Redis最大支持512MB,支持set,list等类型
2:持久性:Memcached只是个内存缓存,对可靠性无要求;而Redis更倾向于内存数据库,因此对对可靠性方面要求比较高、做了数据持久化
3:数据一致性:redis是单线程模型,保证了数据按顺序提交,Redis提供了事务的功能,可以保证一串 命令的原子性,中间不会被任何操作打断。 memcached需要cas保证数据一致性,在高并发下。性能会受到影响,甚至不如redis。
二 数据回收策略
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6种数据淘汰策略:
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据
三 持久化
fork操作都会造成主线程堵塞。这个时候服务是停止的。
3.1 RDB(redis database)
rdb为二进制文件,redis在指定的时间内(默认配置3个时间段,1分钟1W次,10分钟10次,15分钟1次),把内存的数据集快照写入到磁盘,恢复的时候是将硬盘中快照文件写入内存。redis 会单独创建(fork)一个子进程去进行持久化,先写入一个临时文件dump.rdb ,待持久化结束了,再替换上次持久化好的文件,整个过程中,主线程是不需要任何io操作的。(子进程保持数据一致性)。运维手段(上报dump.rdb)
如果需要大规模恢复,且对数据缺失不是很敏感。那么采用RDB,RDB的方式比AOF更加高效,但是最后一次持久化的数据可能丢失。一般做冷数据备份
fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑,当数据集比较大额时候,folk的过程是比较耗时的,可能会导致redis在一些毫秒级不能响应客服端请求
3.2 AOF(append only file)
(如果同时rdb和aof同时存在,先加载aof,error才加载rdb):命令
redis也会fork一个子进程,以日志的形式记录每次写操作命令。 AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,换句话说,恢复的时候,把文件记录命令全部再执行一遍
AOF是存放每条写命令的,所以会不断的增大,当大到一定程度时,AOF会做rewrite操作,rewrite操作就是基于当时redis的数据重新构造一个小的AOF文件,然后将大的AOF文件删除。
- 对于同一份文件AOF文件比RDB数据快照要大。
- AOF开启后支持写的QPS会比RDB支持的写的QPS低,因为AOF一般会配置成每秒fsync操作,每秒的fsync操作还是很高的
- 数据恢复比较慢,不适合做冷备。
四 事务
不保证原子性,部分支持事务,但是可以通过watch实现乐观锁
开启事务-》放入队列-》执行,本质上是一组命令的集合。一个事务中的所有命令都会被序列化,按照顺序串行地执行而不会被其他命令插入。
原子性:redis事务不能保证原子性,有一个执行失败,其它依被执行,没有回滚
隔离级别:没有数据那些隔离级别概念,队列里面没有提交之前都不会执行,在执行过程中也不会被其它事务打断。
- 如果中途命令(编译)出错。那么全部不执行
- 如果是运行时报错,其他依然执行。出错的不执行:字符串+1
一致性问题:
在事务之前有其他执行,那么数据就有误
WATCH (乐观锁)
可以监视一个或多个key,一旦其中有任意一个key被修改,那么事务被打断,都不会被执行,返回失败,但是并不能保证其他客户端不修改监控的值,所以当EXEC命令执行失败之后需要手动重新执行整个事务
伪代码:
exec(WATCH stock:1001);
if(exec(HGET stock:1001 state) == "in stock") {
exec(MULTI);
exec(HSET stock:1001 state "sold");
exec(EXEC);
}
五 集群
软件架构不是越复杂越好,尽量减少过度设计,我们用的主从。
5.1 主从复制
info replication —查看集群信息
- 只有1个Master,可以有N个slaver,而且Slaver也可以有自己的Slaver,由于这种主从的关系决定他们是在配置阶段就要指定他们的上下级关系,而不是Zookeeper那种平行关系是自主推优出来的。
- 读写分离,Master只负责写和同步数据给Slaver,Slaver承担了被读的任务,所以Slaver的扩容只能提高读效率不能提高写效率。
- Slaver先将Master那边获取到的信息压入磁盘,再load进内存,client端是从内存中读取信息的,所以Redis是内存数据库。
- 当一个新的Slaver加入到这个集群时,会向主服务器发送一个 SYNC 命令,Master发现新的小弟后将全量数据(全量是rdb、增量是aof文件)发送给新的Slaver,数据量越大性能消耗也就越大,所以尽量避免在运行时做Slaver的扩容
简单总结下主从模式的设计:
优点:读写分离,通过增加Slaver可以提高并发读的能力。
缺点:Master写能力是瓶颈。
虽然理论上对Slaver没有限制但是维护Slaver开销总将会变成瓶颈。
Master的Disk大小也将会成为整个Redis集群存储容量的瓶颈。
Sentinel哨兵模式:
能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
5.2 集群分片
哈希Slot [slɒt]:就是分库分表,hash取模
Redis Cluster中共有16384个hash slot,Redis会计算每个key的CRC16,将结果与16384取模,来决定该key存储在哪一个hash slot中,同时需要指定Redis Cluster中每个数据分片负责的Slot数。Slot的分配在任何时间点都可以进行重新分配。
5.3 主从分片
想扩展并发读就添加Slaver,想扩展并发写就添加Master,想扩容也就是添加Master,任何一个Slaver或者几个Master挂了都不会是灾难性的故障。
简单总结下哈希Slot的优缺点:
缺点:每个Node承担着互相监听、高并发数据写入、高并发数据读出,工作任务繁重
优点:将Redis的写操作分摊到了多个节点上,提高写的并发能力,扩容简单。