Redis

作者: 我是一名搬运工 | 来源:发表于2019-07-01 08:54 被阅读0次

1、作用(缓存/排行榜/计数器/统计在线用户数/简单消息队列)

2、数据结构(String/Hash/List/Set/ZSet)

        1)String:

            int:8个字节的长整型。

            embstr:小于等于39个字节的字符串。

            raw:大于39个字节的字符串

        2)Hash:

            ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。

            hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。

        3)List(可以重复、可以通过下标找):

            ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使用。

            linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现

        4)Set(不可以重复,完全无序):

            intset(整数集合):当集合中的元素都是整数且元素个数小于set-maxintset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实现,从而减少内存的使用。

            hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现.

        5)zSet(不可以重复,通过score排序):

            ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplistentries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。

            skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率会下降.

3、内存回收策略

        1)过期键删除策略

        对于redis中设置了expireTime的键,键是会过期的,因此对于过期键有相应的删除策略,包括两种策略:

        惰性删除:每次查询的时候,看这个键的过期时间有没有到,如果到的话,直接删除并返回空值;

        定时任务删除:由于惰性删除不能回收不被访问的键,因此有了定时任务删除策略。每秒执行10次定时任务,每次定时任务会抽样删除过期键。

        2)内存满了回收策略

        当redis的内存使用达到maxmemory的上限时,配置memory-policy采用相应的内存回收策略,具体包括:

        Noeviction:默认策略,不回收,直接拒绝写入;

        allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。推荐使用,目前项目在用这种。(推荐)

        allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。应该也没人用吧,你不删最少使用 Key,去随机删。

        volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。不推荐。

        volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。依然不推荐。

        volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。不推荐。如果没有对应的键,则回退到noeviction策略。

4、线上禁用命令

        Redis中有些命令是线上环境绝对禁止的,有些是尽量禁止的,包括:

        Keys:该命令是查询redis里所有的键,时间复杂度是O(N),会导致线上环境卡死;

        Hgetall:该命令是查询redis里某个hash的key的hash里所有的键值对,会导致线上环境卡死;

        Smembers:该命令是查询redis里某个set里所有的值,会导致线上环境卡死;

        Zrange:该命令是查询redis里某个zset里面所有的值,会导致线上环境卡死;

        flushAll:清除所有Redis的库;

        flushDB:清除Redis的当前库;

        Config:该命令可以修改redis的配置。

指令优化:

        1)对于keys\flushdb\flushall\config命令,需要通过rename-command操作,把这些命令的名称改掉,以防误操作。

        2)对于hgetall、Smembers、Zrange这类全量遍历命令,由于在value里面数据较多的时候,会导致线上环境卡死,所以在无法确定value里值的数据量时,绝不能使用,而是换成scan命令。

        Scan命令是渐近遍历命令,它不会全查,默认它每执行一次查10个,所以这样可以有效防止阻塞Redis的其它client。同样的,其它的hscan\sscan\zscan也是一样的机制。

5、慢查询日志

        Redis跟mysql一样,也有慢查询日志,开通慢查询日志有助于找到执行有问题的语句,从而进行调优,一般来说,对于key-value中,value里值比较多的键,查询就会比较慢,所以,通过慢查询可以帮我找到哪些键设计的有问题,需要重新设计。

        慢查询日志列表的长度可以设置成1000,这样,慢查询列表里面有放1000条慢查询;

        慢查询日志的判定时间可以设置成1毫秒,这样,超过1毫秒的查询都会放进来。

6、批量处理命令

        为了提升redis操作的执行效率,redis提供了一些批量的执行命令,一个命令可以执行多个操作,以前是多次操作多次网络传输,现在是多次操作一次网络传输,节省时间,如:

        Mset:可以执行多个Key value的设置操作;

        Mget:可以获取多个key的值;

        Hmset:可以执行一个key里多个field-value的设置操作;

        Hmget:可以获取一个key里多个field的查询操作。

        上述的批量执行命令是redis服务端自带的命令,但是批处理命令毕竟只有那么几个,大部分的命令都是不支持批处理的,因此,还有另外一种机制,来实现批量处理,Pipeline.

        Pipeline也是把一堆的命令,打个包,一起包给redis服务端执行,节约了网络传输时间,每次pipeline放的命令不能太多,要不要执行太慢了,毕竟redis是单线程的,会阻塞。

7、线程模型(单线程+IO多路复用)

8、大Key排查解决

        大Key对于Redis来说,危害很大,一个单纯的大key,操作的时候非常耗时,阻塞redis;一个元素过多的hash\set\zset\list,操作的时候也很耗时,不光是因为元素过多,只能选择性能最差的内部数据结构,而且在查询遍历等操作的时候,也很慢。

        排查大key,只需要执行bigkeys来找就行,bigkeys可以统计出来哪些的string类型的Key长度大,以及哪些set/hash/list/zset的value内元素个数多。

