缓存 —— 热数据

热点数据(经常会被查询,但是不经常被修改或者删除的数据),首选是使用 redis 缓存,毕竟强大到冒泡的 QPS 和极强的稳定性不是所有类似工具都有的,而且相比于 memcached 还提供了丰富的数据类型可以使用,另外,内存中的数据也提供了 AOF 和 RDB 等持久化机制可以选择,要冷、热的还是忽冷忽热的都可选。

结合具体应用需要注意一下:很多人用 spring 的 AOP 来构建 redis 缓存的自动生产和清除,过程可能如下:
Select 数据库前查询 redis,有的话使用 redis 数据,放弃 select 数据库,没有的话,select 数据库,然后将数据插入 redis
update 或者 delete 数据库前,查询 redis 是否存在该数据,存在的话先删除 redis 中数据,然后再 update 或者 delete 数据库中的数据
上面这种操作,如果并发量很小的情况下基本没问题,但是高并发的情况请注意下面场景:
为了 update 先删掉了 redis 中的该数据,这时候另一个线程执行查询,发现 redis 中没有,瞬间执行了查询 SQL,并且插入到 redis 中一条数据,回到刚才那个 update 语句,这个悲催的线程压根不知道刚才那个该死的 select 线程犯了一个弥天大错!于是这个 redis 中的错误数据就永远的存在了下去,直到下一个 update 或者 delete。

上面典型的脏缓存产生原因以及解决办法,可以参考陈皓写的一篇文章 《缓存更新的套路》, 从我个人的角度来讲,除了秒杀,如果业务上达到这个并发级别,相信可以从架构层面避免这个问题。

计数器

诸如统计点击数等应用。由于单线程,可以避免并发问题,保证不会出错,而且 100% 毫秒级性能!爽。
命令:INCRBY
当然爽完了,别忘记持久化 (存到数据库或者本身 redis 持久化),毕竟是 redis 只是存了内存!

队列

相当于消息系统,ActiveMQ,RocketMQ 等工具类似,但是个人觉得简单用一下还行,如果对于数据一致性要求高的话还是用 RocketMQ 等专业系统。
由于 redis 把数据添加到队列是返回添加元素在队列的第几位,所以可以做判断用户是第几个访问这种业务
队列不仅可以把并发请求变成串行,并且还可以做队列或者栈使用

位操作(大数据处理)

用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。
想想一下腾讯 10 亿用户,要几个毫秒内查询到某个用户是否在线,你能怎么做?千万别说给每个用户建立一个 key,然后挨个记(你可以算一下需要的内存会很恐怖,而且这种类似的需求很多,腾讯光这个得多花多少钱。。。)好吧。这里要用到位操作 —— 使用 setbit、getbit、bitcount 命令。
原理是:redis 内构建一个足够长的数组,每个数组元素只能是 0 和 1 两个值,然后这个数组的下标 index 用来表示我们上面例子里面的用户 id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0 和 1)来构建一个记忆系统,上面我说的几个场景也就能够实现。用到的命令是:setbit、getbit、bitcount

分布式锁与单线程机制

验证前端的重复请求(可以自由扩展类似情况),可以通过 redis 进行过滤:每次请求将 request Ip、参数、接口等 hash 作为 key 存储 redis(幂等性请求),设置多长时间有效期,然后下次请求过来的时候先在 redis 中检索有没有这个 key,进而验证是不是一定时间内过来的重复提交
秒杀系统,基于 redis 是单线程特征,防止出现数据库 “爆破”
全局增量 ID 生成,类似 “秒杀”

最新列表

例如新闻列表页面最新的新闻列表,如果总数量很大的情况下,尽量不要使用 select a from A limit 10 这种 low 货,尝试 redis 的 LPUSH 命令构建 List,一个个顺序都塞进去就可以啦。不过万一内存清掉了咋办?也简单,查询不到存储 key 的话,用 mysql 查询并且初始化一个 List 到 redis 中就好了。

