背景
一个专门用来提供服务的系统离不开缓存,因为每一个功能请求的链路中会去数据库获取一些常规的信息,比如这个应用id的终端信息,应用信息,应用权限信息,商户配置信息,卡信息等等,大部分的高并发场景都是读多写少,redis(写8万,读11万)这些信息写入缓存来抗高并发。有一些基本不变的信息还可以使用二级缓存如:Caffeine + Redis。
Redis基础
Redis是一个开源的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。支持多种数据结构。Redis内置了replication、Lua scripting、LRU驱动事件、事务和不同级别的持久化,并通过Redis哨兵sentienl和自动分区Cluster提供高可用性。
Redis工作线程是单线程,整个Redis是多线程(当删除的key是一个非常大的对象hash集合会造成主线程卡顿,开启一个线程异步删除)
Redis工作线程单线程块的原因是 1. Redis所有数据存储在内存中,因此所有的运算都是内存级别的;2.他的数据结构比较简单这些简单的数据结构的查找和操作的时间大部分复杂度都是o(1) 3. 避免了上下文切换,避免了I/O阻塞操作 4.使用I/O多路复用来监听多个socket连接客户端使用一个线程连接多个请求
Redis6增加多线程来提高IO读写性能,采用多路IO可以让单个线程高效处理多个连接请求。
八大数据结构的使用场景
本篇文章只讲八大数据结构的使用场景和项目用到的结构。具体的命令可参考 www.redis.cn/commands.ht…
一 String 字符串
- 分布式锁 set key value [EX seconds][PX millseconds][NX|XX]
EX:key在多少秒后过期 PX:key在多少毫秒之后过期 NX:当key不存在的时候才创建key
- 抖音点赞 incrt key
- 项目中具体是缓存系统配置VO类,缓存使用过的二维码用来判断二维码是否已被使用
二 Hash 哈希类型(Map)
- 购物车:新增商品hset shopcar:uid001 100 1、增加商品数量 hset shopcar:uid001 100 1、商品总数hlen shopcar:uid001、全部选择hgetall shopcar:uid001
- 项目中具体是缓存具体appid所具有的权限和各个应用对应的终端和健康卡实体类的缓存和mq失败重试次数等 hset(APP_METHOD_REL,appId,relList,5*60L)
三 List 链表
- 微信抢红包 微信公众号订阅消息lpush likedarticle:uid 11
四 Set 集合
- 抽奖小程序 sadd key uid保证不重复 scard key 显示多少人
- 微博共同关注的人 sinter s1 s2
五 Zset 有序集合
- 抖音热搜展示前十条 zrange hot:20211111 0 9 withscores
六 BitMap(一个字节存储8位bit,用来存二进制数据,这样能用更小的框架存储更多数据)
- setbit uid 0 1
七 Hyperloglog
- 一般用来统计UV
- 传统方式set需要保存大量用户id计算set数量,Hyperloglog占用内存是固定的只需要12KB内存,但有0.81%的容错率
八 GeoHash
-
存储地理位置相关的数据接结构
-
项目中的场景是 获取用户当前的经纬度与张贴码的经纬度的距离
-
GEODIST Sicily Palermo Catania km
缓存雪崩、穿透、击穿
雪崩
当redis主机挂了,redis全盘崩溃或者缓存中大量数据同时过期这种情况发生时导致所有请求请求打到数据库,就会产生周期性的压力波峰,造成存储层也会挂掉的情况。
解决方案
-
事前:redis集群实现高可用,实际项目中运维人员采用的是Redis Cluster
-
事中:采用二级缓存caffeine+redis+Hystrix 限流降级
-
事后:开启Redis持久化
击穿
大量的请求同时查询一个key时,这个key正好失效,就会导致大量的请求都打到数据库上(前提有击中)
解决方案
- 设置热点数据永不过期
- 采用二级缓存caffeine+redis
穿透
每次请求查询一天记录,redis和mysql发现都查询不到该记录,但是请求每次都会打到数据库上,导致数据库压力暴增
解决方案
- 项目中使用布隆过滤器来判断流水号