标签:mysql
声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。
之前我们介绍了行级锁,顾名思义行级锁就只是锁住一行或多行数据,因为针对的是行去锁的,因为一个表格内会有很多行数据,要在这些数据中去锁定其中几行数据,是比较耗费资源。而表级锁则是可以锁住整个表,所以相对于行级来说没那么耗费资源,表级锁有两个模式:只读模式和只写模式,这和文件权限里的只读只写有点类似。 在一般情况下表格锁并不经常使用,在这里只是介绍一下如何使用表级锁,和解锁表级锁,而且表级锁的资料都可以在网络上查找到,所以了解一下即可,在mysql官方也有表格锁语法的文档:
https://dev.mysql.com/doc/refman/5.6/en/lock-tables.html
LOCK TABLES 表名 READ
示例:
我们再打开一个客户端来看看能否使用SELECT语句查询这个带有表级锁的表格的数据:
因为我们使用的是只读模式的表级锁,自然每个用户都可以读取、查询这个表格的数据,那么我们可以尝试一下update这会对表格数据修改的语句能否执行:
在行级锁里即便某些行数据被上锁了也还是能够使用insert语句插入数据的,那么我们试一下在表格锁里是否能行得通:
从结果可以得知在表级锁的只读模式下,是不允许任何用户对上锁的表格进行任何的修改的。
自然的delete语句也无法使用:
那么如何解锁呢?看看解锁时会发生什么,解锁表级锁的语法很简单:
UNLOCK TABLES
示例:
使用只写模式的表级锁,语法:
LOCK TABLES 表名 WRITE
示例:
在表级锁的只写模式里,只有上锁用户可以对表格进行写入数据,其他用户是不可以写入数据的,其他用户就连使用SELECT语句查询数据都不可以:
上锁用户可以使用insert语句插入数据,其他用户则不允许这个操作:
update语句也是一样的:
还有delete语句:
如果用户给一张表格上了表级锁,那么这个用户在给这个表格解锁之前就只能操作这个表格,数据库里的其他表格均不可以进行任何的操作,如果操作就会报错:
只写模式的解锁语法是一样的,都是UNLOCK TABLES:
总结一下表级锁,表级锁就是针对表格进行锁定,相对于行级锁没那么耗资源,表级锁有两个模式,只读模式和只写模式,只读模式下上锁用户和其他用户都只能查询数据不能写入数据,只写模式下上锁用户可以进行查询数据和写入数据,其他用户既不能查询数据,也不能写入数据,执行任何SQL语句都会进入等待状态,一直等到表格解锁为止,当表格解锁的时候在等待中的事务会马上被执行。某个用户对某个表格使用了表级锁的话,就只能操作那个表格,数据库里的其他表格均不可进行任何操作。
悲观锁(Pessimistic Lock)是一种概念、解决某些问题的模式,并不是一种特定的机制,悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
所以简单来说悲观锁就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
例如之前我们做的火车票务系统的小案例,就是使用的悲观锁的方式,在我们的代码里都是借助于数据库自带的锁机制完成的,当用户A在购票时用户B就不能够购票,或者购票失败,这就是在整个数据处理过程中,将数据处于锁定状态,具有很明显的排他性(悲观)。 代码:
悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那数据。
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。 相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。
数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。
所以实际上乐观锁和悲观锁一样也是一种概念、解决某些业务需求的模式,并不是一种特定的机制,乐观锁的主要实现方式是我们开发人员自己通过在数据库中增加一条存储数据版本的列,然后通过代码来判断这些数据的版本,而不是借助数据库自带的锁来完成的一种锁定数据的模式。
例如我们假设一种情况:用户A和用户B同时在ATM机里往一个账户余额2000元的账户取款,两个用户都查询到账户还有2000元,所以用户A要取款1500,用户B要取款1000,然后这两个取款事务会同时提交,如果这个银行的系统逻辑写得不够好的话,就会出现2000-1500-1000=-500的结果,账户余额就会出现负数。
示意图:
在这种取款的情况下,如果使用悲观锁来锁住数据的话,由于其排他性,那么另外一个用户就无法查询账户余额,只能处于等待状态,因为在悲观锁里在事务结束之前数据都是处于锁定状态,而且在银行在这种数据量大的地方,使用共享锁这种行级锁也耗费资源。所以就需要用到乐观锁了,乐观锁只有在操作提交的时候才会去锁定数据。在乐观锁中我们可以给数据设定一个版本号,一旦这个数据发生修改,版本号就会发生变化,每一个操作都会先判断版本号是否是最新的版本号,不是的话就不允许操作,在乐观锁的实现过程中我们并不会使用到数据库自带的锁,所以用户们都可以任意的查询或提交操作。
示意图:
下面我们做一个简单的取款系统来演示如何实现乐观锁:
先准备一个表格里面填充一行数据:
代码示例:
运行结果:
乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个【学Java,到凯哥学堂kaige123.com】数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据(Dirty Data),依据脏数据所做的操作可能是不正确的。
在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样.一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。
标签:mysql