mysql(innodb)锁有两种:
共享锁(s):表示我正在读,你可以读(可以读表示加上s锁的读),但不能修改
排他锁(x):表示我正在修改,你不能读,也不能修改
单独的select语句(不去获得锁)可以执行,是因为读的是加锁或者事务前的快照版本
当然还有所谓的意向共享锁,意向排它锁,只是说上锁的数据由一行变成了多行,mysql中的锁可以看成java中的锁,在java里你只有获得了锁才能进入上锁的代码中,而mysql是你上锁后只有获得了锁才能进行相应的操作。
然后,innodb默认在修改语句上加了排它锁,也就是说
update t set a=1 where b=2
这样的语句其实真正是
select * from t where a =1 for update
update t set a=1 where b=2
共享锁:
共享锁是就像一起看一本书,我在看这一页,你也在看这一页,如果你想翻页什么的,就需要你和我都看完了这一页,不然肯定要打起来什么的了,当然,如果快看完的时候我表示想看上一页,你表示想看下一页,这要么就pk,要么就有一个人让步了,这就是死锁了,innodb默认解除死锁的方式是回滚较小的事务。
例如 select * from t where a = 1 lock in share mode ,后面的 lock in share mode 就是加上共享锁的意思了。共享的意思是分享,现在不是说是共享时代么,你想一下,共享单车之类的,每个人都可以用同一辆车,但是你如果想把这车变成自己的,我们就不同意了,这是公共的东西,也就是说你只能读,不能修改。就是说上锁后,其他其他人只能用,不能拿走或者改变它。
排他锁:
排他锁的话就是类似于租东西,在租期内这东西为我所有,你不能看,不能拿,不能改,瞅都不给你瞅一眼。你想要的话,只有等我还了这东西你才能拿得到。
例如 select * from t where a = 1 for update, 后面的 for update 就是加上排他锁的意思,排他,我想这不用我多解释了。真正感觉起来,排他锁就类似于悲观锁,任何人不管做什么是都会妨碍到我,所以一点都不让动。当然,如果这样的情况,张三借了A书,李四借了B书,张三准备还书前是准备借B书的,而李四还书前准备借A书,但是一看书被借走了,就一直没有还书,在等着其他人还书好去借。这种情况又是典型的死锁了,这就需要书店居中协调了,总要有个人先还书,大不了一个人就当没借过这个书,不要你的钱,当然,书店肯定是不要那个更少的钱,这也就是innodb默认解死锁的方式,回退较小的事务。
其他扩展:
真的感觉锁就是一个意向或者是状态,上锁后就是一个加锁的状态,而加上锁时就是一个意向。
这些锁看起来并没有什么用,这就需要扩展了。当然这就牵扯到事务了
脏读:
脏数据:缓冲池中被修改的数据,还没有提交。(事务的操作时在缓冲池中,提交了才写到磁盘中)
脏读:一个事务可以读到另一个事务未提交的数据。。。违背了事务的隔离性
事务的隔离性:事务间隔离开来,不能互相干扰,每个事务都应该感受不到另一个事务
不可重复读:
不可重复读:一个事务读取同一记录两次但是数据不一样,即一个事务读取另一个事务提交的数据造成的结果,数据没有问题(数据已经写到磁盘),但是违反了事务的一致性。(与脏读区别为一个未提交(脏数据),一个已提交(有效数据))
事务的一致性:事务开启时的状态应该和结束时的状态一致,如转账,能量守恒之类的。不可重复读,第一个事务的状态在第二个事务提交后已经改变。例如:a转账给b,但是中途另一个客户端把a转账给c了,状态已经改变(感觉也违反了隔离性)
虚读(幻读):
虚读(幻读):一个事务更新了’全部数据‘,另一个事务插入了包含着’全部数据‘(一定条件的数据)里的数据,并提交,第一个事务这时候再提交,发现符合条件的数据并没有全部更新(跟不可重复读类似,只是虚读是删除和增加造成的,而不可重复读是修改造成的)
丢失更新:
丢失更新:比如一个转账的业务,a用客户端转给b二十元,这时我们的想法肯定是开启个事务,
update a set balance=balance-20 ,
update b set balance=balance+20
再提交了吧,如果因为网络延迟,第一个事务在提交前,a用另一个客户端给b转了30块钱,这时又开了另一个事务,
update a set balance=balance-30 ,
update b set balance=balance+30
本来应该减50的,我们的想法是叠加,但最后要么减了三十,要么减了20,这是一个严重的问题
明白了这三个,然后就是事务的隔离级别了,很明显,
read_uncommited 读未提交。。。这样肯定就是会脏读了。
read_commited 读已提交,这样脏读肯定就没了,但是造成了不可重复读。
repeatable_read 可重复读,这样不可重复读没了,但是虚读没有解决。
serializable 序列化,这样的话,一切都解决了。但是一般我们不用这种,因为这是强制每个数据行的操作时同步进行的,效率太低了,那么这问题就来了,我们不用这种隔离级别,怎么防止虚读和丢失更新呢,虚读还好一点,丢失更新的话可能丢失的就不是一点点数据了。
这时候锁的用处就来了,加上个排他锁,或者自己实现一个乐观锁,比如在数据库中加个version的字段,更新是判断一下,更新完后version加一。
锁算法:
record lock:单行记录锁
gap lock : 范围锁,不包含记录本身
next-key lock:锁定一个范围,并锁定记录本身
查看事务和锁的方法:
information_schema 数据库 中 innodb_trx ,innodb_locks ,innodb_lock_waits表 可以查看相应的事务和锁情况
网友评论