分布式分段锁解决数据竞争问题

一、介绍

分布式分段锁是在分布式环境下,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

在数据存储层面对单表订单分片,集群中使用分段锁技术路由至指定订单分片,各线程按顺序轮询负载均衡策略获取分段锁,

支持集群,多线程,各台机器可以在各分片获取订单,以达到数据被公平性竞争,重量级锁,集群机器数<=分片数,以免造成一直获取不到数据的极端情况

二、具体方案

分段锁集群架构图

锁获取流程图

锁和数据分片简单映射关系

1.锁中心

这里使用redis中的命令set(key,value,'NX','EX',seconds)实现锁机制,用来在分布式环境中获取锁

key:唯一对应db中一区间,分段锁

value:分片编号,例如shard=1

NX:setnx命令

EX:单位秒

seconds:秒数值

2.集群

应用服务器(rpmatch)集群,集群数量<=provider中分区数量,可以暂时定为2台机器

  • 缓存

在配置中的信息使用CopyOnWriteArrayList集合,存储各分片的分段锁,

分段锁,即segment,例如:rpmatch:cluster:order:segment:0表示订单表中分片shard=0的订单锁,rpmatch:cluster:order:segment:1表示订单表中区间shard=1的订单锁,共计两把锁

缓存数据:分片是否有数据,所有分片是否有数据,

偏移量:本地缓存保存上一次竞争锁的偏移量,

  • 获取锁

rpmatch循环获取集合的分段锁信息, 从本地拿到锁令牌,按次序去锁中心获取锁,默认从偏移量0开始

情况一:假如未获取到锁,则判断下一把锁,redis命令rt为0.02ms左右,自旋1ms,假如都没有获取到数据和锁则进入休眠,休眠2s,

在过程中记录线程上次获取锁偏移量和锁对应的分片是否有数据,假如不存在数据则标记为false,那么线程循环时要先判断它是否为false,假如为false则不需要轮询它,false过期时间为2s

情况二:假如获取锁异常或者锁中心(redis)宕机,则把债权抛入等待队列,此时源队列也无法消费新标或者债转,此情况需要做异常监控

3.provider

订单提供者,订单表增加shard字段,把订单数据切8个分片,分片编号0,1...8;

  • 先手工初始化分片数据
  • 在插入源头需要孙伟那边把shard值插入
  • 可以动态调整shard值

4.配置中心

目前使用fiona来做配置,存储着锁等信息,shard数量发生变化,则可以通过配置来修改

原有代码需要改造的有,等待队列需要全局控制消费,避免数据竞争

问题:

1:对债转而言,在区间数据内,获取到的订单并非是最精准的,后续通过改进匹配规则可以避免

发布

1.fiona配置

[{
"segmentLock": "rpmatch:cluster:order:segment:0",
"shardIdList": [0, 1, 2, 3]
}, {
"segmentLock": "rpmatch:cluster:order:segment:1",
"shardIdList": [4, 5, 6, 7]
}]

2.db

db数据统计

总数:2719749

Mod%8=1   339975

Mod%8=2.  339978

Mod%8=3   339977

Mod%8=4   339985 

Mod%8=5   339978

Mod%8=6   339979

Mod%8=7   339983

Mod%8=0   339977

增加字段

alter table T_RpPlan_OrderAccount add COLUMN ShardId int not null default 0 comment '单表逻辑分片id';

更新表数据

update T_RpPlan_OrderAccount set shardid=1 where autoid%8=1;
update T_RpPlan_OrderAccount set shardid=2 where autoid%8=2;
update T_RpPlan_OrderAccount set shardid=3 where autoid%8=3;
update T_RpPlan_OrderAccount set shardid=4 where autoid%8=4;
update T_RpPlan_OrderAccount set shardid=5 where autoid%8=5;
update T_RpPlan_OrderAccount set shardid=6 where autoid%8=6;
update T_RpPlan_OrderAccount set shardid=7 where autoid%8=7;

猜你喜欢

转载自www.cnblogs.com/javaConcurrent/p/9195938.html
今日推荐