Redis知识汇总

 

一、为什么要使用Redis?

通常,我们使用Redis的目的主要有两个:高性能和高并发。

高性能:把数据放入缓存中,避免走数据库查询,性能提升。

高并发:Mysql扛不住高并发,用Redis来支撑高并发。

数据库建议不要超过2000/s的并发,Redis别说是4000/s,就算是4万/s也没问题。如果是非高并发的项目,那就只说高性能的作用就行了。(by中华石杉)

二、Redis的主要用途

Redis有非常广泛的用途,从大的方面来讲,其主要有以下几个用途:

①用作数据库   ②用作缓存(最常用)    ③用于消息订阅和发布(官网:Pub/Sub)   ④实现分布式锁

Redis作者在自己的博客How to take advantage of Redis just adding it to your stack(翻译:Redis作者谈Redis应用场景)中谈到了Redis常见的用法,主要有如下几点:

三、Redis的线程模型?

Redis的线程模型

四、使用缓存可能遇到的问题?

使用缓存可能遇到的问题和解决方案

五、Redis的数据类型

官网:

An introduction to Redis data types and abstractions

Data types short summary

Redis有5个基本数据结构:

string

基本的key,value都是字符串。

list

比如微博某个大V的粉丝列表

key=某大V

value=[zhangsan,lisi,wangwu]

 

分页查询:lrange

hash

存储结构化的数据。

hset user id 1
hset user name zhangsan
hset user age 18

set

去重:

  • 当数据量不大时,可以直接在JVM内存里进行去重。
  • 当数据量较大需要持久化时,可以使用redis的set数据结构去重。
  • 当数据量达到亿级别时,需要通过boomfilter来去重。

求交集:

比如将两个大V的粉丝用set结构存储,然后对两个set做交集,就能知道两个大V的共同好友有谁。

zset

基于set的,所以也可以去重。另外有得分的概念,所以可以用来实现排行榜。

添加
zadd rankborad 85 zhangsan
zadd rankborad 72 lisi
zadd rankborad 80 wangwu
zadd rankborad 93 zhaoliu

……

获取排名(从小到大):zrange rankboard 0 1000
获取排名(从大到小):zrevrange rankboard 0 1000
前三名:zrevrange rankboard 0 2

 

其它几种数据类型

BitMaps

Bitmaps是在字符串类型上面定义的位操作。一个字节由 8 个二进制位组成。

应用场景:用户访问统计/在线用户统计。

Hyperloglogs

Hyperloglogs:是一种概率相关的数据结构,提供了一种不太准确的基数统计方法,比如统计网站的 UV,存在一定的误差。

Streams

Redis 5.0 推出的数据类型。支持多播的可持久化的消息队列,用于实现发布订阅功能,借鉴了 kafka 的设计。

六、Redis底层数据结构

1. 动态字符串(SDS)

Redis 没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string SDS)的抽象类型,并将SDS用作Redis 的默认字符串表示;除了用来保存字符串以外,SDS还被用作缓冲区(buffer)AOF(持久化)模块中的AOF缓冲区。

2. 链表(list)

顺序存储对象信息,有用于缓存链表长度的属性,在插入删除对象功能中有良好性能,避免环的产生链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。链表在Redis 中的应用非常广泛,比如列表键的底层实现之一就是链表。当一个列表键包含了数量较多的元素,又或者列表中包含的元素都是比较长的字符串时,Redis 就会使用链表作为列表键的底层实现。

3. 字典(dict)

字典,又称为符号表(symbol table)、关联数组(associative array)或映射(map),是一种用于保存键值对的抽象数据结构。在字典中,一个键可以和一个值进行关联,字典中的每个键都是独一无二的。在C语言中,并没有这种数据结构,但是Redis 中构建了自己的字典实现。字典在Redis中的应用相当广泛,比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增删改查操作也是构建在对字典的操作之上。另外,字典也是hash键的实现之一

4. 跳表(skiplist)

Redis 只在两个地方用到了跳表,一个是实现有序集合键,另外一个是在集群节点中用作内部数据结构。

跳表的效率也非常高,有时可以用来代替红黑树,redis选用跳表而非红黑树的原因:

①区间查找

②跳表比红黑树实现相对更简单。红黑树的插入存在左旋和右旋操作,可能会降低效率。

③跳表更灵活。可通过改变索引构建策略,来平衡效率和内存消耗。

5. 整数集合(intset)

整数集合是集合建的底层实现之一,当一个集合中只包含整数,且这个集合中的元素数量不多时,redis就会使用整数集合intset作为集合的底层实现。