排行榜

谁得分高谁排名往上。命令:ZADD(有续集,sorted set)

其它 web 应用场景

1. 在主页中显示最新的项目列表

redis 使用的是常驻内存的缓存,速度非常快。lpush 用来插入一个内容 id,作为关键字存储在列表头部。ltrim 用来限制列表中的项目数最多为 5000。如果用户需要的检索的数据量超越这个缓存容量,这时才需要把请求发送到数据库。

3.排行榜及相关问题

排行榜(leader board)按照得分进行排序。zadd 命令可以直接实现这个功能,而 zrevrange 命令可以用来按照得分来获取前 100 名的用户,zrank 可以用来获取用户排名,非常直接而且操作容易。

4.按照用户投票和时间排序

这就像 reddit 的排行榜,得分会随着时间变化。lpush 和 ltrim 命令结合运用,把文章添加到一个列表中。一项后台任务用来获取列表,并重新计算列表的排序,zadd 命令用来按照新的顺序填充生成列表。列表可以实现非常快速的检索,即使是负载很重的站点。

5.过期项目处理

使用 unix 时间作为关键字,用来保持列表能够按时间排序。对 current_time 和 time_to_live 进行检索,完成查找过期项目的艰巨任务。另一项后台任务使用 zrange...withscores 进行查询,删除过期的条目。

Redis 与 Memcache 对比

  • 网络 IO 模型 性能对比:(占用的核数,线程数,网络模型)
  • 由于 Redis 只使用单核,而 Md,所以平均每一个核上 Redis 在存储小数据时比 Memcached 性能更 高。而在 100k 以上的数据中,Memcached 性能要高于 Redis,虽然 Redis 最近也在存储大数据的性能上进行优化,但是比起 Memcached,还是稍有逊色。

    • Memcached 是多线程,非阻塞 IO 复用的网络模型,分为监听主线程和 worker 子线程,监听线程监听网络连接,接受请求后,将连接描述字 pipe 传递给 worker 线程,进行读写 IO, 网络层使用 libevent 封装的事件库,多线程模型可以发挥多核作用,但是引入了 cache coherency 和锁的问题,比如,Memcached 最常用的 stats 命令,实际 Memcached 所有操作都要对这个全局变量加锁,进行计数等工作,带来了性能损耗。
    • Redis 使用单线程的 IO 复用网络模型,自己封装了一个简单的 AeEvent 事件处理框架,主要实现了 epoll、kqueue 和 select,对于单纯只有 IO 操作来说,单线程可以将速度优势发挥到最大,但是 Redis 也提供了一些简单的计算功能,比如排序、聚合等,对于这些操作,单线程模型实际会严重影响整体吞吐量,CPU 计算过程中,整个 IO 调度都是被阻塞住的。
    • Memcached 使用预分配的内存池的方式,使用 slab 和大小不同的 chunk 来管理内存,Item 根据大小选择合适的 chunk 存储,内存池的方式可以省去申请 / 释放内存的开销,并且能减小内存碎片产生,但这种方式也会带来一定程度上的空间浪费,并且在内存仍然有很大空间时,新的数据也可能会被剔除
    • Redis 使用现场申请内存的方式来存储数据,并且很少使用 free-list 等方式来优化内存分配,会在一定程度上存在内存碎片,Redis 跟据存储命令参数,会把带过期时间的数据单独存放在一起,并把它们称为临时数据,非临时数据是永远不会被剔除的,即便物理内存不够,导致 swap 也不会剔除任何非临时数据(但会尝试剔除部分临时数据),这点上 Redis 更适合作为存储而不是 cache。

原文作者: A_aliane
原文链接:https://learnku.com/articles/25331#29c419

部分内容有增删。

最后修改:2019 年 07 月 08 日 04 : 18 PM
打赏不打超过你工资的一半!