分布式事务锁

分布式事务锁
一、首先什么是并发? 并发是指在同一时间段多对象同时处理一条数据,并且针对于是高并发的操作,一般避免数据库压力过大,我们一般采用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,具体做法就不讲解了,需要的可以找我要做法源码。

 谢谢学习!!!

posted @ 2022-08-30 17:41  锦大大的博客呀!  阅读(245)  评论(0编辑  收藏  举报