分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)(下)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)

分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)(上):https://developer.aliyun.com/article/1420037


分布式锁解决方案_Zookeeper分布式锁原理



公平锁和可重入锁的原理


这种排队取水模型,就是一种锁的模型。


什么是可重入锁呢?


创建临时顺序节点

[zk: localhost:2181(CONNECTED) 1] create -e  -s
/test 123


ZK分布式锁的实现原理


当第一个客户端请求过来时,Zookeeper 客户端会创建一个持久节 点 locks。如果它(Client1)想获得锁,需要在 locks 节点下创建 一个顺序节点 lock1。


接着,客户端 Client1 会查找 locks 下面的所有临时顺序子节点,判 断自己的节点 lock1 是不是排序最小的那一个,如果是,则成功获得锁。


这时候如果又来一个客户端 client2 前来尝试获得锁,它会在 locks 下再创建一个临时节点 lock2。


客户端 client2 一样也会查找 locks 下面的所有临时顺序子节点,判 断自己的节点 lock2 是不是最小的,此时,发现 lock1 才是最小 的,于是获取锁失败。获取锁失败,它是不会甘心的,client2 向它 排序靠前的节点 lock1 注册 Watcher 事件,用来监听 lock1 是否存 在,也就是说 client2 抢锁失败进入等待状态。


此时,如果再来一个客户端Client3来尝试获取锁,它会在 locks 下 再创建一个临时节点 lock3。


同样的,client3 一样也会查找 locks 下面的所有临时顺序子节点, 判断自己的节点 lock3 是不是最小的,发现自己不是最小的,就获 取锁失败。它也是不会甘心的,它会向在它前面的节点 lock2 注册 Watcher 事件,以监听 lock2 节点是否存在。


释放锁


如果是任务完成,Client1 会显式调用删除 lock1 的指令。


如果是客户端故障了,根据临时节点得特性,lock1 是会自动删除的。


lock1 节点被删除后,Client2 可开心了,因为它一直监听着 lock1。lock1 节点删除,Client2 立刻收到通知,也会查找 locks 下面的所有临时顺序子节点,发下 lock2 是最小,就获得锁。


同理,Client2 获得锁之后,Client3 也对它虎视眈眈:


分布式锁解决方案_基于Zookeeper实现分布式锁



简介


Apache Curator是一个比较完善的ZooKeeper客户端框架,通过封 装的一套高级API 简化了ZooKeeper的操作。


引入Curator依赖

       <dependency>
         <groupId>org.apache.curator</groupId>
          <artifactId>curator-framework</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-client</artifactId>
            <version>5.2.0</version>
        </dependency>


编写Zookeeper配置

@Configuration
public class ZookeeperConfig {
    @Bean
    public CuratorFramework zookeeperClient() {
        CuratorFramework client = CuratorFrameworkFactory.builder()
               .connectString("127.0.0.1:2181")
               .sessionTimeoutMs(5000)
               .connectionTimeoutMs(5000)
               .retryPolicy(new ExponentialBackoffRetry(1000, 3))
               //.namespace("test")
               .build();
        client.start();
        return client;
   }
}


编写创建订单接口实现


使用InterProcessMutex的acquire和release方法,来获取和释放锁。

@Autowired
    CuratorFramework client;
    @Override
    public String createOrderZookeeper(Integer productId, Integer count) throws Exception 
     {
        // client cruator中zk客户端对象   path 抢锁路径同一个锁path需要一致
        InterProcessMutex lock = new InterProcessMutex(client, "/lockPath");
        //第一个属性:定时的时间数字
        //第二个属性:定义时间的单位
        if (lock.acquire(3, TimeUnit.SECONDS))
          {
            try {
                // 1、根据商品id查询商品信息
                Product product = productMapper.selectById(productId);
                // 2、判断商品是否存在
                if (product == null) {
            throw new RuntimeException("购买商品不存在:" + productId + "不存在");
               }
                // 3、校验库存
                if (count > product.getCount())
                 {
                    throw new RuntimeException("商品" + productId + "仅剩" +
product.getCount() + "件,无法购买");
               }
                // 4、计算库存
                Integer leftCount = product.getCount() - count;
                // 5、更新库存
                product.setCount(leftCount);
                productMapper.updateById(product);
                // 6、 创建订单
                TOrder order = new TOrder();
                order.setOrderStatus(1);//待处理
                order.setReceiverName("张三");
                order.setReceiverMobile("18587781068");
                order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));//订单价格
                baseMapper.insert(order);
                // 7、 创建订单和商品关系数据
                OrderItem orderItem = new OrderItem();
                orderItem.setOrderId(order.getId());
                orderItem.setProduceId(product.getId());
                orderItem.setPurchasePrice(product.getPrice());
                orderItem.setPurchaseNum(count);
                orderItemMapper.insert(orderItem);
                return order.getId();
           } finally {
                lock.release();
           }
       }
        return "创建失败";
   }


