写代码的时候出现了一个思路上的问题
就是对于数据库中的数据,有并发问题 ,比如用户的好友最大数量30,当前用户的好友数量是29,如果用户之前向另外2位玩家发送过好友申请,并且这2位好友同时同意的话,就会出现并发问题。
该逻辑中,是执行了多条不同的sql来处理这个加好友的实现,多条sql是在一个事务中执行的。我一开始的解决思路是在执行第一条sql的时候就加锁,执行完全部sql的时候解锁,但是想到了mysql中是有行锁的 并且第一条sql是会触发行锁的sql ,属于删改类型的sql。
默认这2个线程 的过滤条件是一样的 这样会触发行锁
现在有2个线程 第一个线程开启了事务,执行了一系列的sql,第二个线程也开启了事务,但在执行第一条sql的时候,由于第一个线程没有commit提交,第二线程就会阻塞在第一条sql,也不会执行接下来代码,这样不是就等于互斥锁了吗,只有第一个线程commit,第二线程才会接着往下执行。如果还在代码中加互斥锁,是不是多此一举了。
开启事务
第一条update 语句 where id = 1
编程语言代码中的逻辑
if xxx{
}
第二条update语句 where id = 2
编程语言代码中的逻辑....
第三条update语句 where id = 3
...
提交事务
我一开始认为,如果一个线程开启了事务,并且执行了一系列sql,但是没有提交。另一个线程执行同样的一系列sql 是不会阻塞的,只会在commit的时候阻塞。如果是这样的话,事务就不能代替互斥锁。但我测试过后,发现只要同样的过滤条件,被一个已经开启的事务的会话所执行,并且没有提交。那么其他会话执行sql也会阻塞。
数据的初始状态
第一步:
创建一个会话 并且开启事务 执行这条修改语句 但是没有commit
第二步:
创建一个新的会话 对同样的过滤条件进行修改操作
可以看到此时是在第一条sql的时候就阻塞了第三步:
提交第一个会话的事务
然后查看第二个会话的内容
可以看到在第一个会话提交事务后 被阻塞的第二个会话就复活了。由此,行锁机制,是在sql执行的时候就阻塞,而不是提交的时候
既然如此,那么事务能不能替代互斥锁呢,这就是我搞不明白的地方,希望懂的朋友们在评论区与我交流,解惑我的不解,多谢!
ps.
我自己在代码中进行了测试。(代码片段是go实现,只需要看流程就行)
代码流程如下
开启事务-> 打印-> 执行删除sql-> 打印 ->睡眠5秒 ->提交或者回滚事务
首先定义一个全局变量 每次执行函数都会自增加1,以方便查看打印是属于哪个线程的
接下来是打印结果
可以看到 线程1的俩个打印是再同一秒内完成,线程2的第二个打印是再间隔几秒后打印(由于是手动发送2个请求,第二个线程是发送时间比第一个晚1秒多,所以间隔不是整5s) 。是不是就证明目前来看事务配合行锁是可以在代码中有互斥锁的效果的。
如果事务能实现互斥锁的效果 那我在代码中再加一个互斥锁的话,性能是不是相比之下会差