redis应用场景

背景

主要讲实际工作中的应用场景,不讲废话和八股文。

以下几个应用场景,基本上是平时最常用的应用场景。

这些应用场景的共同点,都是高性能。

缓存中间件,出生的时候,就是为了提高性能,生来就是为了解决高性能。即使不是明显的高性能,也是和高性能有关系。

具体的系统是,支付系统。

缓存

主要是查询订单表数据,比如查询订单的状态是否支付成功。

如果有更新,那么就删除缓存。从而确保数据一致性。

如果万一偶尔有数据不一致的情况,查询订单服务也会重试。说白了,就是外部调用订单查询服务的时候,第一次订单状态还没有支付成功,第二次再次查询订单,基本上就成功了。当然,这是最坏的情况,实际上很少发生这种情况,因为更新数据,也就是影响一瞬间的数据一致性。

另外,就算是影响了一瞬间的数据一致性,业务上也是要重试的。你第一次没查成功,大不了再查一次呗。极端情况,多查一次,也没啥。所以,业务上,也允许一瞬间的数据不一致。


key/value是什么?订单号/订单对象。

分布式锁

解决重复支付和幂等支付的时候,主要有两个思想:

1、先查后改

同一笔订单,一般通过先查询后修改,来避免创建多笔订单。

2、锁

一般情况下,是不用加锁的。什么叫一般情况?就是非高并发的情况。如果是高并发,其实这个也是极端情况,一般不会发生,但是万一发生了,就会导致数据不一致,所以金融系统必须要解决高并发的问题,那怎么解决?加锁。具体来说,是分布式锁。本质是,在任何时候,如果是同一笔订单,都只能有一个线程修改数据。


最后,总结就是:一锁,二查,三改。


key/value是什么?订单号/订单号,即可。数据量越小越好,value不需要是订单对象,因为只需要按订单号字段获取锁即可。

重复支付

一锁二查三改,主要是解决同一笔订单的高并发问题。

那万一确实产生了多笔订单,怎么办?比如,确实点击了两次,然后支付了两次?一般前端会置灰,就可以解决。但是假设前端没有控制住,或者人为伪造了多次支付,那怎么办?网关入口可以基于token机制,比如扫码的时候写token/订单对象 到redis,并且返回token到前端,然后真正支付的时候,即再次请求的时候,再把token带到后端来,这样后端获取到的订单,始终是同一笔订单,哪怕多次点击。

那缓存数据存储多久?几分钟,比如五分钟。五分钟内,如果多次提交/支付,从缓存获取到的,始终都是同一笔订单。五分钟之后,还在提交,缓存数据已经失效,说明订单已经过期,这个时候可以提示订单已经过期,请重新扫码支付。


token/订单对象,第一次写的时候,是否需要创建订单号?

需要。

为什么?因为如果没有订单号,缓存只是可以解决订单五分钟过期不让支付的问题,但是还是没有解决重复支付的问题,所以在扫描的时候,必须一开始就要创建订单号,而不是等到真正支付的时候,创建订单号,如果是后者,那就是创建了新的订单号,最终还是多次支付。

小结

所以,由此可见,这里其实细分的话,是有两个不同的问题,一个是幂等问题,一个是重复支付问题,二者不是同一个问题。

幂等问题,针对的是同一笔订单,即同一个订单号。一锁二查三改,要解决的也是幂等问题,即多线程并发修改同一笔订单的时候,不能出现篡改数据的情况。说白了,就是确保同一笔订单的数据一致性。

而,重复支付,本质是避免多次支付。怎么避免?不小心多次点击支付的时候,从缓存获取到的是同一笔订单,这样就解决了。


理论是一回事,实战的话,一般也不需要那么的严格,一般情况下,只需要做到一锁二查三改,就可以解决幂等问题。

如果并发不高,数据一致性要求不那么高,比如非金融场景,先查后改,就可以99%的解决篡改数据的问题。再加上锁,就可以100%的解决高并发篡改数据的问题。

所以,幂等,只需要一锁二查三改,就可以100%解决。

至于剩下的多次支付的问题,实际上也基本上不会发生,因为前端已经加了按钮置灰。就算置灰按钮没有控制住,用户后面还要输入密码------如果输入两次密码,用户就会警惕是不是多次支付。就算万一没控制住,偶尔多创建一笔订单,也是支付公司多收了一笔钱,大不了退款。总之,实际情况是,后端如果没有解决重复支付的问题,基本上也很少发生重复支付的问题。

分库路由

其实就是自己实现了一套分库分表的方案,具体来说,就是:订单号/分库。

举例,订单号1在1库,那么存储到redis的key/value就是:订单号1/db01。订单号2在2库,那么存储到redis的key/value就是:订单号2/db02。

说白了,就是分库的存储层,是基于redis。说的更细一点,其实就是简单的key/value,而且还是字符串,也没有用什么复杂的数据结构。


另外,支付系统包含了多个订单号:比如商户订单号,公司内部订单号,渠道订单号。

那怎么办?多个key/value。每个维度字段,就新增一个key/value。value都是分库的值


商户订单号,不能唯一确定一笔订单,而是组合字段:商户订单号 + 商户号。

这个时候,key是啥?拼接即可,比如:商户订单号:商户号。其实就是用一个分隔符:拼接组合字段。

渠道订单号,也一样,也是组合字段:渠道订单号 + 渠道编码。

存储层为什么用redis?

不用redis也可以。其实一开始用的是mongodb,后面才改为redis。

改的原因,有2个:

1、性能

mongodb耗时大概5ms左右。redis只有1ms左右。快了好几倍。

2、维护

redis中间件比mongodb中间件使用更频繁,所以肯定更熟练,这个就类似选编程语言,往往选用的人多的,和团队的人更熟悉的。

饿了么

饿了么,早期的系统,也是这几个应用场景。

当时我们使用 Reids 主要有三个用途,一是缓存,类似表和接口纬度;二是分布式锁,部分场景用来防并发写;三是餐厅流水号的生成。代码已经是好几年前的前人写的。

mp.weixin.qq.com/s/pZlFov4ir...

相关推荐
wclass-zhengge6 小时前
Redis篇(最佳实践)(持续更新迭代)
redis·缓存·bootstrap
Dylanioucn6 小时前
【分布式微服务云原生】探索Redis:数据结构的艺术与科学
数据结构·redis·分布式·缓存·中间件
Code成立6 小时前
1、深入理解Redis线程模型
数据库·redis·bootstrap
千年死缓16 小时前
go+redis基于tcp实现聊天室
redis·tcp/ip·golang
小小娥子18 小时前
Redis的基础认识与在ubuntu上的安装教程
java·数据库·redis·缓存
DieSnowK18 小时前
[Redis][集群][下]详细讲解
数据库·redis·分布式·缓存·集群·高可用·新手向
CoderJia程序员甲1 天前
重学SpringBoot3-集成Redis(一)
java·redis·缓存·springboot
speop1 天前
408笔记|随笔记录|自用|2
java·redis·笔记
王维诗里的代码i1 天前
Redis基础二(spring整合redis)
java·数据库·redis·spring
技术卷1 天前
Redis数据库与GO完结篇:redis操作总结与GO使用redis
数据库·redis·golang