简而言之:其实就是一个特殊的集合,里面存储的数据只能够是整数,并且数据量不能过大。

6. 压缩列表(ziplist)

压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值, 要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。

七、Redis的持久化机制

官网:Redis Persistence(Redis持久化)

RDB和AOF

  • RDB持久化方式能够在指定的时间间隔能对数据进行快照存储.
  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时,会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
    (中华石杉:AOF文件会不断增大,达到一定大小,会进行重写操作(基于当时内存中的数据来重新创建一个更小的aof文件,并将旧的删除)。
  • 如果同时开启了RDB和AOF两种持久化机制,则Redis重启时会使用AOF来重建数据,因为AOF中的数据更加完整。 通过重放aof中的命令来恢复数据。

RDB原理:Redis内部一个定时器事件,每隔固定时间去检查当前数据发生的改变次数与时间是否满足配置的持久化触发的条件,如果满足则通过操作系统fork调用来创建一个子进程,该子进程默认与父进程共享相同的地址空间,这时就可以通过子进程来遍历整个内存进行存储操作,而主进程仍然可以提供服务,当有写入时有操作系统按照内存页(page)为单位来进行copy-on-write保证父子进程间不会相互影响。
(子进程将数据写到磁盘上一个临时RDM文件中,当子进程完成写临时文件后,将原来的RDB替换掉,这样会就可以copy-on-write。)

AOF原理:每条会使redis内存发生改变的命令都会追加到一个log文件中,成为Redis的持久化数据。类似于mysql的基于语句的binlog方式。

选择哪种持久化方式?

如果仅使用RDB,则会丢失数据,因为RDB是定时进行快照,存在时间窗口可能丢失数据,所以适合做冷备份,恢复速度快。因为间隔时间长,不适合做第一优先的恢复方案。

如果仅使用AOF,有两个问题:

  • 一是做冷备份时AOF没有RDB恢复速度快。
  • 二是RDB简单粗暴的快照方式,可以避免AOF这种复杂机制产生的bug(AOF曾出现过bug)。

所以应该同时使用两种持久化功能。虽然AOF方式可以数据更加完整,但是定时生成 RDB快照非常便于进行数据库备份,并且RDB 恢复数据的速度也要比 AOF 恢复的速度要快。

生产配置

AOF配置

【AOF配置】
1.开启aof
AOF方式默认是关闭的,生产环境通常都会将AOF打开(appendonly yes)。
2.AOF刷盘策略 appendfsync:AOF刷盘策略有3种取值   always     everysec(生产环境通常会使用这个,为了平衡数据安全和性能)   no mysql
--->内存策略,大量磁盘IO,【QPS达到1k-2k】 redis--->内存,磁盘持久化,单机一般来说【QPS上万】没有问题。

3.其它重要参数
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
比如:原AOF为128M,当达到100%(256M)后,此时可能会触发rewrite。但还要判断另一个参数即文件最小大小是否达到了设置的值。如果达到了,就会触发rewite。
-------------来源于中华石杉

RDB配置

【RDB配置】
save 900 1(每15分钟至少1个key改变)
save 300 10(每5分钟至少30个key改变)
save 60 10000(每分钟10000个key改变)
上面两条可不调整,第3条可根据业务调整。

若【同时开启AOF和RDB】,则
①RDB进行快照时,AOF不会进行rewrite,反之也是。
②重启时,会默认使用aof数据文件进行恢复,因为aof比rdb数据更完整。

-------------来源于中华石杉

八、Redis的高可用(复制、sentinel、cluster)

Redis的高可用(复制、sentinel、cluster)

九、Redis的键过期和淘汰策略

官网文档:

How Redis expires keys

Using Redis as an LRU cache  (将Redis当做LRU缓存来使用)

 

问题:如果设置一个/批key的超时时间为1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?

答案:超时剔除(定期删除+惰性删除) + 淘汰策略

(1)expire

Redis的key的过期有两种方式:主动过期和被动过期。

①被动过期(惰性删除):仅当客户端尝试访问key时,会检测key是否过期,如果过期了就将该key删除。但对于某些过期的key来说,如果不会再被客户端访问,那就成了漏网之鱼。

②主动过期(定期删除):Redis会定期在设置了超时的key的集合中随机挑选一些key进行检测,如果发现过期就会将其删除。Redis会以10次/s的频率来随机检查:

  1. 每次挑选20个设置有超时时间的key进行检查。
  2. 删除过期的key。
  3. 如果超过25%的key过期,继续第1步。
This is a trivial probabilistic algorithm, basically the assumption is that our sample is representative of the whole key space, and we continue to expire until the percentage of keys that are likely to be expired is under 25%

This means that at any given moment the maximum amount of keys already expired that are using memory is at max equal to max amount of write operations per second divided by 4.

这是一个微不足道的概率算法,基本上是假设我们的样本可以代表整个key空间,并且我们会继续过期,直到可能过期的key百分比低于25%.
这意味着在任何指定时刻,占用内存的已过期key的最大数量最大等于每秒最大写操作数量除以4

这样仍然会存在漏网之鱼,因为有些过期的key既没有被客户端访问到,也没有被随机检查到。但是,Redis还有淘汰策略来兜底

(2)Eviction policies(淘汰策略)

当添加key时,Redis会检查内存的使用量,如果大于maxmemory值,就会根据设置的淘汰策略来剔除key。淘汰策略可以分为三类:

noeviction是不进行淘汰,allkeys开头的是在全部key范围内进行数据淘汰,volatile开头的是在设置了过期时间的key范围内进行数据淘汰。

总共有以下几种剔除策略:

  • noeviction

    当内存不足时,不淘汰key,写入操作直接报错(一般不会用。我们的项目场景中用到了)

  • allkeys-lru
    当内存不足时,在所有key范围中移除最近最少使用的key(最常用)。也就是key未设置过期时间也可能被淘汰掉。
    Java中可以使用LinkedHashMap来实现LRU算法。
  • allkeys-random
    当内存不足时,在所有key范围中随机移除某个key(这个一般没人用)
  • volatile-lru
    当内存不足时,在设置了过期时间的key中移除最近最少使用的key(一般不太合适)
  • volatile-random
    当内存不足时,在设置了过期时间的key中,随机移除某个key
  • volatile-ttl:
    当内存不足时,在设置了过期时间的key中,越早过期的key优先移除

Redis 4.0引入了新的LFU算法(Least Frequently Used),是对LRU算法的优化。

  • allkeys-lfu:

  • volatile-lfu

LFU还提供了可调的参数

lfu-log-factor 10
lfu-decay-time 1

 

中华石杉视频中还提到了主动更新
(3)主动更新
使用场景:应用方对于数据的一致性要求高,需要在真实数据更新后,立即更新缓存数据。例如可以利用消息系统或者其他方式通知缓存更新。
一致性:一致性最高,但如果主动更新发生了问题,那么这条数据很可能很长时间不会更新,所以建议结合超时剔除一起使用效果会更好。
维护成本:维护成本会比较高,开发者需要自己来完成更新,并保证更新操作的正确性。

【最佳实践】:
低一致性业务建议配置最大内存和淘汰策略的方式使用。
高一致性业务可以结合使用超时剔除和主动更新,这样即使主动更新出了问题,也能保证数据过期时间后删除脏数据。

十、Redis的事务机制

Redis事务机制

十一、Redis实现分布式锁

中文官网:Redis分布式锁

十二、其它

Redis大批量插入 官网:Redis Mass Insertion

lua脚本 官网:Lua scripting

lru策略:官网:Using Redis as an LRU cache

Redis延迟监控框架 官网:Redis latency monitoring framework

Redis性能测试 官网:How fast is Redis?

 

Redis的序列化:

Spring整合Redis时,spring-data-redis提供了几种序列化的方式:

  • JdkSerializationRedisSerializer——使用Java序列化机制将对象序列化,Redis中存储序列化后的二进制字节。这是默认序列化方式。
  • OxmSerializer——将对象序列化为xml字符串

Spring提供了RedisTemplate和StringRedisTemplate。StringRedisTemplate继承RedisTemplate,但两者数据并不共通。使用RedisTemplate存的数据也只能由RedisTemplate来取,使用StringRedisTemplate存的数据就只能用StringRedisTemplate来取。

  • RedisTemplate默认使用的是JdkSerializationRedisSerializer。
  • StringRedisTemplate默认使用的是StringRedisSerializer,key和value都是采用此序列化保存的。

 

中华石杉:如果我往深了问,可以问的很细,结合项目扣的很细: 
比如你们公司线上系统高峰QPS
3000
请求主要访问哪些接口?
redis抗了多少请求?
mysql抗了多少请求?
你到底是怎么实现高并发的?
咱们聊聊redis的内核吧,看看你对底层了解的多么?
如果要缓存几百GB的数据会有什么坑,该这么弄?
如果缓存出现热点现象该这么处理?
某个value特别大把网卡给打死了怎么办?等等等等,可以深挖的东西其实有很多。。。。。

 

参考:

那些年用过的redis集群架构

posted @ 2020-01-09 17:15  静水楼台/Java部落阁  阅读(127)  评论(0编辑  收藏  举报