redis整理心得
1、数据类型:
1.1、String(查看具体操作命令使用 help @String)
String使用场景:
//单值缓存: SET KEY VALUE GET KEY //对象缓存 SET user:1 value(json格式数据) MSET user:1:name xifeng user:1:age 18 MGET user:1:name user:1:age //分布式锁 SETNX product:1000 true //返回1,加锁成功 SETNX product:1000 true //再次加锁返回0,加锁失败 DEL product:1000 //释放锁 SET product:10001 true ex 10 nx //防止程序意外终止导致死锁 //计数器 INCR article:readcount:{文章ID} //订阅数加1 GET article:readcount:{文章ID} //获取订阅数 //web集群session共享 spring session+redis //redis实现session共享 //分布式系统获取序列号(比如订单ID) INCRBY ORDERID 1000 //一次性获取1000序列号
1.2、List(查看具体操作命令使用help @List)
LPUSH key value [value ...] //将一个或多个值value插入到key列表的表头(最左边) RPUSH key value [value ...] //将一个或多个值value插入到key列表的表尾(最右边) LPOP key //移除并返回key列表的头元素 RPOP key //移除并返回key列表的尾元素 LRANGE key start stop //返回列表key中指定区间内的元素,区间以偏移量start和stop指定 BLPOP key [key ...] timeout //从key列表表头弹出一个元素,若列表中没有元素,阻塞等待 timeout秒,如果timeout=0,一直阻塞等待 BRPOP key [key ...] timeout //从key列表表尾弹出一个元素,若列表中没有元素,阻塞等待 timeout秒,如果timeout=0,一直阻塞等待
使用场景:
//生成数据结构 LPUSH+LPOP //STACK栈 LPUSH+RPOP //QUEUE队列 LPUSH+BRPOP//BLOCKING MQ阻塞队列 //微博和微信公号消息流 //1、诸葛老师关注了Mactalk,备胎说车等 //2、MacTalk发微博,消息ID为10018 LPUSH MSG:{诸葛老师ID} 10018 //3、备胎说车发微博,消息ID为10086 LPUSH MSG:{诸葛老师ID} 10086 //4、查看最新消息 LRANGE MSG:{诸葛老师ID} LREVRANGE MSG:51668 0 4
1.3、Hash(查看具体操作命令使用help @hash)
优点:1、同类数据归类整合存储,方便数据管理2、相比string操作消耗内存和cpu更小3、相比string储存更节省空间。
缺点:过期功能不能使用在field上,只能用在key上2、redis集群架构下不适合大规模使用
HSET key field value //存储一个哈希表key的键值 HSETNX key field value //存储一个不存在的哈希表key的键值 HMSET key field value [field value ...] //在一个哈希表key中存储多个键值对 HGET key field //获取哈希表key对应的field键值 HMGET key field [field ...] //批量获取哈希表key中多个field键值 HDEL key field [field ...] //删除哈希表key中的field键值 HLEN key //返回哈希表key中field的数量 HGETALL key //返回哈希表key中所有的键值 HINCRBY key field increment //为哈希表key中field键的值加上增量increment
Hash使用场景
//存储对象 HMSET user {userid}:name xifeng {userid}:age 18 HMSET user 1000:name xifeng 1000:age 18 HMGET user 1000:name 1000:age //购物车增加商品 //以用户id为key,用户商品为field,商品数量为value //添加商品 HSET cart:1000 10088 1 //商品增加数量 hincrby cart:1000 10088 2 //购物车商品总数 hlen cart:1000 //删除商品 hdel cart:1000 10088 //获取购物车所有商品 hgetall cart:1000
1.4、Set(查看具体操作命令使用help @set)
//set常用命令 SADD key member [member ...] //往集合key中存入元素,元素存在则忽略,若key不存在则新建 SREM key member [member ...] //从集合key中删除元素 SMEMBERS key //获取集合key中所有元素 SCARD key //获取集合key的元素个数 SISMEMBER key member //判断member元素是否存在于集合key中 SRANDMEMBER key [count] //从集合key中选出count个元素,元素不从key中删除 SPOP key [count] //从集合key中选出count个元素,元素从key中删除 //多个set之间的运算操作 SINTER key [key ...] //交集运算 SINTERSTORE destination key [key ..] //将交集结果存入新集合destination中 SUNION key [key ..] //并集运算 SUNIONSTORE destination key [key ...] //将并集结果存入新集合destination中 SDIFF key [key ...] //差集运算 SDIFFSTORE destination key [key ...] //将差集结果存入新集合destination中
应用场景:
//1、zhuge关注的人 zhugeSet-> {guojia, xushu} //2、yangguo关注的人 yangguoSet--> {zhuge, baiqi, guojia, xushu} //3、guojia关注的人 guojiaSet-> {zhuge, yangguo, baiqi, xushu, xunyu) //4、zhuge关注的人也关注他(yangguo): SISMEMBER guojiaSet yangguo//判断yangguo在guojiaSet是否存在 SISMEMBER xushuSet yangguo //5、zhuge和yangguo老师共同关注: SINTER zhugeSet yangguoSet //6、如我进入到yangguo老师的页面,推荐zhuge可能认识的人 SDIFF yangguoSet zhugeSet
SADD brand:huawei P40 SADD brand:xiaomi mi-10 SADD brand:iPhone iphone12 SADD os:android P40 mi-10 SADD cpu:brand:intel P40 mi-10 SADD ram:8G P40 mi-10 iphone12 //筛选最终符合条件的手机 SINTER os:android cpu:brand:intel ram:8G {P40,mi-10}
1.5、zSet(查看具体操作命令使用help @sorted_set)
//ZSet常用操作 ZADD key score member [[score member]…] //往有序集合key中加入带分值元素 ZREM key member [member …] //从有序集合key中删除元素 ZSCORE key member //返回有序集合key中元素member的分值 ZINCRBY key increment member //为有序集合key中元素member的分值加上increment ZCARD key //返回有序集合key中元素个数 ZRANGE key start stop [WITHSCORES] //正序获取有序集合key从start下标到stop下标的元素 ZREVRANGE key start stop [WITHSCORES] //倒序获取有序集合key从start下标到stop下标的元素 //Zset集合操作 ZUNIONSTORE destkey numkeys key [key ...] //并集计算 ZINTERSTORE destkey numkeys key [key …] //交集计算
应用场景:
//新闻访问次数 ZINCRBY hotNews:20190819 1 守护香港 //展示当日排行前十,并且带点击量 ZREVRANGE hotNews:20190819 0 9 WITHSCORES //七日搜索榜单计算 ZUNIONSTORE hotNews:20190813-20190819 7 hotNews:20190813 hotNews:20190814... hotNews:20190819 //展示七日排行前十 ZREVRANGE hotNews:20190813-20190819 0 9 WITHSCORES
1.6、geo
1.7、HyperLogLog(并不是一种新的数据类型(实际还是String类型),而是一种技术算法,通过极小的内存空间完成总数的统计,存储的数据是不会重复的)
//用于向 HyperLogLog 添加元素,如果添加成功返回1 pfadd key element [element …] pfadd 08-15:u:id "u1" "u2" "u3" "u4" //用于计算一个或多个 HyperLogLog 的独立总数 pfcount key [key …] //返回结果4 pfcount 08-15:u:id //此时向08-15:u:id插入 u1、u2、u3、u90 pfadd 08-15:u:id "u1" "u2" "u3" "u90" //此时结果是5 pfcount 08-15:u:id //可以求出多个 HyperLogLog 的并集并赋值给 destkey pfmerge destkey sourcekey [sourcekey ... ]
使用场景:
1.8、bitmaps
使用场景:实现布隆过滤器(当然判断存在的时候有误差,判断数据不存在那么就一定不存在),使用布隆过滤器实现40亿qq账号数据去重(预先把40亿的账号数据放入到bitmaps中,再使用布隆过滤器筛选哪些数据可能在布隆过滤器中,单独做标记)。
1.9、管道(pipline)和lua(脚本)
脚本会将多个命令和操作当成一个命令在redis中执行,也就是该脚本在执行过程中,不会被其他命令或脚本插入、打扰,因此具备原子性,更适合处理事务。
管道也会一次性将多个命令传输到redis服务端,但在服务端执行的时候仍然是多个命令,因此有可能会被其他命令插入、打扰,因此管道并不具有原子性。
2、高并发方案
2.1、穿透:查询数据库和缓 中都不存在的数据(解决方案:1、根据访问的键值缓存空对象,实现简单,效果不好。2、布隆过滤器:代码复杂 ,效果好。)
2.2、击穿:热点数据访问,刚好缓存过期或失效,导致大量并发访问到达数据库,对于数据库是致命的(使用分布式锁,在分布式锁的前面和后面分别加上缓存,保证缓存失效时,并发访问只能到达数据库一次)
2.3、雪崩:redis挂了或大批量数据过期
解决方案:
1、redis搭建高可用集群
2、大批数据,分批错开设置过期时间,或考虑过期时间用时间戳
3、使用redisson分布式锁
4、限流
2.4、微服务读写并发情况下,数据库和缓存不一致的问题(一般为数据一致未被使用或者缓存数据刚好失效,这时候有读的操作进来,因为读和更新缓存这两个操作不具备原子性,然后在读操作更新缓存之前,写操作刚好把数据和缓存都更新了,此时读操作再把缓存更新,就会存在不一致问题)。解决方案:使用redisson的分布式读写锁机制解决。
3、分布式锁实现
原理:使用setnx()获取锁,获取失败的话等待获取(循环获取,每次休眠500ms),获取锁成功之后,先给锁设置过期时间,再执行业务,最后再设置删除锁
4、数据库和缓存不一致解决方案(具体使用哪种方案,看业务对于数据库和缓存不一致的忍受情况而定):
a、使用redisson分布式读写锁(此方案可以完全杜绝不一致问题,但是会有并发读写性能问题)
b、使用延时双删方案(当更新数据时,先删除缓存,再更新数据,延时x秒(根据业务逻辑和写入缓存时间判断延时时间),再次删除缓存),此方案可以提高并发吞吐量,但是可能还是会存在数据库和缓存不一致的情况。
c、使用阿里的canal框架解决缓存更新问题(此框架监听mysql binlog日志变化,根据binlog更新缓存数据),因为监听binlog变化也会存在网络延时,此方案也会出现数据库和缓存不一致的情况。