分布式锁比较

分布式锁比较

基于数据库

1. 基于数据库表

​ 直接创建一张锁表,当要锁住某个方法或资源时,在该表中增加一条记录,想要释放锁的时候就删除这条记录。给某字段添加唯一性约束,如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执行方法体内容。

2. 基于数据库排他锁

​ 如果使用的是MySql的InnoDB引擎,在查询语句后面增加for update,数据库会在查询过程中(须通过唯一索引查询)给数据库表增加排他锁,我们可以认为获得排它锁的线程即可获得分布式锁,通过 connection.commit() 操作来释放锁。

会引入数据库单点、不可重入、无法保证一定使用行锁(部分情况下MySQL自动使用表锁而不是行锁)、排他锁长时间不提交导致占用数据库连接等问题。

3. 数据库实现分布式锁总结

优点:

​ 直接借助数据库,容易理解。

缺点:

​ 操作数据库需要一定的开销,有一定的性能问题
无失效时间、不阻塞、不可重入

1.建表
CREATE TABLE `methodLock` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `method_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_method_name` (`method_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';
2.锁方法
insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)
3.释放锁
delete from methodLock where method_name ='method_name'
基于缓存

​ 相比较于基于数据库实现分布式锁的方案来说,基于缓存来实现在性能方面会表现的更好一点。目前有很多成熟的缓存产品,包括Redis、memcached、tair等。

这里以Redis为例举出几种实现方法:

1. 基于 redis 的 setnx()、expire() 方法做分布式锁

setnx 的含义就是 SET if Not Exists,其主要有两个参数 setnx(key, value)。该方法是原子的,如果 key 不存在,则设置当前 key 成功,返回 1;如果当前 key 已经存在,则设置当前 key 失败,返回 0。

expire 设置过期时间,要注意的是 setnx 命令不能设置 key 的超时时间,只能通过 expire() 来对 key 设置。

2. 基于 redis 的 setnx()、get()、getset()方法做分布式锁

getset 这个命令主要有两个参数 getset(key,newValue),该方法是原子的,对 key 设置 newValue 这个值,并且返回 key 原来的旧值。

3. 基于 Redlock 做分布式锁

Redlock 是 Redis 的作者 antirez 给出的集群模式的 Redis 分布式锁,它基于 N 个完全独立的 Redis 节点(通常情况下 N 可以设置成 5)

4. 基于 redisson 做分布式锁

redisson 是 redis 官方的分布式锁组件,GitHub 地址:https://github.com/redisson/redisson

基于缓存实现分布式锁总结

优点:

​ 性能好

缺点:

​ 锁失效时间要取决于超时时间

1.pom.xml添加依赖
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.2</version>
</dependency>
2.添加配置文件
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient getClient(){
    Config config = new Config();
    config.useSingleServer().setAddress("redis://127.0.0.1:6379");
    RedissonClient redisson = Redisson.create(config);
    return redisson;
   }
}
3.实现分布式锁
String lockName="bookLock-"+xxx.getUserId()+xxx.getBookNo();//唯一命名
RLock lock=redissonClient.getLock(lockName);//对名称加锁
lock.tryLock(100,10, TimeUnit.SECONDS);
try{
   xxx
}catch(Exception e){
    log.error("--------出错了{}----------",e.getMessage());
}finally{
    if(lock!=null){
        lock.unlock();
    }
}
基于Zookeeper

大致思想为:每个客户端对某个方法加锁时,在 Zookeeper 上与该方法对应的指定节点的目录下,生成一个唯一的临时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个临时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题

Zookeeper实现分布式锁总结

优点:

​ 可重入,非阻塞、可释放

缺点:

​ 需要动态创建、销毁临时节点来实现锁功能
​ 需要对Zookeeper的原理有所了解

1.pom.xml添加依赖
<dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.13</version>
   		 	<!--自带日志文件会有冲突,要屏蔽一下-->
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
2.添加配置文件
@Configuration
public class ZooConfig {
    @Autowired
    private Environment env;
    @Bean
    public CuratorFramework curatorFramework(){

        CuratorFramework curatorFramework= CuratorFrameworkFactory.builder().
                connectString(env.getProperty("zk.host")).
                namespace(env.getProperty("zk.namespace")).
                retryPolicy(new RetryNTimes(5,1000)).
                build();
        curatorFramework.start();
        return curatorFramework;
    }
}
3.实现分布式锁
	//定义Zookeeper客户端CuratorFramework实例
    @Autowired
    private CuratorFramework client;
    //Zookeeper分布式锁的实现原则是由ZNode节点的创建,删除与监听器构成的
    //而ZNode节点将对应一个具体的路径-根Unix文件路径类似-需要以/开头
    private static final String pathPrefix="/zkLock/";

    public void zkLockRobBook(BookDto dto){
        InterProcessMutex lock=new InterProcessMutex(client,pathPrefix+dto.getUserId()+dto.getBookNo()+"---lock");

        try {
            if(lock.acquire(15, TimeUnit.SECONDS)){
                xxx....
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                lock.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

1、数据库实现(效率低,不推荐)**

2、redis实现(使用redission实现,但是需要考虑思索,释放问题。繁琐一些)**

3、Zookeeper实现 (使用临时节点,效率高,失效时间可以控制)

4、Spring Cloud 实现全局锁(内置的)

参考文章:
分布式锁的几种实现方式~:
利用Zookeeper实现 - 分布式锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值