Redis应用场景
特性:
- 所有的命令过来之后,Redis会放入一个任务队列中,挨个执行,所以Redis天生线程安全(NIO实现单线程速度优化)
- 访问速度快,Redis单服务器的并发大概是几万(普通服务器)
string
- 单值缓存
- 商品库存 商品ID做为key,库存数量作为value
- 对象缓存
- 仍然使用set的方式,将对象的主键提出来作为key,将对象转换为json格式作为value
- mset 批量进行存储操作,将对象的主键和对象的某一个属性比如user:1:name作为key,将对象改属性的值作为value
- 缺点:set的操作会复杂一点,从直觉上没有json的方式异动
- 有点:取用和修改的时候更灵活
- 分布式锁
- 场景:有两个服务器中运行着减库存的方法,该方法的结构
- 查询库存
- 减库存
- 把减完的数据存储回数据库
- 实现:使用setnx命令
- setnx命令相对于set命令会多一个判断逻辑,判断要设置的key在Redis中目前是否存在,如果不存在则将key和value进行正常的保存操作并返回1,如果已经存在不会对value进行更新并返回0
- 根据setnx的特性,在上面的流程中,在查询库存之前往Redis里面写入
1
2<!--将商品的ID拼接到key中-->
setnx pro-1 true - 在数据成功存储到数据库之后,将商品对应的key进行删除
1
del pro-1
- 这样多个线程想要在某个线程在对商品库存进行修改的过程中再去对商品库存信息进行读取修改的时候,Redis会返回0,实现分布式锁,当然没拿到锁的线程就自己去指定策略
- 场景:有两个服务器中运行着减库存的方法,该方法的结构
- 原子加减
- 场景:点赞计数
- 问题:如果使用数据库要考虑并发的处理
- 命令:incr会固定的将key对应的值+1
1
2
3
4<!--初始化数据为100,这一步可以不做-->
set user-1-like 100
<!--进行数据累加,如果key原本不存在,会默认初始化为1,该命令会返回累加之后的value-->
incr user-1-like - 这个数据需要同步到数据库吗,是什么样的机制?
- web集群的session共享
- 分布式系统全局序列号
- 大型系统中对MySQL进行分库分表,这个时候用mysql默认的自增主键的时候可能会出现重复的ID
- 使用Redis来生成ID,如果使用incr来取单个的ID的话每秒能生成的ID数量会受到Redis处理性能的限制
- 使用incrby命令,这个命令可以设置数据递增量
1
incrby userId 1000
- 通过incrby一次获取1000个可用的ID放到内存中慢慢使用即可
hash
性能比string高,空间占用比string小,缺点是如果使用了集群架构,hash的方式存储的数据会集中在一个节点上不能做分片操作
- 购物车 不用将这部分信息放到数据库,直接由Redis来维护
- 场景:典型的购物车模块,涉及到用户、商品、商品数量三个数据,以及将商品添加到购物车、将商品从购物车移除、购物车商品数量增加、购物车商品数量减少以及查看购物车信息五个业务操作
- 原理:使用redis的hash存储,redis本身是通过key-value的方式进行存储,hash就是value内容又是一个key-value,即是一个嵌套的map,结构:key1-[key2-value]
- 方案:将shopping-cart-user:username作为key1, 将pro-id:proId作为key2,将商品数量作为value
- 命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--添加购物车-->
hset shopping-cart-user:luoyang pro-id:1001 1
hset shopping-cart-user:luoyang pro-id:1002 4
hset shopping-cart-user:luoyang pro-id:1003 1
hset shopping-cart-user:luoyang pro-id:1004 2
<!--批量操作-->
hmset shopping-cart-user:luoyang pro-id:1001 1 pro-id:1002 4 pro-id:1003 1 pro-id:1004 2
<!--查看购物车信息-->
hgetall shopping-cart-user:luoyang
<!--将商品从购物车移除-->
hdel shopping-cart-user:luoyang pro-id:1004 2
<!--购物车商品数量增加-->
hincrby shopping-cart-user:luoyang pro-id:1003 1
<!--购物车商品数量减少-->
hincrby shopping-cart-user:luoyang pro-id:1003 -1 - 和数据库实现对比:
- 数据库实现需要建表、如果有涉及到表字段的增减需要对ORM映射流程的源代码进行修改、数据库中收藏表和订单详情表结构存在很大程度的内容重复冗余;效率上:高并发的场景对数据库不友好;
- Redis实现只需要针对购物车流程中的核心数据进行存储,没有数据库表创建、没有dbschema备份执行、不用考虑字段类型优化、不用考虑索引创建、没有ORM映射文件生成,没有没有都没有,操作简单灵活(当然这一切都建立在数据库已经做了完善的基础信息维护之上,可以认为是一个更轻量级的场景解决方案,数据库只要存储一些像订单结果之类的改动频率低的数据即可)
list
公众号消息推送
- 场景是微信公众号的消息推送,假定一个用户关注了10个公众号,公众号会不定时的发送消息,在用户打开公众号消息列表的时候,需要将公众号消息按发布时间进行展示
- 原理:使用redis的list存储,list是一个可以选择从左侧或者从右侧插入和获取数据的存储结构
- 方案:将msg:username作为key,将消息ID作为value
- 命令:
1
2
3
4
5
6
7<!--公众号发布消息的时候,将消息推送到粉丝的消息列表(从左侧插入数据)-->
lpush msg:luoyang 1001
lpush msg:luoyang 1002
lpush msg:luoyang 1003
lpush msg:luoyang 1004
<!--用户打开界面查看消息时,取最新的三条消息进行展示(从左侧获取数据)-->
lrange msg:luoyang 0 3
聊天功能实现
- 用hash存储某个用户拥有的消息队列以及其是否被激活:key1-[key2-value]
- 用户只有一个消息队列:msg-list:username&customerServiceName,还需要以string的方式记录用户消息队列的名称:(当用户打开客服咨询框的时候每秒钟从消息队列中刷新数据到页面)
- key:chat-string-for:username
- value:msg-list:username&customerServiceName
- 客服需要维护两个set集合分别表示有新信息队列和没有信息的消息队列:(打开消息查看界面之后加载chat-set-new中所有消息队列数据,查看过的消息队列放到chat-set-old中去,并每秒钟都查看chat-set-new中所有消息队列的消息)
- key: chat-set-new:customerServiceName chat-set-old:customerServiceName
- value: msg-list:username&customerServiceName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--luoyang联系客服xiaona-->
lpush msg-list:luoyang&xiaona luoyang:nihao
set chat-string-for:luoyang msg-list:luoyang&xiaona
sadd chat-set-new:xiaona msg-list:luoyang&xiaona
<!--luoyang固定每秒遍历自己的消息队列,取最新的10条消息,上拉刷新的时候可以加载更多-->
lrange msg-list:luoyang&xiaona 0 9
<!--客服xiaona每5秒像服务器发起一次请求,服务器遍历chat-set-new:xiaona的值将由新消息的对话返回给前端;
当客服在前端点击对应的对话框的时候,再获取对应消息队列最新的10条数据,并将这个消息队列放到chat-set-old:xiaona中-->
smembers chat-set-new:xiaona
<!--xiaona点击某个对话框的时候,去获取最后10条消息-->
lrange msg-list:luoyang&xiaona 0 9
<!--这里new往old里面可以使用move,但是old往new里面移动要使用删除和添加,不确定old里面是否有对应的key-->
smove chat-set-new:xiaona chat-set-old:xiaona msg-list:luoyang&xiaona
- 用户只有一个消息队列:msg-list:username&customerServiceName,还需要以string的方式记录用户消息队列的名称:(当用户打开客服咨询框的时候每秒钟从消息队列中刷新数据到页面)
- 用hash存储某个用户拥有的消息队列以及其是否被激活:key1-[key2-value]
