Mysql中锁的介绍
1. Mysql引擎
MySQL引擎就是指表的类型。数据库的存储引擎决定了表在计算机中的存储方式。
分类:通过show engines;
查看。结果如下图所示、
创建新表时如果不指定存储引擎,那么系统就会使用默认的存储引擎,MySQL5.5之前的默认存储引擎是MyISAM,5.5之后就改为了InnoDB。
查看数据库的引擎命令:
对比:
show variables like '%storage_engine%'
2. 锁的定义
概念:锁是计算机协调多个进程或线程并发访问某一资源的机制。锁可以保证数据并发访问的一致性、有效性;锁是Mysql在服务器层和存储引擎层的的并发控制。
注意:加锁是消耗资源的,锁的各种操作,包括获得锁、检测锁是否是否已解除、释放锁等.
3. 锁的分类
(1)按锁的粒度分类
根据锁的粒度划分,可以将锁分为行级锁、页级锁、表级锁。
① 行级锁:是MySQL锁定粒度最小的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,单加锁开销最大。InnoDb支持行级锁。
②页级锁:是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。
③表级锁:是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。
对比:表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB支持页级锁
(2)按锁的级别分类
根据锁的级别划分,可以将锁分为共享锁、排它锁。
①共享锁:又称"读锁",是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE; --加共享锁
②排它锁:又称"写锁",如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
SELECT * FROM table_name WHERE ... FOR UPDATE;--加排它锁
(3)按加锁的方式分类
按加锁的方式,可以分为自动锁、显示锁。
Tips: INSERT、UPDATE、DELETE InnoDB会自动加排他锁,对于普通SELECT语句,InnoDB不会加任何锁。
(4)按操作进行分类
按操作进行分类,可分为DML锁,DDL锁
①DML(data manipulation language):数据操作语言。
它们是SELECT、UPDATE、INSERT、DELETE,这4条命令是用来对数据库里的数据进行操作的语言,这些语句的操作对于的锁为DML锁。
②DDL(data definition language):数据定义语言
DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表的结构,数据类型,表之间的连接和约束等初始化工作上,他们大多在建立表时使用。这些语句的操作对于的锁为DDL锁。
(5) 按适用方式分类
按适用方式分类:悲观锁和乐观锁
悲观锁(Pessimistic Concurrency Control,PCC):假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
乐观锁(Optimistic Concurrency Control,OCC):假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
优缺点:
① 悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发性不好。但这也保证了数据的安全性。
② 乐观锁不能解决脏读,加锁的时间要比悲观锁短(乐观锁可以获得较好的并发访问性能。但是不能保证安全性。比如:如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。
4. 死锁的原理及分析
(1) MVCC
MySQL InnoDB存储引擎,实现的是基于多版本并发控制协议—MVCC(Multi-Version Concurrency Control) 。
MVCC最大的好处,读不加锁,读写不冲突。在读多写少的应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,这也是为什么现阶段,几乎所有的RDBMS,都支持了MVCC。
(2)两段锁:2PL
传统RDBMS加锁的一个原则,就是2PL (二阶段锁):Two-Phase Locking。锁操作分为两个阶段:加锁阶段与解锁阶段,并且保证加锁阶段与解锁阶段不相交。
过程:
(3)为什么会发生死锁
MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB中,锁是逐步获得的,就造成了死锁的可能。
在InnoDB中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。
当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。
(4)如何避免死锁
(1)如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
(2)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
(3)对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;