总结下来:互斥实现>分布式环境锁,包含分布式环境锁
基于数据库数据库锁实现(这同样可以解决分布式环境下的锁问题,分布式中的基于主键也可以解决互斥问题,当然基于互斥也可以解决分布式环境下大部分的锁问题,但是为什么我们还是要用分布式锁呢?)
数据库使用悲观锁和乐观锁就可以保持互斥,那么分布式锁有个屁用!!
很多人学了后,一定会这么认为。
但是实际上,数据库锁只是对数据执行的语句,进行互斥,万一你还有操作缓存,那咋办,所以我们还是要使用分布式锁。
悲观锁
乐观锁
基于分布式环境锁实现
基于数据实库
基于redis实现
基于zookeeper实现
悲观锁:总有刁民要害郑,行锁就是悲观锁。
悲观锁有:共享锁,排它锁
共享锁:就是读锁,具有读的性质,可以让你读
排它锁:修改删除数据时,别人不能操作数据。
我当时看这图我也懵逼了,这是什么意思,听我娓娓道来
你可以把x理解成“写数据”,把s理解为读数据。
x锁,别人不能写也不能读(排它锁)
s锁:别人不能写,可以读(共享锁)
反正它们两个都是可以互相转换的
读锁是什么了?
就是查询select,我觉得每个select语句都是默认加了s锁的,错了不怪我,我自己认为的。
把读锁也变成排它锁
@Select("select * from t_goods where id = #{id} for update") Goods laodByIdForUpdate(Long id);
乐观锁
乐观锁:就是数据提交的时候进行检测。
最常见的手段就是,对数据库表中加字段,我记得是基于cas思想(这是什么呢?我觉得和cap有点像,我记得好像是,用预期值比较,如果是预期值就成功,不是预期值就失败,错了不怪我,我都是没工作的大四学生,投了一千份简历,我微信好友里,别个机械的,工程的都找到java工作了,我软件本科没人理,这还有天理吗!!)。
加上时间戳或者版本号,我们就以版本号为例子。(但是实际上面试的时候,谁会问那么底层,你直接说,乐观锁在数据库加上版本号或者时间戳进行比较,这样实现乐观锁,谁让举例子了!)
update account set version = version + 1 where id = #{id} and version = #{oldVersion}
第一个人获得了,版本号加一,另外一个人版本号匹配不上,后面的人都匹配不上了(他们都是老版本为0)。
没有匹配上的话,你只有在自己业务层进行判读,失败的话,自己决定该怎么办!
我没有实际用过。但是我想象的话,自己查一下数据库中的版本号,把它版本号加上去,然后再去抢,不知道这样行不行的通(上面的话,纯自己脑补的,正确性待商榷)
基于主键(唯一索引)解决互斥问题
新建一个表,每次抢到锁就insert一个数据。(sql语句就变成两个了,操作原数据,又要插入一个用来锁的表),后面的人同样也要插入,因为主键的唯一性,发现插入不了,只有等待执行完的那个进程,把记录删了才行。
比如a对数据的操作
加锁:insert into lock(id) values(1)
业务sql:update good set count = count -1 where id =1;
执行完后要释放锁:delete lock where id =1
中途要执行的人,看要拿锁拿不到(基于主键唯一性),只有等他删除记录才可以,重新拿到锁。(仿佛我们这些投了一千份简历都没工作的人,只有等他们把饭碗扔了,我们再去抢)