这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记。
前言
redis真不错啊,面试官喜欢问,github上的秒杀项目里面也少不了它的身影,八股文里缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级的问题都可以拿出来水好多篇文章了……
说到缓存二字,有些人会想起redis作为缓存使用,有些人想起的是cpu那边的多级缓存……在笔者的眼中看来,他们都有着——用空间换时间,通过在读写效率更高的介质上存储数据以换取更高的读写效率的性质。我们当然希望我们的系统能有更高的运行效率,从这个角度上,不难理解为什么我们需要引入缓存了。
传统后端系统可能产生的问题
理想的一个后端系统是,当一个请求发送到后台提供的一个接口后,需要在服务内生成对应的sql语句并在MySQL之类的数据库内执行,由数据库返回需要的数据并由后台服务返回到客户端。
这样做当然是没问题的,事实上在早期的互联网服务里,后端都是直接和数据库打交道的。但是即使是这样的模型还是有一点点问题。熟悉mysql的小朋友都知道,mysql的数据最终是要存储到硬盘内的,写入硬盘这个过程本身就要消耗不少时间。这样我们的请求的响应速度就会慢下来。
时代变了,互联网的用户的规模早已今非昔比。设想你在看bilibili的播放排行榜,每个人点开一次B站的首页,都会往B站的数据库里执行一次查询语句。
MySQL的只读性能顶天了也只有7000左右1,面对如此大的请求数量,MySQL明显有点扛不住了。可能其他的请求会被挂起在后端,更有可能的是MySQL的线程直接被Kill掉。
一旦MySQL挂掉,我们的整个服务也一起跟着完蛋了,这显然是无法接受的。
什么是Redis?
Redis是一款开源的(BSD licensed)的基于内存的Key-Value数据库。因为Redis是基于内存的,他相对传统的数据库拥有更好的读写性能。根据redis官方文档下的benchmarks测试2,在使用MacBook Air 11下运行redis benchmark,redis单机下就有着将近50w的吞吐量。这比传统的数据库厉害多了。
加入Redis后的后端系统
对于需要频繁查找的数据,我们可以写入到Redis里面,并让大量的查找请求直接导向Redis上,Redis强大的吞吐能力允许我们轻松的承载大量的查找请求,这样我们MySQL的工作压力大量降低,整个系统也就能容纳更多用户的请求了。
But……代价是什么?
引入Redis当然不是没有代价的。
与MySQL的数据不一致
关于一致性的定义简单了解一下分布式系统的CAP定理,这里不做赘述。带Redis的后端系统和MySQL事实上也成为了一个简单的分布式模型。因此Redis确实会可能出现和MySQL的数据不一致的情况。
"如果数据库需要和其他外部存储相协调,那么丢弃写入内容是极其危险的操作。例如在 GitHub 的一场事故中,一个过时的 MySQL 从库被提升为主库。数据库使用自增 ID 作为主键,因为新主库的计数器落后于老主库的计数器,所以新主库重新分配了一些已经被老主库分配掉的 ID 作为主键。这些主键也在 Redis 中使用,主键重用使得 MySQL 和 Redis 中的数据产生不一致,最后导致一些私有数据泄漏到错误的用户手中。"3
延时双删策略和设置缓存过期时间能一定程度上解决不一致的问题,除此以外也有很多的解决方案,具体交给大家自行探索了。
缓存雪崩、穿透
计算机科学只有两大难题:缓存失效和命名。 —— Phil Karlton4
Redis中的缓存过期了,原本应该导向Redis的查找请求在业务的设计下会被重新倒向数据库,如果Redis中的缓存出现了大面积过期的情况(因为缓存过期或者误操作),大量的请求会被直接导向数据库中,而一旦数据库处理不了如此数量的请求,整个系统就一起被芭比Q了。
为什么不直接用Redis作为数据库?
既然Redis那么厉害,为什么我们不直接拿Redis作为数据库呢?
由于redis是基于内存的数据库,一旦redis的线程被kill掉,里面的数据就会全部丢失!
Redis也提供了例如rdb和aof的持久化方案,但是持久化的Redis相对于MySQL之类的传统数据库,会有更高的丢失数据的风险,同时因为引入了硬盘的读写,会降低Redis本身的读写性能。例如微博中的点赞等重要性不高的数据就可以放在Redis里面进行持久化。如果保存的数据重要性比较高,那最好还是不要直接使用Redis存储数据。
除此以外,Redis是基于Key-Value的数据库,他的高效更多的是依赖内存本身的读写性能,它并不支持MySQL等传统关系型数据库所支持的联合搜索,因此在带条件的查找的性能是不如关系型数据库的。
小结
本文简单的介绍了Redis在业务系统中作为缓存的思想。使用Redis能显著的增加业务系统的吞吐量。介绍了引入Redis作为缓存系统可能会发生的问题。同时也简单讨论了使用Redis直接作为数据库的可能性。