分布式锁总结

一、分布式锁的应用场景

è¿éåå¾çæè¿°

锁的机制主要目的是为了解决再多线程情况下的数据安全一致性。

仅有一台应用服务器的情况,例如查询某个账户余额并进行扣款,使用java自带的线程锁便可以解决问题。

  • synchronized
  • lock
  • db lock

若有多台应用服务器,逻辑分布在多个应用服务上,需要通过中间渠道来保证操作的唯一性,此时便需要采用分布式锁。我们需  要怎么样的分布式锁?

  • 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。

  • 这把锁要是一把可重入锁(避免死锁)

  • 这把锁最好是一把阻塞锁(根据业务需求考虑要不要这条)

  • 这把锁最好是一把公平锁(根据业务需求考虑要不要这条)

  • 有高可用的获取锁和释放锁功能

  • 获取锁和释放锁的性能要好


二、分布式锁的集中实现方式


1、基于数据库方式

CREATE TABLE `DistributedLock` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(64) NOT NULL DEFAULT '' COMMENT '锁名',
  `desc` varchar(1024) NOT NULL DEFAULT '备注信息',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_name` (`name`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';

1、利用索引唯一性

//数据库中的每一条记录就是一把锁,利用的mysql唯一索引的排他性
 
lock(name,desc){
    insert into DistributedLock(`name`,`desc`) values (#{name},#{desc});
}
 
unlock(name){
    delete from DistributedLock where name = #{name}
}

锁重入:可增加可重入功能(避免再次获取锁导致死锁)

增加字段进程识别信息(ip、服务名称、线程id) 与 重入计数count,如果是同一个进程同一个线程则允许重入。

 
  1. 获取:再次获取锁的同时更新count(+1).

  2. 释放:更新count-1,当count==0删除记录。

可靠性
主从mysql:mysql宕机,立刻切换。
锁的持有者挂掉:定时任务清楚持有一定时间的锁。

性能
db操作都有一定性能损耗

阻塞锁
有此需求的业务线需要使用自旋多次尝试获取锁的实现。

2、利用select … where … for update 排他锁

boolean lock(){
    connection.setAutoCommit(false)
    while(true){
        try{
            result = select ... from DistributedLock where name=lock for update;
            if(result==null){
                return true;
            }
        }catch(Exception e){
        connection.commit();
        }
        sleep(*);
    }
    return false;
}
 
void unlock(){
    connection.commit();

其他附加功能与实现一基本一致,这里需要注意的是“where name=lock ”,name字段必须要走索引,否则会锁表。有些情况下,比如表不大,mysql优化器会不走这个索引,导致锁表问题。
2、乐观锁 version

所谓乐观锁与前边最大区别在于基于CAS思想,是不具有互斥性,不会产生锁等待而消耗资源,操作过程中认为不存在并发冲突,只有update version失败后才能觉察到。我们的抢购、秒杀就是用了这种实现以防止超卖。
通过增加递增的版本号字段实现乐观锁

  1. select ...,version

  2. update table set version+1 where version=xx(备注:这里的version应该设置为version自增, 而不是account= oldAccount, 可以防止集群环境下 a+a-a的情况)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值