9、如何进行持久化

        Redis持久化提供两种方式,RDB和AOF

        1)RDB

        RDB是把REDIS中内部的全部数据生成快照,保存成.rdb文件。

        RDB提供自动触发的机制,触发的时机包括:

        ——从节点每次从主节点全量复制的时候,主节点会自动执行bgsave,生成RDB

        ——主节点执行debug reload或者shutdown的时候,会自动bgsave,生成RDB。

        RDB的优缺点如下:

        ——RDB是数据快照,拿来做数据恢复的速度非常快;

        ——RDB每次都要主进程fork一个子进程来生成,很耗时,所以RDB不能频繁执行。

        2)AOF

        AOF是把REDIS的写命令不断地追加写到日志里。

        AOF一般设置成每秒都执行一次。

        AOF的日志数据量不断增大,达到AOF日志总容量的一定比例后,就需要执行rewrite日志,把日志文件变小。 

        3)两种方式结合

        我们可以使用RDB加AOF两种结合的方式,使用RDB做数据冷备,使用AOF做数据恢复。

10、什么数据进缓存(热点读数据/不频繁更新)

11、如何解决缓存与存储数据不一致

        问题出现原因:存储修改了,缓存还是以前的缓存,就会出现缓存里查的不是最新的数据。

        解决办法:数据库修改数据之前,把缓存清除掉,可以用@CacheEvit来清除,里面有个beforeInvocation的参数,设置为true,表示先清缓存,再修改数据库。

12、如何解决缓存雪崩

        问题出现原因:缓存服务器挂了,所有的数据请求全部打到数据库,打数据库打死了。

        解决办法:

        1)通过高可用技术,如哨兵或者cluster,保证就算挂了一个,也有其它项上;

        2)使用二级缓存,一级缓存用本地ehcacche,二级缓存用redis,就算redis挂了,还在本地ehcache在。当然本地ehcache只能缓存少量数据。

        3)Hystrix限流,限定只有少量请求能够访问数据库,确保不会压死数据库。

13、如何解决缓存穿透

        问题出现原因:查询的数据在缓存和数据库里都没有,导致这些查询都打到数据库,把数据库打死了。

        解决办法:

        1)对于数据库查不到的数据,直接把NULL写到缓存里,这样下次来查的时候,直接缓存就返回NULL了;

        2)这些空值要设置过期时间,要不然数据库全是空值,过期时间设个5分钟就行;

        3)如果数据库里又插了这条数据,那就通过@CacheEvit把这个缓存清掉。

14、如何解决热点KEY

        问题出现原因:热点KEY只会存在一个缓存服务器上,一旦该KEY的OPS过大,超出了单台Redis的OPS上限,就会把Redis打死了。

        解决办法:

        1)采用本地缓存ehcache,配合Redis做二级缓存。本地缓存只缓存少量的数据,这样热点KEY就只需在web服务本机读取缓存就可以了。

15、如何保证高可用

        Redis支持高可用的方式主要是采用Sentinel哨兵,它能够做自动故障转移。

        Redis做sentinel需要另外部署3台sentinel节点,sentinel就是用来做redis数据节点的故障检测和转移的。

        Redis单机只能支持5万左右并发,超过5万就要上主从节点,做成读写分离的,扩充读的并发数。

        在Jedis中使用Sentinel,只需要申明JedisSentinelPool池就可以了,然后通过getResource获取一个Jedis客户端,来处理redis的读写操作。

16、如何支持分布式

        Redis支持分布式的方式是采用redis cluster,把数据分散到每一个数据节点中,至于哪个数据分到哪个数据节点,是通过分布式寻址算法来确定的。

        Redis Cluster和sentinel不一样,它不需要引入额外的监测节点,数据节点之间通过gossip协议,相互之间发送Ping\pong\meet\fail命令,来确定其它节点是不是还存活、有没有新节点加入、有没有节点挂了。

        如果节点监测到有别的节点挂了,会把这个节点设为主观下线状态。如果有超过一半的节点都发现这个节点挂了,就会触发客观下线状态,把这个节点剔除掉,剔除掉之后,这个节点的从节点会申请成为主节点,顶替原来的节点。

        Redis的分布式寻址算法用的是虚拟槽算法,一共16384个slot,所有的主节点平均分配这些slot,每一个主节点都维护一份列表,里面有每个节点与slots的对应关系,这样就知道哪个节点对应哪些slots。

        数据(KEY/VALUE)与slot的对应关系是通过hash算法来确定的,进行CRC16(KEY) mod 16384 操作后,就确定了一个key放在哪个slot里面。

        每次有新节点加入或者旧节点挂了,都会触发slot的自动迁移。

相关文章

网友评论

      本文标题:Redis

      本文链接:https://www.haomeiwen.com/subject/jhuacctx.html