1、加锁多点

定义:加锁多点,是通过对每个服务进程进行加锁控制的方式来实现多点部署,达到负载均衡的目的。
优势
扩展性:无限制的扩展性,扩展服务进程后,只须设定唯一的锁标签即可。
容错性:通过监控服务的死锁唤醒,可以对数据进行补偿处理。
负载均衡:通过控制批量的量,可以达到较好的负载均衡。
架构
锁的结构
| 字段 | 名称 | 类型 | |
| ServiceLockTag | 锁标签 | Guid | |
| ModifyOn | 变更时间 | DateTime |

主要步骤
批量加锁:每个服务在处理数据之前,先进行批量加锁数据,批量的多少以单个服务进程的处理能力为准,所加锁为乐观锁,这样保证多个服务并发加锁数据时,数据最终能够成功为最后的服务加锁,每个服务进程都有自己的锁标签,作为区分于其它服务进程的标识,锁标签可在配置文件中设置。
加锁查询:批量加锁后,服务进程查询对应标签的数据。
批量解锁:数据处理完成后进行解锁处理,可单条处理,也可批量处理。
死锁唤醒:对加锁超过一定时限的数据进行解锁处理,这样可防止单个服务进程异常造成数据无法处理的状况。
2、分布式锁
应用场景

秒杀场景:防止商品超卖。
表单重复提交:避免重复提交表单。
接口幂等性:确保接口幂等性。
调度任务:避免调度任务在多台机器重复执行。
缓存过期:所有请求都去加载数据库。
主要特性
互斥:同一时刻只能有一个线程获得锁。
可重入:当一个线程获取锁后,还可以再次获取这个锁,避免死锁发生。
高可用:当小部分节点挂掉后,仍然能够对外提供服务。
高性能:要做到高并发、低延迟。
支持阻塞和非阻塞:Synchronized是阻塞的,ReentrantLock.tryLock()就是非阻塞的。
支持公平锁和非公平锁:Synchronized是非公平锁,ReentrantLock(boolean fair)可以创建公平锁。
实现方式
MySQL实现分布式锁
数据库表设计
CREATE TABLEdistributed_lock (id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',resource_name varchar(200) NOT NULL DEFAULT '' COMMENT '资源名称(唯一索引)',owner varchar(200) NOT NULL DEFAULT '' COMMENT '锁持有者(机器码+线程名称)',lock_count int NOT NULL DEFAULT '0' COMMENT '加锁次数',expire_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '锁过期时间',
PRIMARY KEY (id),
UNIQUE KEYuk_resource_name (resource_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分布式锁';
锁的实现原理:将用来竞争的资源名称设置为表的唯一索引,获取锁的时候,就插入一条记录,插入成功就代表获取到锁,插入失败就代表获取锁失败,释放锁的时候,就删除这条记录。
支持阻塞和非阻塞:可以用while循环直到插入成功,不过自旋也会占用CPU。
支持可重入:使用锁持有者owner和加锁次数字段lock_count实现可重入:获取锁,次数加一,释放锁,次数减一,次数为零就删除这把锁。
支持过期时间:使用过期时间expire_time字段,通过异步任务检测避免因程序异常或机器宕机导致锁无法释放。
支持锁续期:获取锁的同时,启动一个异步任务,每当业务执行到三分之一时间,也就是6秒中的第2秒的时候,就自动延长锁过期时间,继续延长到6秒,这样就能保证业务逻辑处理完成之前锁不会过期。
Redis实现分布式锁
获取锁并设置过期时间
// 获取锁
redis.setnx('resource_name1', 'owner1')
// 增加锁过期时间
redis.expire('resource_name1', 6, TimeUnit.SECONDS)
释放锁
// 释放锁
if ('owner1'.equals(redis.get('resource_name1'))){
redis.del('resource_name1')
}
原子操作:但是setnx和expire两条命令不是原子的,可能获取锁之后还没来得及设置过期时间就宕机了,所以可以使用Redis 2.6.12之后提供的一条复合命令:
redis.set('resource_name1', 'owner1',"NX" "EX", 6)
释放锁判断锁的持有者:释放锁时判断锁的持有者,可以避免把其他线程持有的锁给掉了,但是get和del两条命令不是原子操作,需要引入Lua脚本把两条命令打包成一条发给Redis执行:
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redis.eval(script, Collections.singletonList('resource_name1'), Collections.singletonList('owner1'))
锁续期功能:可以使用Redis客户端的Redisson的WatchDog功能,在我们调用lock自动唤醒WatchDog。
Zookeeper实现分布式锁
节点类型:zookeeper采用树形节点,类似Linux目录文件结构,同一目录下的节点名称不能重复,节点有分为四种类型:持久节点、临时节点、持久顺序节点、临时顺序节点。
监听通知机制:zookeeper还有个监听-通知机制,客户端可以在资源节点上创建watch事件,当节点发生变化,会通知客户端,客户端可以根据变化做相应的业务处理。
利用临时顺序节点创建分布式锁:由于创建的临时节点,断开连接后自动删除,所以无需设置锁超时时间,也就不用考虑不释放和锁续期。
实现逻辑:在资源/resource1目录下创建临时顺序节点node;获取/resource1目录下的所有节点,如果当前节点序号最小,代表加锁成功;如果不是,就watch监听序号最小的节点。
负载均衡下的锁机制主要包括加锁多点和分布式锁两种实现方式,加锁多点通过对每个服务进程进行加锁控制来实现多点部署和负载均衡,具有扩展性、容错性和负载均衡的优势,分布式锁则用于解决多台机器之间的资源竞争问题,具有互斥、可重入、高可用、高性能等特性,分布式锁可以通过MySQL、Redis和Zookeeper等多种方式实现,每种方式都有其特点和适用场景。
以上就是关于“负载均衡下的锁机制”的问题,朋友们可以点击主页了解更多内容,希望可以够帮助大家!
【版权声明】:本站所有内容均来自网络,若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!