一级锁协议:事务T在修改数据R之前必须先对数据R所在的项申请排它锁,在获准了加排它锁后,直到事务T结束后才释放所加的排它锁。如果未获准加排它锁,则该事务T进入等待状态,直到获准加排它锁后该事物才继续执行(一级锁协议可防止丢失修改,并保证事务T是可恢复的)。
二级锁协议:除包括一级锁协议的内容外,还包括:事务T在读数据R之前必须先对数据R所在的项申请加共享锁,在获准了加共享锁之后,读完数据R即可释放所加的共享锁。如果未获准加共享锁,则该事务T进入等待状态,直到获准加S锁该事物才继续执行(不仅可以防止丢失修改,还可防止读“脏”数据。)
三级锁协议:除包括一级锁协议的内容外,还包括:事务T在读数据R之前必须先对数据R所在的项申请加共享锁,在获准了加共享锁之后,直到该事务T结束时才释放所加的共享锁。如果未获准加共享锁,则该事务T进入等待状态,直到获准加S锁该事物才继续执行(不仅可以防止丢失修改,还可防止读“过时”数据。)
1 为什么需要锁
数据库通常有大量的用户在同时操作,所以并发的情况下需要控制对临界资源的操作,数据库通过锁来控制对临界资源的访问,从而保证数据的一致性。例如对于同一个账户,操作之前账户余额为1000,同时开始2个事务,一个事务取款100,一个事务往账户中汇入100,那么2个事务结束后,账户的余额必须还是1000,否则要么银行不干,要么个人不干。
2 锁类型
2.1 共享锁(读锁)
读锁是共享的,互相不阻塞,也就是可以多个客户同时读取同一资源,而不互相干扰。
加锁条件:当执行select时,数据库为这个事务分配一把共享锁,来锁定被查询的数据。
解锁条件:默认情况下,数据被读取之后,数据库立即解锁。例如select * from table中,先锁定第一行,读取后,立即解锁第一行,然后再锁定第二行,这样大大降低锁争用程度。在repeatable read和serializable 这两种事务隔离级别下,共享锁是在事务结束时释放的,serializable对表加锁。
2.2 排它锁(独占锁)(写锁)
写锁是排他的,一个写锁会阻塞其他的读锁和写锁,只有这样才能保证同一时刻,只有一个用户能够写入,并阻止其他用户读取正在写入的同一资源。如果要锁定的数据资源,已经放置了其他的锁,则不能再放置排它锁。
加锁条件:当执行insert update 和delete语句时,数据库会对操纵的资源使用排它锁。
解锁条件:事务结束时解锁。
3.锁原理
锁的基本原理如下:
1.当第一个事务访问数据库资源时,如果执行select语句,则必须先获得共享锁,如果执行insert update和delete时,必须获得排它锁
2.当第二个事务也要访问相同的资源时,如果执行select语句,也必须先获得共享锁,如果执行insert update或者delete,也必须获得共享锁。根据已经放置在资源上的锁类型,来决定第二个事务是应该等待第一个事务释放锁,还是立即获得锁。
4 锁粒度
常见的锁有表锁、 行锁、 页锁、外键锁等。
锁有一定的消耗,主要有:获得锁,检查锁是否已经解除,释放锁等,这些操作都会增加系统的开销。
表锁开销比较小,但是并发性能也较差,行锁并发性能高,但是需要更多锁,数据库系统一般都支持锁的自动升级,例如一个事务中的锁过多的时候,可能会将行锁升级到表锁。
mysql的各个存储引擎根据不同的应用场景采用不同的锁机制,MyISAM存储引擎采用表锁,InnoDB使用行锁。如果执行alert table之类的操作,服务器也会采用表锁,而忽略存储引擎的锁机制。
页锁,某些数据库支持页锁,页锁粒度介于行锁和表锁之间,用于锁定存放数据的页,1页通常含有n个数据行。
外键锁:外键会产生高级别的锁,后面介绍死锁的时候会提到。
5.乐观锁和悲观锁
悲观锁:假设如果不加锁就一定会出现问题。
乐观锁:先假定不会出现并发问题,出现问题后再采取相应的措施。
悲观锁通过select * from tbl for update实现。
乐观锁可以在表中加一个version字段,每次更新的时候都+1,这样如果出现并发,第二次更新的时候version的值已经不再匹配,可以在这时采取相应的措施。
6 死锁
6.1 死锁是如何产生的
同java的死锁一样,都是互相等待对方释放锁,造成了相互阻塞,造成的死锁。
请看下面的表格:
6.2 如何避免死锁
1.修改操作表的顺序
从上面的示例中可以看出,如果调整一下操作表的顺序就可以避免死锁。
2.外键
如果向子表写记录,那么外键约束会检查父表的记录,并锁住父表的记录,来保证这条记录不会在这个事务完成之时就被删除了。
产生高级别的锁,会阻止其他事务操作或者其他DML操作,如果是因为外键产生的死锁,可以去掉外键约束,由应用来保证数据的完整性,通常生产环境都不建议加外键约束。
3.短事务
缩短事务的执行时间,可以减少锁的持有时间,可以降低死锁的风险。
7 锁可能出现的问题
上锁是有开销的,即使不是给数据行而是给数据页上锁,也是有一定的时间的,如果第一个事务update一张大表,这时候第二个事务update这张大表比较靠后的位置的数据,这时候就可能会出现,第一个事务还没来得及给相应的数据上锁,第二个事务已经上了锁,所以出现第一个事务要等待第二个事务提交后释放锁,才能继续执行的情况。
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/4406.html