一、介绍
分布式分段锁是在分布式环境下,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
在数据存储层面对单表订单分片,集群中使用分段锁技术路由至指定订单分片,各线程按顺序轮询负载均衡策略获取分段锁,
支持集群,多线程,各台机器可以在各分片获取订单,以达到数据被公平性竞争,重量级锁,集群机器数<=分片数,以免造成一直获取不到数据的极端情况
二、具体方案
分段锁集群架构图
锁获取流程图
锁和数据分片简单映射关系
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;
原文地址:https://www.cnblogs.com/javaConcurrent/p/9195938.html