三种分布式锁对比



数据库分布式锁实现


优点:简单,使用方便,不需要引入 Redis、Zookeeper 等中间件。

缺点:1、不适合高并发的场景 2、db 操作性能较差


Redis 分布式锁实现


优点:1、性能好,适合高并发场景 2、较轻量级 3、有较好的框架支持,如 Redisson

缺点:1、过期时间不好控制 2、需要考虑锁被别的线程误删场景


Zookeeper 分布式锁实现


优点:1、有较好的性能和可靠性 2、有封装较好的框架,如 Curator

缺点:1、性能不如 Redis 实现的分布式锁  2、比较重的分布式锁。


汇总对比


1、从性能角度:Redis > Zookeeper >= 数据库

2、从实现的复杂性角度:Zookeeper > Redis > 数据库

3、从可靠性角度:Zookeeper > Redis > 数据库


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
73 2
基于Redis的高可用分布式锁——RedLock
|
2月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
5天前
|
NoSQL 安全 关系型数据库
20)用 Redis 实现分布式锁
20)用 Redis 实现分布式锁
15 0
|
2月前
|
NoSQL Go Redis
用 Go + Redis 实现分布式锁
用 Go + Redis 实现分布式锁
|
2月前
|
SQL 存储 关系型数据库
如何系统地学习数据库?
如何系统地学习数据库?【8月更文挑战第25天】
36 0
|
2月前
|
存储 算法 数据库
带你学习DM数据库的基本操作
带你学习DM数据库的基本操作
155 0
|
18天前
|
NoSQL 关系型数据库 MySQL
微服务架构下的数据库选择:MySQL、PostgreSQL 还是 NoSQL?
在微服务架构中,数据库的选择至关重要。不同类型的数据库适用于不同的需求和场景。在本文章中,我们将深入探讨传统的关系型数据库(如 MySQL 和 PostgreSQL)与现代 NoSQL 数据库的优劣势,并分析在微服务架构下的最佳实践。
|
20天前
|
存储 SQL 关系型数据库
使用MySQL Workbench进行数据库备份
【9月更文挑战第13天】以下是使用MySQL Workbench进行数据库备份的步骤:启动软件后,通过“Database”菜单中的“管理连接”选项配置并选择要备份的数据库。随后,选择“数据导出”,确认导出的数据库及格式(推荐SQL格式),设置存储路径,点击“开始导出”。完成后,可在指定路径找到备份文件,建议定期备份并存储于安全位置。
160 11
|
16天前
|
存储 SQL 关系型数据库
MySQL的安装&数据库的简单操作
本文介绍了数据库的基本概念及MySQL的安装配置。首先解释了数据库、数据库管理系统和SQL的概念,接着详细描述了MySQL的安装步骤及其全局配置文件my.ini的调整方法。文章还介绍了如何启动MySQL服务,包括配置环境变量和使用命令行的方法。最后,详细说明了数据库的各种操作,如创建、选择和删除数据库的SQL语句,并提供了实际操作示例。
58 13
MySQL的安装&数据库的简单操作
|
21天前
|
存储 SQL 关系型数据库
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
MySQL如何进行分库分表、数据迁移?从相关概念、使用场景、拆分方式、分表字段选择、数据一致性校验等角度阐述MySQL数据库的分库分表方案。
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
下一篇
无影云桌面