Redis重要知识点学习记录

这篇文章要解决的问题

A

  • 什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
  • redis通讯协议(RESP ),能解释下什么是RESP?有什么特点?
  • Redis 有哪些架构模式?讲讲各自的特点
  • Redis常用命令?
  • 使用过Redis分布式锁么,它是怎么实现的?
  • 使用过Redis做异步队列么,你是怎么用的?有什么缺点?
  • mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
  • redis常见性能问题和解决方案:
  • redis事物的了解CAS(check-and-set 操作实现乐观锁 )?

B

  • 为什么使用redis
  • 使用redis有什么缺点
  • 单线程的redis为什么这么快
  • redis的数据类型,以及每种数据类型的使用场景
  • redis的过期策略以及内存淘汰机制
  • redis和数据库双写一致性问题
  • 如何应对缓存穿透和缓存雪崩问题
  • 如何解决redis的并发竞争问题

以上是我们使用redis会遇到的问题,这篇文章我们将以解决这些问题为最终目的来进行总结。

1,2的14次方是16384

redis数据类型

类型 说明 命令 使用场景
String 二进制安全的字符串,该类型可以接受任何格式的数据 set key value value较小、模型简单的 value可以使用String类型存储
List 按照插入顺序排序的字符串链表 lpush name value,rpush name value,lrem name index key,llen name 在评级系统中,比如社会化新闻网站,你可以把每个新提交的链接添加到一个list,用LRANGE可简单的对结果分页;在博客引擎实现中,你可为每篇日志设置一个list,在该list中推入进博客评论等等。
Set 没有排序的字符串集合,和List类型一样,区别是不允许重复 sadd name value 可以使用Redis的Set数据类型跟踪一些唯一性数据,比如访问某一博客的唯一IP地址信息。对于此场景,仅需在每次访问该博客时将访问者的IP存入Redis中,Set数据类型会自动保证IP地址的唯一性。
SortedSet(zset) 和Set相似,一个Set中。它们之间的主要差别是SortedSet中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序 zadd name score value 1,可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更新玩家的分数,此后再通过ZRANGE命令获取积分TOP TEN的用户信息。当然也可以利用ZRANK命令通过username来获取玩家的排行信息。最后将组合使用ZRANGE和ZRANK命令快速的获取和某个玩家积分相近的其他用户的信息。2,SortedSet类型还可用于构建索引数据。3,建立一个SortedSet中元素个数不要超过 1 W。
Hash Redis hash 是一个键值(key=>value)对集合。 Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 hmset name key1 value1 key2 value2 1,对于海量数据的情况,可以自己对数据进行分桶,然后使用Hash结构来存储。对于很多value为简单的字符串,做过测试,采用hash存储更节省空间。2,将对象存储为Hash结构而不是String,可以每次只更新、获取Hash中的一个field,这样可以提高效率。比如session比较适合用hash存放

redis持久化

RDB(默认) 全量模式

SAVE

客户端可以显式触发,单线程串行化执行命令
把redis的当前状态写入磁盘作为快照保存的过程。

BGSAVE

BGSAVE命令执行始于fork出一个子线程,在子线程成功启动之后修改一些redisServer对象的状态之后执行关闭。

AOF 增量模式

基于全量的持久化保存是数据的“状态”,而增量持久化保存的是状态的每一次变迁。

每次处理完写命令后,通过propagate函数触发,propagate方法将当前命令的内容append到redisServer对象的aof_buf变量中。主循环在下一个迭代进入多路复用的select方法搴,redis会通过flushAppendOnlyFile方法将aof_buf的内容write到AOF对应的文件中。再显式调用fsync()方法才能强制地让操作系统落地数据到磁盘。
redis的AOF包含三种同步策略:

  • always:每个flushAppendOnlyFile方法直接触发fsync方法,强制数据落地到磁盘
  • every second:每秒一步触发一次fsync方法
  • no:不显式调用fsync,由操作系统决定什么时候落地到磁盘。

AOF优化

AOF文件会越来越大,降低回放加载效率。redis通过rewrite机制合并历史AOF记录。
处理方式:当增量数据积累到大于某个状态快照的程度,此时将这些增量用快照代替。但数据仍然存储在AOF文件中。

集群部署方式

主从复制(master-slave)

master节点对外提哦那个读写服务,slave节点作为master的数据备份,拥有master的全量数据,提供读服务

数据同步方式:BGSAVE

sentinel

sentinel节点间因为共同监视同一个maseter节点从而也关联了起来

sentinel节点通过定期地向master发送心跳包判断其存活状态,称为ping

proxy型

主备+sentinel+proxy

直连(拓扑结构)

16384个数据分片,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

去中心化,客户端可以和集群中任意节点链接。

redis cluster通过引入两个自增的epoch变量来使得集群配置在各个节点间达成最终一致。

节点的fail是通过集群中超过半数的节点检测失效时才生效。

redis 内存淘汰机制

在redis.conf中有一行配置

maxmemory-policy volatile-lru

该配置就是配内存淘汰策略的

  • 1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
  • 2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。
  • 3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。
  • 4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐
  • 5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐
  • 6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐
    ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

redis事务

我们可以通过MULTI命令开启一个事务

可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作

WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务
执行失败

redis常见性能问题和解决方案:

  • 1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
  • 2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
  • 3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
  • 4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内

redis 非阻塞I/O多路复用机制

我们的redis-client在操作的时候,会产生具有不同事件类型的socket。在服务端,有一段I/0多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。

单线程redis为什么快

  • 纯内存操作
  • 单线程避免了频繁的线程上下文切换
  • 非阻塞多路复用io

缓存穿透和缓存雪崩

缓存穿透

黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。

  • (一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
  • (二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  • (三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

缓存雪崩

  • (一)给缓存的失效时间,加上一个随机值,避免集体失效。
  • (二)使用互斥锁,但是该方案吞吐量明显下降了。
  • (三)双缓存。

双缓存

我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点

  • 从缓存A读数据库,有则直接返回
  • A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
  • 更新线程同时更新缓存A和缓存B。

如何解决redis的并发竞争key问题

参考文章

猜你喜欢

转载自blog.csdn.net/iverson2010112228/article/details/83351058