亦碎流年 2020-06-11
Redis是C语言开发的一个开源的(基于BSD协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。
它是一种NoSQL(Not-Only SQL)的数据库。
Redis作为一个内存数据库,性能优秀,数据在内存中,读写速度非常快,支持并发10W QPS。
单进程单线程,是线程安全的,采用IO多路复用机制。
丰富的数据类型,支持字符串(string)、散列(hash)、列表(list)、集合(set)、有序集合(sorted set)等。
支持数据持久化。可以将内存中的数据保存到磁盘中,重启时加载。
主从复制,哨兵,高可用。
可以作为分布式锁。
可以作为消息中间件使用,支持发布订阅模式。
Redis内部使用一个RedisObject对象来表示所有的key和value。type表示一个value具体是什么数据类型,encoding是不同数据类型在Redis内部的存储方式。
比如type=string表示value存储的是一个普通字符串,那么encoding可以是raw或者int。
1.String:
Redis最基本的类型,可以理解成与Memcached一样的类型,一个Key对应一个Value。Value不仅是String,也可以是数字。
String类型是二进制安全的,意思是Redis的String类型可以包含任何数据,比如图片或者序列化的对象。String类型的值最大能存储512M。
常用命令:get, set
2.Hash:
Hash是一个兼职(key-value)的集合。Redis的Hash是一个String的Key和Value的映射表,Hash特别适合存储对象。
常用命令:hget, hset, hgetall
3.List:
List的简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
常用命令:lpush, rpush, lpop, rpop, lrange(获取列表片段)
应用场景:关注人列表, 粉丝列表
数据结构:List就是链表,可以用来当做消息队列。Redis提供了List的Push和Pop操作,还提供了操作某一段的API,可以查询或者删除某一段的元素。
实现方式:Redis List的实现是一个双向链表,即可以支持反向查找和遍历,更方便操作,不过会带来额外的内存开销。
4.Set:
Set是Sting的无需集合。
常用命令:sadd, spop, smembers, sunion等
应用场景:Redis Set对外提供的功能和List一样是一个列表,特殊之处在于Set是自动去重的,而且Set提供了判断某个成员是否在一个Set集合中。
实现方式:集合是通过HashTable来实现的。Set中的元素是没有顺序的,而且是没有重复的。
5.Sorted Set:
Sorted Set和Set一样是String类型元素的集合,且不允许有重复的元素。
和Set相比,Sorted Set关联了一个Double类型权重的参数score,使得集合中的元素能够按照score进行有序排列。Redis正是通过score来为集合中的成员进行从小到大的排序。
常用命令:zadd, zrange, zrem, zcard等
使用场景:Sorted Set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且插入是有序的,及自动排序。
实现方式:Redis Sorted Set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射。而跳跃表里存放的是所有成员,排序依据是HashMap里存的score,使用跳跃表的结构可获得比较高的查询效率,并且在实现上比较简单。
数据类型应用场景总结:
分布式环境下非常容易出现缓存和数据库间数据一致性的问题。针对这一点,如果项目对缓存的要求是强一致性的,那么就不要使用缓存。
我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。
合适的策略包括合适的缓存更新策略,更新数据库后及时更新缓存,缓存失败时增加重试机制。
Redis雪崩指的是缓存数据大面积过期失效,流量直接打到数据库上,增加数据库压力导致数据库挂掉。
解决方法:
缓存穿透是指缓存和数据库中都没有的数据,但用户一直发送请求,导致一直访问数据库,增加数据库的压力,严重会是数据库挂掉。
解决方法:
与缓存雪崩类似,但雪崩是因为大面积缓存失效,击垮了数据库。而缓存击穿是指一个key非常热点,在不停的扛着大量的请求,大并发集中对着一个点进行访问,当这个key是失效的瞬间,持续的大并发直接落到了数据库上,就在这个key的点上击穿了缓存。
解决方法:
Redis官方提供的数据可以达到10w+ 的QPS(每秒内的查询数),这个数据并不比Memcached差。
Redis是单进程单线程的模型,因为Redis完全是基于内存的操作,CPU不是瓶颈,Redis的瓶颈最有可能是机器内存的大小伙子网络带宽。避免了多线程线程切换带来的损耗。
Redis和Memcached区别
Redis有6种淘汰策略:
Redis设置过期时间:
常用方式:expire key time(以秒为单位)
字符串独有方式:setex(key,senconds,value)
除了字符串自己独有设置过期时间的方法外,其他方法都需要依靠expire方法来设置时间。
如果没有设置时间,那就是永不过期。如果设置了过期时间,之后又想让不过期,使用persist key。
定时删除
懒汉式删除
定期删除
【懒汉式删除+定期删除】
Memcached只用了懒汉式删除,而Redis同时使用了懒汉式删除与定期删除。
懒汉式删除流程:
1)在进行get或setnx等操作是,先检查key是否过期;
2)若过期,删除key,然后执行相应操作;
3)若没过期,直接执行相应操作;
定期删除流程:
对指定个数个库的每一个库随机删除小于等于指定个数个过期key
1)变量每个数据库;
2)检查当前库中的指定个数key(默认是每个库检查20个key);
3)如果当前库中没有一个key设置了过期时间,直接执行下一个库的遍历;
4)随机获取一个设置了过期时间的key,检查该key是否过期,如果过期,删除key;
5)判断定期删除操作是否已经达到了指定时长,若已达到,直接退出定期删除;
对于定期删除,会有全局全量current_db来记录下一个将要遍历的库,假如有16个库,本次删除遍历了10个,那此时current_db就是11,下次就会从11开始,假如current_db=15,那么只会遍历就从0开始。
Redis为了保证效率,数据缓存在了内存中,但是会周期性的把更新的数据写入磁盘或者吧修改操作写入追加的记录文件中,以保证数据的持久化。
RDB:已快照形式直接把内存中的数据保存到一个dump的文件中,定时保存。
AOF:把所有对Redis进行修改的命令都存到一个文件中。
Redis默认是使用RDB的持久化方式。当Redis重启的时候,它会优先使用AOF文件来还原数据集,因为AOF文件保存的数据集通常比RDB文件所保存的数据更完整。当然也可以关闭持久化功能,让数据只在服务器运行时存。
默认Redis是会以快照RDB的形式将数据持久化到磁盘的一个二进制文件dump.rdb。
当Redis需要做持久化是,Redis会fork一个子进程,子进程将数据写到磁盘上一个临时RDB文件中。当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处是可以copy-on-write。
使用AOF做持久化,每一个命令都通过write函数追加到appendonly.aof中。
AOF可以做到全程持久化。这样Redis没执行一个修改操作命令,都会把它添加到AOF文件中,当Redis重启时,将会读取AOF文件进行重放,恢复到Redis关闭前的最后时刻。
总结:
RDB备份速度和恢复速度都要比AOF快,但可能会丢失数分钟内的数据。AOF处理巨大的写入会降低Redis的性能。
当然,Redis支持同时开启RDB和AOF,系统重启时,Redis会优先使用AOF来恢复数据,这样丢失的数据会最少。
Redis单节点会存在单点故障问题,可以通过对Redis配置从节点,主从配置结合哨兵模式能解决单点故障问题。提高Redis可用性。
从节点仅提供读操作,主节点提供写操作。对于读多写少的情况,可以给主节点配置多个从节点,从而提高响应效率。
1.从节点执行slaveof[masterIP][matserPort],保存主节点信息。
2.从节点中的定时任务发现主节点信息,建立和主节点的Socket连接。
3.从节点发送Ping信号,主节点返回Pong,两边能互相通信。
4.连接建立后,主节点将所有数据发送给从节点(数据同步)。主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。
Redis2.8之前使用sync[runId][offset]同步命令,Redis2.8之后使用psync[runId][offset]命令。两者的不同在于,sync命令仅支持全量复制过程,psync支持全量和部分复制。
概念介绍:
主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区中。
从节点同步主节点数据完成后,主节点将缓冲区的数据继续发送给从节点,用于部分复制。
主节点相应写命令时,不但会把命名发送给从节点,还会写入复制积压缓冲区,用于复制命令丢失的数据补救。
psync执行流程:
从节点发送psync[runId][offset]命令,主节点有三种响应:
全量复制
全量复制执行流程:
部分复制
1.部分复制主要是Redis针对全量复制的过高开销做出的一种优化措施,使用psync[runId][offset]命令实现。
当从节点正在复制主节点时,如果出现网络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,主节点的复制积压缓冲区将这部分数据直接发送给从节点。
这样就可以保持主从节点复制的一致性。补发的这部分数据一般远远小于全量数据。
2.主从连接中断期间主节点依然响应命令,但因复制连接中断命令无法发送给从节点,不过主节点内的复制积压缓冲区依然可以保存最近一段时间的写命令数据。
3.当主从连接恢复后,由于从节点之前保存了自身已复制的偏移量offset和主节点的runId。因此会把它们当做psync参数发送给主节点,要求进行部分复制。
4.主节点接收到psync命令后首先核对参数runId是否和自身一致,如果一致,说明之前复制的是当前主节点。
之后根据参数offset在复制积压缓冲区中查找,如果offset之后的数据存在,则对从节点发送+COUTINUE命令,表示可以进行部分复制。由于缓存区大小固定,若发生缓存溢出,则进行全量复制。
5.主节点根据偏移量offset吧复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。
复制问题
Redis Sentinel(哨兵)架构图: