针对mysql的事务,四大特性中的隔离性,就需要靠事务锁进行实现
根据不同的划分,可以将锁划分为几种方式:
- 按照粒度划分:行锁、表锁、页锁
- 按照使用方式划分:共享锁、排他锁
- 按照思想划分:悲观锁、乐观锁
粒度划分(行锁,表锁,页锁)
表锁在操作数据时会锁定整张表,因而并发性能较差;
行锁则只锁定需要操作的数据,并发性能好,但是由于加锁本身需要消耗资源(获得锁、检查锁、释放锁等都需要消耗资源),因此在锁定数据较多情况下使用表锁可以节省大量资源
页锁是粒度介于行级锁和表级锁中间的一种锁,表示对页进行加锁。
注意:
MySQL中不同的存储引擎能够支持的锁也是不一样的。MyISAM只支持表锁,而InnoDB同时支持表锁和行锁,且出于性能考虑,绝大多数情况下使用的都是行锁。
InnoDB 行锁是通过给索引项加锁实现的,如果没有索引,InnoDB会通过隐藏的聚簇索引来对记录加锁。也就是说,如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。因为没有了索引,找到某一条记录就得扫描全表,要扫描全表,就得锁定表。
使用方式划分(共享锁,排它锁)
共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁。获取排他锁的事务是可以对数据就行读取和修改。
注意:
对于select语句,InnoDB不会加任何锁,也就是可以多个并发去进行select的操作,不会有任何的锁冲突,因为根本没有锁。对于insert,update,delete操作,InnoDB会自动给涉及到的数据加排他锁,只有查询select需要我们手动设置排他锁。
思想划分(悲观锁,乐观锁)
悲观锁是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。 悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。悲观锁通过常用的select … for update操作来实现悲观锁,悲观锁的流程也被称为:一锁二查三更新。
悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会。另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载。还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。
乐观锁相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。乐观锁通常使用版本标识方法来实现,比如MVCC 多版本并发控制(MultiVersion Concurrency Control,简称MVCC或MCC)。
乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。