zoukankan      html  css  js  c++  java
  • redis解决秒杀问题与数据倾斜

    秒杀过程:
    库存查验、库存扣减和订单处理:
    在库存查验过程:
    支撑大量高并发的库存查验请求,我们需要在这个环节使用 Redis 保存库存量,这样一来,请求可以直接从 Redis 中读取库存并进行查验。

    订单处理可以在数据库中执行,但库存扣减操作,不能交给后端数据库处理。在数据库中处理订单的原因比较简单,

    因为订单处理会涉及支付、商品出库、物流等多个关联操作,这些操作本身涉及数据库中的多张数据表,要保证处理的事务性,需要在数据库中完成。而且,订单处理时的请求压力已经不大了,数据库可以支撑这些订单处理请求。

    库存扣减操作不能在数据库执行,这是因为,一旦请求查到有库存,就意味着发送该请求的用户获得了商品的购买资格,用户就会下单了。同时,商品的库存余量也需要减少一个。如果我们把库存扣减的操作放到数据库执行,会带来两个问题。额外的开销。Redis 中保存了库存量,而库存量的最新值又是数据库在维护,所以数据库更新后,还需要和 Redis 进行同步,这个过程增加了额外的操作逻辑,也带来了额外的开销。

    可能出现下单量超过实际库存量,出现超售。由于数据库的处理速度较慢,不能及时更新库存余量,这就会导致大量库存查验的请求读取到旧的库存值,并进行下单。此时,就会出现下单数量大于实际的库存量,导致出现超售,这就不符合业务层的要求了。

    redis方法支持秒杀场景:

    支持高并发。Redis 本身高速处理请求的特性就可以支持高并发。而且,如果有多个秒杀商品,我们也可以使用切片集群,用不同的实例保存不同商品的库存,这样就避免,使用单个实例导致所有的秒杀请求都集中在一个实例上的问题了。

    保证库存查验和库存扣减原子性执行。针对这条要求,我们就可以使用 Redis 的原子操作或是分布式锁这两个功能特性来支撑了。

    原子操作: Lua 脚本

    分布式锁来保证多个客户端能互斥执行这两个操作:使用分布式锁来支撑秒杀场景的具体做法是,先让客户端向 Redis 申请分布式锁,只有拿到锁的客户端才能执行库存查验和库存扣减。

    数据倾斜:
    1:某一个实例上分布的数据特别多

    2:某个实例上数据都是热点数据,被访问的非常频繁

    数据倾斜原因:
    1:存在bigkey:业务层避免创建bigkey,把集合类型的bigkey拆分成多个小集合,分散保存

    bigkey 保存了大量集合元素(集合类型),会导致这个实例的数据量增加,内存资源消耗也相应增加。bigkey 的操作一般都会造成实例 IO 线程阻塞,如果 bigkey 的访问量比较大,就会影响到这个实例上的其它请求被处理的速度。

    2:slot手工分配不均匀:避免把较多的slot分配到一个实例上,进行槽的迁移

    进行槽的迁移命令:

    在实例5上执行此命令代表要从实例3上导入哈希槽编号为300的槽
    1:cluster setslot 300 importing 3

    在实例3上执行此命令表示实例三要将哈希槽300迁移到实例5

    2:cluster setslot 300 migrating 5

    从实例三获取哈希槽300的100个key,因为 Slot 中的 key 数量可能很多,所以我们需要在客户端上多次执行下面的这条命令

    3:cluster  getkeysinslot 300 100

    将刚才第三步获取的100个key中的key1迁移到实例5上的0号数据库上把迁移的超时时间设置为 timeout(ip为IP 为 192.168.10.5)

    4:migrate 192.168.10.5 6379 key1 0 timeout

    反复执行migrate命令,直到将100个key迁移完,在执行命令三,重新从哈希槽300中拿数据,重复3,4步直到哈希槽300中的数据

    全部迁移完。

    3:使用了hash tag,使得大量数据被分配到了一个实例上的同一个slot上:如果是hash tag造成的数据倾斜,优先避免数据倾斜,不适用hash tag

    hash tag应用场景:
    它主要是用在 Redis Cluster 和 Codis 中,支持事务操作和范围查询。因为 Redis Cluster 和 Codis 本身并不支持跨实例的事务操作和范围查询,当业务应用有这些需求时,就只能先把这些数据读取到业务层进行事务处理,或者是逐个查询每个实例,得到范围查询的结果。这样操作起来非常麻烦,所以,我们可以使用 Hash Tag 把要执行事务操作或是范围查询的数据映射到同一个实例上,这样就能很轻松地实现事务或范围查询了。

    4:存在热点数据:采用带有不同key前缀的多副本方法(

    我们把热点数据复制多份,在每一个数据副本的 key 中增加一个随机前缀,让它和其它副本数据不会被映射到同一个 Slot 中。这样一来,

    热点数据既有多个副本可以同时服务请求,同时,这些副本数据的 key 又不一样,会被映射到不同的 Slot 中。在给这些 Slot 分配实例时,

    我们也要注意把它们分配到不同的实例上,那么,热点数据的访问压力就被分散到不同的实例上了。

    热点数据多副本方法只能针对只读的热点数据。如果热点数据是有读有写的话,就不适合采用多副本方法了,因为要保证多副本间的数据一致性,会带来额外的开销。

    支持高并发。这个很简单,Redis 本身高速处理请求的特性就可以支持高并发。而且,如果有多个秒杀商品,我们也可以使用切片集群,用不同的实例保存不同商品的库存,这样就避免,使用单个实例导致所有的秒杀请求都集中在一个实例上的问题了。
    支持高并发。这个很简单,Redis 本身高速处理请求的特性就可以支持高并发。而且,如果有多个秒杀商品,我们也可以使用切片集群,用不同的实例保存不同商品的库存,这样就避免,使用单个实例导致所有的秒杀请求都集中在一个实例上的问题了。
  • 相关阅读:
    JS运行机制之 Event Loop 的思考
    模块机制 之commonJs、node模块 、AMD、CMD
    git报错:'fatal:remote origin already exists'怎么处理?附上git常用操作以及说明。
    Uncaught RangeError: Maximum call stack size exceeded-栈溢出
    对循环内部反复声明变量的写法的一点想法?
    JS的forEach和map方法的区别
    函数的属性和方法之call、apply 及bind
    利用Apach ab对nodejs进行并发负载的压力测试
    怎么判断一个对象是不是数组类型?
    Python模块学习之fabric
  • 原文地址:https://www.cnblogs.com/foreverlearnxzw/p/14001896.html
Copyright © 2011-2022 走看看