概述
- 我们先看一个场景,到了双11,我们的商户又开始卖商品啦
- 但是,我们的库存是有限的,如果超卖了,可能平台就会涉及相关法律责任了
- 所以,我们的库存扣除问题,一定是一个非常经典的问题
- 先看上图,我们库存服务部署在一台机器上,现在有 2个 Goroutine
- 这个库存是被并发读取的,但是读取后扣除明显是一个异步的问题
- 用 Goroutine 来模拟并发, 2个Goroutine 分别是G1, G2 都去下单,扣减库存
- 一共100件,2个Goroutine查询的时候很可能都还是100
- 在计算扣除的时候都是 100 - 10 = 90 这个肯定不对,应该最终的结果是 80
- 引发这个问题的原因是:并发查询和扣减一起操作,无法得到时时的真实的数据
- 当时查询的数据和时时的数据并不是同一个数据
- 现在,我们用分布式锁来解决这个问题
- 分布式锁好比一个独立的第三方,是可信任的,从计算机角度来说,这是一把锁
- 我们约定谁拿到了这把锁就可以扣减库存,查询库存
- 在查询之前先获得锁,没有锁你连查询都不能查询,就可以保证顺序执行
- 抢到了锁,执行完自己的操作,就要释放锁
分布式锁的发展
- 上面蓝色的圈代表2个 Goroutine
- 现在的场景是在一个服务器上,在服务器上会有一些服务,其中2个服务都有 Goroutine
- 我们的这个锁是操作系统提供的,它针对的是操作系统的这把锁
- 这是一开始go语言当中的这一部分锁的范畴
- 再看这张图,当我们一台服务器不够的时候,比如双11活动, 这种一台服务器,它肯定是扛不住的
- 在我们这个订单的web服务问题下,在多个服务器集群的场景下,Goroutine 不能实现跨服务器的锁
- 因为是两个完全物理隔绝的服务器,两个完全不一样的硬件,为了高并发,必须提供更多的服务器
- 为了解决这个问题,分布式锁应运而生
- 看这里简化的图,就是说服务器一和服务器,我们这个锁并不在服务器上,是单独的一把锁
- 这个锁不属于任何一个服务器,是一个公共的第三方,任何服务抢到就可以优先执行任务
- 就是说你这个服务器1和服务器2,谁先抢到这把分布式的锁,谁就执行这个任务
- 各个服务器都是平权的,要想争夺公共的资源,谁优先抢到了,就是谁的
常见的锁和分布式锁的区别
1 ) 常见的锁
- 第一个就是golong程序里面用到的锁
- Mutex 互斥锁,也叫独占锁,就是我占有了你就不能占有,除非被释放
- RWMutex 读共享,写互斥的锁, 当大家一起读的时候,这个锁的性能是不错的,如果是写,就是互斥的
- 适合读多写少的场景,比如读博客人多,写博客人少;看视频人多,发视频人少
- 以上是golang程序中两种类型的锁
- 第二个是 mysql 里面的 悲观锁和乐观锁
- 这都是赋予人的这种情绪上的描述
- mysql里一般有什么锁呢?排他锁, 共享锁,表锁,行锁等等,这些呢就容易混
- 1 )而这里的悲观锁是一种从思想上处理并发的一种方式
- 比如,悲观所和某人性格有关,比如这个人就是悲悲的,他总是负面情绪很大
- 就是没有什么正能量,在数据库里也是一样,总是会认为会和别人发生冲突,总觉别人会修改它的数据
- 既然这样,我就在修改的时候,拿一把锁给他锁住,别人谁也别想用,就是这么个情绪
- 通常这把锁就是排他锁,因为说只有我能用,别人不能用,别人能用,我就不能用,它就是一个排他的
- mysql的悲观锁对应一句笑话:“总有刁民想害朕”,就是一直觉得总有人和我竞争,抢夺资源
- 2 )与之相对应的就是说乐观锁,它就是赋予人的这种乐观情绪,什么事也不放在心上。
- 但是mysql它一定要解决这个数据的竞争问题,乐观锁用一个version字段去记录这一条记录
- 这记录,大家都可以修改,但总有一人会修改成功,与 version 匹配的,在某些场景下比悲观锁好一些
- 但是,有的时候也分业务场景,具体的性能要根据压测,很多在上线之前都会有压测和相关指标
- 在编码阶段不会超过整个产品研发三分之一的时间,其他的时间可能在
- 准备环境,准备case,准备压测的case,还有性能,还有监控,还有的运维,以及应对后续突发流量等
- 很多预案都要考虑到,并不是说我们把代码写完了就结束了
2 ) 分布式锁
- 第三个是redis的分布式锁
- 这是在mysql的库存服务的一张表,它的 id 是66,数量是100
- 库存服务1想要拿到id是 1 的这个记录的锁
- 库存服务2也想要拿到id是 1 的这个记录的锁
- 这个锁是mysql提供的,悲观锁它就是一个互斥锁
- 如果你用悲观锁,那就是永远是这么一个服务,被一方拿到之后就被锁定
- 只有一方执行完了,退出了,才会被其他服务锁定
- 如果有更多的服务,就排队一个一个来
- 谁抢到,谁先执行,抢不到就在旁边等着
- 所以它不适合并发场景要求相对高的情况