分布式缓存-redis实现
为什么要做分布式缓存?即应用场景
1、需要用到缓存,但是应用又分布在不同的机器上,也就是会有数据一致性的问题,分布式缓存可以解决这个问题。
2、缓存量很大,本地缓存或者单个redis之类的缓存应用无法支撑
3、提供分布式锁
什么是分布式缓存?
分布式和集群:分布式经常与集群进行区分,两者的特点都是整个系统涉及到多台服务器,但是区别在于,集群是多台服务器完成同一件事,而分布式系统是各个服务器(服务,不一定是多台机器)各司其职,共同协作完成一件事。集群在完成业务的时候不需要在各个节点间进行通信,独立完成业务,分布式系统需要通信,协作完成业务。
分布式缓存:将缓存任务分到多个节点协作完成。一般会使用memcache或者redis来做。redis-cluster 是一种分布式集群系统,也就是说他兼有集群和分布式系统的特性,每个节点负责的数据是不一样的,但是每个节点提供的功能都是一样的,都可以作为查询的入口。
ps:redis如何在多节点分布存储数据?通过slot(槽,hash槽)这个概念,集群内部通过槽来确定数据位置,如果新增节点,槽位会重新分配(可以选择全部重新分配,也可以指定)。当放入一个键值对的时候,对key使用crc16算法得到一个值,再用这个值对16384取模得到一个哈希值(槽位),将他放到槽位对应的节点上。ps:redis集群最少有3个节点,最多有16384个槽位
分布式缓存的优缺点? 参考 https://zhuanlan.zhihu.com/p/265114107
1. 支持大数据量存储,不受应用进程重启影响
分布式缓存由于是独立部署的进程,拥有自身独立的内存空间,不会受到应用进程重启的影响,在应用进程重启时,分布式缓存的数据依然存在。同时对于数据量而言,由于不需要占用应用进程的内存空间,并且一般支持以集群的方式拓展,故可以进行大数据量的数据缓存。
2. 数据集中存储,保证数据一致性
当应用进程采用集群方式部署时,集群的每个部署节点都通过一个统一的分布式缓存进行数据存取操作,故不存在本地缓存中的数据更新问题,保证了不同节点的应用进程的数据一致性问题。
3. 数据读写分离,高性能,高可用
分布式缓存一般支持数据副本机制,可以实现读写分离,故可以解决高并发场景中的数据读写性能问题。并且由于在多个缓存节点冗余存储数据,提高了缓存数据的可用性,避免某个缓存节点宕机导致数据不可用问题。
4. 数据跨网络传输,性能低于本地缓存
由于分布式缓存是独立部署的进程,并且一般都是与应用进程位于不同的机器,故需要通过网络来进行数据传输,这样相对于本地缓存的进程内部的数据读取操作,性能会较低。
分布式缓存可能会出现的问题与解决方案。
缓存一致性问题:在分布式的情况下如何保证缓存和数据库里数据一致性
缓存穿透:缓存穿透指查询一个根本不存在的数据,缓存层和存储层都不命中
缓存雪崩:
1、缓存集体过期,导致集中查询数据库
2、缓存服务器挂掉了
缓存击穿:缓存过期了,此处正好有大量并发访问此数据,此时都发现缓存中不存在数据,都去访问数据库,导致数据库崩溃
上述问题参考:https://blog.csdn.net/qq_41604383/article/details/109001171
数据倾斜问题
hot key
一个key在一段时间内被频繁访问,造成对应服务器访问压力很大造成的。
比如
- 新闻应用中的热点新闻内容;
- 活动系统中某个用户疯狂参与的活动的活动配置;
- 商城秒杀系统中,最吸引用户眼球,性价比最高的商品信息;
hot key 解决方案
1. 使用本地缓存即二级缓存
在 client 端使用本地缓存,从而降低了redis集群对hot key的访问量,但是同时带来两个问题:
1、如果对可能成为 hot key 的 key 都进行本地缓存,那么本地缓存是否会过大,从而影响应用程序本身所需的缓存开销。
2、如何保证本地缓存和redis集群数据有效期的一致性。
所以在使用本地缓存的时候需要考虑场景,适用场景是缓存的数据内容在有效期内不会变更,如果有变更的话就会产生数据不一致的问题。
2、将key进行打散处理即备份key(推荐)
将key加上随机前缀(这里不要加后缀,下有说明),让key有多个副本,因为他们的key不同所以会尽量分散到不同的redis实例或者说slot上,当然slot如果也集中在一个实例的话要手动分配一下。但是这种方案只适用于只读的key,如果要读写的话就不行了,会产生数据不一致。
ps:如果使用Twemproxy代理redis,那么根据他的分片算法,越靠前的数据权重越大,所以把随机数生成前缀比较容易区分key以便分到不同的redis实例或者slot
big key
即数据量大的 key ,由于其数据大小远大于其他key,导致某个具体存储这个 big key 的实例内存使用量远大于其他实例,造成内存不足,拖累整个集群的使用。big key 在不同业务上,通常体现为不同的数据,比如:
- 论坛中的大型持久盖楼活动;
- 聊天室系统中热门聊天室的消息列表;
big key解决方案
1、删除key 如果业务不需要了,将bigkey删除,但是注意如果直接del,由于redis是单线程,很有可能会发生阻塞。
如果是String,则直接删除
如果 key 类型为 hash、list、set、sorted set,使用 hscan 命令,每次获取部分(例如100个)field-value,再利用 hdel 删除每个 field;渐进式删除
Redis 在4.0 版本支持 lazy delete free 的模式,删除 Bigkey 不会阻塞 Redis
2、拆分key 考虑两种情况拆分
- 该对象需要每次都整存整取
可以尝试将对象分拆成几个key-value, 使用mGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;mGet可以从不同实例获取key 参考 :https://www.jianshu.com/p/22aa8efaec8f
- 该对象每次只需要存取部分数据
可以像第一种做法一样,分拆成几个key-value, 也可以将这个存储在一个hash中,每个field代表一个具体的属性,使用hget,hmget来获取部分的value,使用hset,hmset来更新部分属性
3、更换数据库 如果很多数据都是bigkey,那说明不适合使用redis存储,可以考虑MongoDB,存储长文本
如何提前发现bigkey 和 hotkey?
1. 根据业务预判
在业务开发阶段,就要对可能变成 hot key ,big key 的数据进行判断,提前处理,如秒杀商品的信息,这需要的是对产品业务的理解,对数据设计的经验。
2.事中-监控和自动处理
监控
- 在应用程序端,对每次请求 redis 的操作进行收集上报;不推荐,但是在运维资源缺少的场景下可以考虑。开发可以绕过运维搞定;
- 在proxy层,对每一个 redis 请求进行收集上报;(推荐,改动涉及少且好维护);
- 对 redis 实例使用monitor命令统计热点key(不推荐,高并发条件下会有造成redis 内存爆掉的隐患);
- 自己抓包处理,难度大,还把架构搞复杂了,不推荐
自动处理
通过监控之后,程序可以获取 big key 和 hot key,再报警的同时,程序对 big key 和 hot key 进行自动处理。或者通知程序猿利用一定的工具进行定制化处理(在程序中对特定的key 执行前面提到的解决方案)
3、事后就尽量别事后了,黄花菜都凉了

浙公网安备 33010602011771号