• 分布式事务锁


    分布式事务锁
    一、首先什么是并发? 并发是指在同一时间段多对象同时处理一条数据,并且针对于是高并发的操作,一般避免数据库压力过大,我们一般采用redis来进行处理存储。
    二、并发主要是分为以下几点:
      1. 单应用的时候:处理秒杀的活动只在一个程序中进行,解决方案如下:
      
    public static readonly object olock = new object();
    // lock 必须存储引用类型,数组,对象,特殊字符串
    lock (olock)
    {}

      2. 应用集群的环境下处理方案:

    public static readonly object olock = new object();
    Monitor.Enter(olock);
    {}
    //释放锁
    Monitor.Exit(olock);

      同时思考如下:为什么枷锁的对象需要使用引用对象?因为引用对象占四个字节,里面同时存储了其他的比如hashcode相关的指,同时会有唯一的引用地址。而值类型,里面是只有一个字节,所以不能存储太多的值。  

    3.  Redis阻塞锁

    using (var client = new RedisClient("127.0.0.1", 6379))
                {
                    // 这个就是一个阻塞锁的用法。。
                    //timeout : 锁的占有时间,这个时间不是redis里面的过期时间,就是写进去一个值。
                    //timeout : 如果拿不到锁,我需要自身轮询等待多久,,先去判断能不能拿到锁,如果拿不到,等多久,如果等多久之后,还拿不到,则直接不会执行里面的代码。。
                    // timeout 问题。。 10s ,过期时间是10s-- 2021-06-05 20:27:50 2021-06-05 20:27:60
                    // 第二个线程拿锁的时间是 2021-06-05 20:27:71
                    using (var datalock = client.AcquireLock("DataLock:" + key, timeout))
                    {
                        // 首先判断这个DataLock的key不在,说明没有人拿到锁,我就可以写一个key:DataLock,values=timeout
                        // 如果这个key存在,,判断时间过没过期,如果过期了,则我们可以拿到锁,如果没有,则进行根据你的等待时间在去轮询判断锁在不在
                        //库存数量
    
                        // 如果多个线程在判断锁的时候,发现锁没有过期,然后一直等待,,
                        // 刚好三个线程,并发同一时间,发geiredis请求,判断这个key,此时此刻,刚好,这个key不在,会不会出现,三个人都去set key ,大不了把值覆盖,但是可能这个三个线程都拿到锁。。。 
                        // AcquireLock 底层
                        // 判断这个锁在不在时候,都会查询出当前的key,还有我们的版本号
                        // 如果锁过期。然后在抢锁的过程--用事务的版本--- 带着版本号提交,如果版本号一致,则拿到锁,如果不一致,那锁失败 
                        var inventory = client.Get<int>("inventoryNum");
                        if (inventory > 0)
                        {
                            client.Set<int>("inventoryNum", inventory - 1);
                            //订单数量+1
                            var orderNum = client.Incr("orderNum");
                            Console.WriteLine($"{i}抢购成功*****线程id:{ Thread.CurrentThread.ManagedThreadId.ToString("00")},库存:{inventory},订单数量:{orderNum}");
                        }
                        else
                        {
                            Console.WriteLine($"{i}抢购失败");
    
                            Thread.Sleep(100); //20s
    
                        }
                        // todo 比如还有其他时间--  2021-06-05 20:27:61
                        //  查询mysql数据库,数据量小的时候,2s 10s
    
                        // 从redis 里面删除DataLock  回收锁 时间问题 -- 本身存在的问题。。。设计这个时间,已经经过大量测试,选择一个适合时间。。。 
                        // timeout 一个就是value。  guid---唯一标识
                        //timeout 拿不到锁轮询的时候
                        //timeout就是操作redis的时候,过期时间,让redis后台去删除这个key..
    
                        // 线程用完锁之后,带着value--如果唯一标识,你就可以删除,这个锁,不然就是别人的锁,你是删不掉的。。 redlock 就是这么设置的===
    
                    }
    
                }

    4. Redis非阻塞锁

    using (var client = new RedisClient("127.0.0.1", 6379))
                {
                    // true ,拿到锁,false 拿不到timeout 它就是操作redis的时候,设置过期时间,交给redis
                    // 就老师自己写的, 非阻塞锁。。。而且也不会出现,删除别的锁---
                    // 有一个问题,如果时间时间设置太多,,, 10s 
                    // 续命,,子线程 ,循环-- 标识-- 1  9 +13 --执行完成--改标识,就不要在去循环做续命的事情。。。
                   // 如果页面哪里真的遇到了问题,没有办法执行下去,其他客户端一直等,一直等。。。 数据,业务,那边真的出现问题,,特殊数据出现的问题。。 死锁,,少不了人工参与-- 也用的比较 
                    bool isLocked = client.Add<string>("DataLock:" + key, key, timeout);
                    if (isLocked)
                    {
                        try
                        {
                            //库存数量
                            var inventory = client.Get<int>("inventoryNum");
                            if (inventory > 0)
                            {
                                client.Set<int>("inventoryNum", inventory - 1);
                                //订单数量
                                var orderNum = client.Incr("orderNum");
                                Console.WriteLine($"{i}抢购成功*****线程id:{ Thread.CurrentThread.ManagedThreadId.ToString("00")},库存:{inventory},订单数量:{orderNum}");
                            }
                            else
                            {
                                Console.WriteLine($"{i}抢购失败:原因,没有库存");
                            }
                        }
                        catch
                        {
                            throw;
                        }
                        finally
                        {
                            // 如果没有到过期时间,而且自己也执行完了,是不是要删。。
                            //如果自己的锁的过期 12
                            client.Remove("DataLock:" + key);
                        }
                    }
    
                    else
                    {
                        Console.WriteLine($"{i}抢购失败:原因:没有拿到锁");
                    }
                }

    5. RedisRedlock(宏锁) 这种方式适用于,redis集群和应用集群的情况下,但是注意节点需要是单数的节点,比如:3、5、7,具体做法就不讲解了,需要的可以找我要做法源码。

     谢谢学习!!!

  • 相关阅读:
    [LeetCode] 34. 在排序数组中查找元素的第一个和最后一个位置
    [LeetCode] 32. 最长有效括号
    [LeetCode] 31. 下一个排列
    [LeetCode] 30. 串联所有单词的子串
    [LeetCode] 29. 两数相除
    [LeetCode] 27. 移除元素
    转:畅享云时代:开发者必备的8个最佳云端集成开发环境
    转:前端集锦:十款精心挑选的在线 CSS3 代码生成工具
    转:So Easy!让开发人员更轻松的工具和资源
    转:Backbone与Angular的比较
  • 原文地址:https://www.cnblogs.com/wangjinya/p/16640242.html
Copyright © 2020-2023  润新知