写点什么

Redis(二)分布式锁与 Redis 集群搭建

作者:神秘码农
  • 2022 年 4 月 02 日
  • 本文字数:4363 字

    阅读完需:约 14 分钟

@[toc]

一、 线程锁与分布式锁

  • 线程锁 单体项目

  • 单体项目

  • 步骤

  • 代码如下


         //定义静态全局锁         private readonly static object _lock = new object();        // 控制器中添加代码          lock (_lock)          {             Stock sto = new Stock();            sto = demoDbContext.stock.Where(p => p.ID == 1).FirstOrDefault();            if (sto.count == 0)            {                Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "---秒杀结束,无库存");                return Ok("秒杀结束,无库存");             }              Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "--秒杀成功;");              //库存减1              sto.count = sto.count - 1;              demoDbContext.SaveChanges();          }          return Ok("秒杀结束");
复制代码


- 数据库数量为10  如图:  ![在这里插入图片描述](https://img-blog.csdnimg.cn/afefbb4a19074df2b2bfe61124d8aeb2.png#pic_center)
- 用jmeter并发10个线程 如图: ![在这里插入图片描述](https://img-blog.csdnimg.cn/bdd57dccd3794492bf3c9ab3613124f3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
- 运行结果如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/74dc100990c0461d9fba106606ccc8aa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
复制代码


  • 分布式锁

  • 条件

  • 启动两个实例 5000/5001

  • Nginx

  • jmeter

  • 步骤

  • 核心代码


            public class RedisLock            {                public readonly ConnectionMultiplexer connectionMultiplexer;                private IDatabase database = null;                 public RedisLock()                 {                    connectionMultiplexer = ConnectionMultiplexer.Connect("127.0.0.1:6380");                    database = connectionMultiplexer.GetDatabase(0);                }                              /// <summary>                /// 加锁                /// </summary>                public void Lock()                 {                     while (true) //                    {                           //redis_lock 锁名称                        // Thread.CurrentThread.ManagedThreadId 线程名称                        // TimeSpan.FromSeconds(10)  设置过期时间  防止死锁                        bool flag = database.LockTake("redis_lock", Thread.CurrentThread.ManagedThreadId, TimeSpan.FromSeconds(10));                        //true :加锁成功  false:加锁失败                        if (flag)                         {                            break;                        }                        Thread.Sleep(10);//防止死锁  等待时间  释放资源。                    }                }                /// <summary>                /// 释放锁                /// </summary>                public void UnLock()                 {                    database.LockRelease("redis_lock", Thread.CurrentThread.ManagedThreadId);                    connectionMultiplexer.Close();                }            }  
复制代码


  控制器中使用
复制代码


       [HttpGet]        [Route("SubStock")]        public IActionResult SubStock()        {
RedisLock redisLock = new RedisLock(); redisLock.Lock(); Stock sto = new Stock(); sto = demoDbContext.stock.Where(p => p.ID == 1).FirstOrDefault(); if (sto.count == 0) { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "---秒杀结束,无库存"); //redisLock.UnLock(); return Ok("秒杀结束,无库存"); } Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "--秒杀成功;"); //库存减1 sto.count = sto.count - 1; demoDbContext.SaveChanges(); redisLock.UnLock();
return Ok("秒杀结束"); }
复制代码


- 运行两个实例  如图:  ![在这里插入图片描述](https://img-blog.csdnimg.cn/4e9da2eba43d474f906ca784c65d7ed4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
复制代码



- 启动Nginx  如图:  ![在这里插入图片描述](https://img-blog.csdnimg.cn/14cbc391fadf4b30b51db82c3226f0fc.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
- 数据库库存 如图: ![在这里插入图片描述](https://img-blog.csdnimg.cn/b5d57acb08204262bc003822e3c6ef36.png#pic_center)
- jmeter并发10个线程 ![在这里插入图片描述](https://img-blog.csdnimg.cn/8121d441912541a29b2debf8f7497140.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
- 运行结果如下 ![在这里插入图片描述](https://img-blog.csdnimg.cn/430ad8d8f9d24929beb93e42cf4c7317.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
复制代码


  • 分布式锁的使用场景当集群系统中修改某个字段值时使用分布式锁。

  • 分布式锁的设计思路比如并发两个进程,当第一个进程加锁后,第二个进程加锁会失败,会休眠(10 毫秒),直到第一个进程执行完业务代码并释放锁,如果第一个进程处理业务代码超过 10 毫秒,redis 的过期时间也是 10 毫秒,那么第二个进程进行加锁执行业务代码并释放锁。备注:休眠的毫秒数可根据自己业务代码定义,毫秒数最好和 redis 过期时间一致。

二、Redis 集群

  • 第一代集群 主从集群

  • 如图:


  • 缺点只有一个 master,当 maset 宕机后,整个 redis 集群系统无法使用。

  • 第二代集群 哨兵集群

  • 如图


  • 第二代集群比第一代集群多了一个 sentinel 监视的角色,当主节点宕机后,sentinel 会从多个从节点中选择一个为主节点。

  • 缺点

  • 只有一个 master,无法解决高并发写的问题。

  • 无法存储海量数据。

  • 第三代集群

  • 如图:


  • 优点与缺点

  • 优点

  • 解决高并发写。

  • 存储海量数据。

  • 缺点

  • 消耗资源比较大。

  • 实现

  • 条件

  • windows 环境

  • Redis

  • 网盘下载地址链接:https://pan.baidu.com/s/1-rdemcSLHHFSy3b03EnQsg 提取码:liiz

  • Ruby

  • 网盘下载地址链接:https://pan.baidu.com/s/1NEnVoZzzMyROdm0qNeqw0g 提取码:lf10

  • Ruby 驱动

  • 网盘下载地址链接:https://pan.baidu.com/s/1LkpTstTMenespCx4J0ZtWA 提取码:7wn6

  • 分配主从工具

  • 网盘下载地址链接:https://pan.baidu.com/s/18ah0ePiGr9XCukRsRdIiXw 提取码:0e02

  • 步骤

  • 配置集群文件 (6 个实例) 配置 6 个配置文件【并将 6 个配置拷贝到 redis 根目录下】 ==配置不能有中文注释也不行!!!!==


        port 6380                                         #端口        bind 127.0.0.1                                    #IP地址        appendonly yes                                    #数据保存格式为aof        appendfilename "appendonly.6380.aof"              #数据保存文件        cluster-enabled yes                               #是否开启集群        cluster-config-file nodes.6380.conf               #集群节点配置文件        cluster-node-timeout 15000                        #节点超时时间        cluster-slave-validity-factor 10                  #验证slaver节点次数        cluster-migration-barrier 1                       #        cluster-require-full-coverage yes                 #master节点和slaver节点之间是否全量复制
复制代码


- 执行所有实例
复制代码


      redis-server.exe redis.6380.conf       redis-server.exe redis.6381.conf       redis-server.exe redis.6382.conf       redis-server.exe redis.6383.conf       redis-server.exe redis.6384.conf       redis-server.exe redis.6385.conf 
复制代码


  如图:  ![在这里插入图片描述](https://img-blog.csdnimg.cn/7753feae85c64198b5975295f192cd5d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
复制代码







 - 安装 ruby
复制代码


         ruby --version #验证是否安装成功
复制代码


- redis-cluster 驱动安装命令
复制代码


        #进入 ruby安装目录bin文件下执行安装命令        ruby gem install --local  D:\Assembly\redis\Windows\redis-cluster\redis-3.2.2.gem  #驱动文件路径
复制代码


- 执行分配主从工具脚本
复制代码


      ruby redis-trib.rb create --replicas 1  127.0.0.1:6380  127.0.0.1:6381 127.0.0.1:6382   127.0.0.1:6383  127.0.0.1:6384  127.0.0.1:6385       #写入所有的实例地址和端口号      # --replicas 1:是否分配3主3从  一个主节点和从节点 
复制代码


  如图:  ![在这里插入图片描述](https://img-blog.csdnimg.cn/531a6e12a81e49b187e667d6a73509a5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQEDnpZ7lhpzlhpnku6PnoIE=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
- 查看是否分配成功 当6个实例不停输出日志,说明已经分配成功。
复制代码


  • redis 集群内部关系结构图如图:


  • 在 redis 集群中,每个节点都是相互通信的,用的协议是 Gossip 协议。

  • redis 集群内部数据存储原理

  • Slot 槽 主节点有槽[平均分配] 从节点没有槽 总共有 16384 个槽


        ruby redis-trib.rb check 127.0.0.1:6380 
复制代码


- Hash算法- 取模算法当客户端将数据写入节点中时,节点会将key使用hash算法取到一个固定长度数值,然后对槽总数【16384】用取模算法进行取模,得到的数值后在到各个主节点中查看数值在哪个节点的槽数数值之间,在将数据写到那个主节点中。 
复制代码


发布于: 刚刚阅读数: 2
用户头像

神秘码农

关注

还未添加个人签名 2022.03.14 加入

好好学习,天天向上!

评论

发布
暂无评论
Redis(二)分布式锁与Redis集群搭建_神秘码农_InfoQ写作平台