InnoDB与MyISAM的最大不一样有两点:html
一是支持事务(TRANSACTION);二是采用了行级锁。mysql
MyISAM不支持事务、行锁和外键,访问速度快(表级锁,可同时读,不可写),多使用于多读写少或对事务完整性没有要求的状况;算法
MEMORY:将全部的数据保存在RAM中,对表的大小有限制;sql
MERGE:用于将一系列等同的MyISAM表以逻辑方式组合在一块儿,并做为一个对象引用它们;数据库
事务是由一组SQL语句组成的逻辑处理单元,事务具备如下4个属性,一般简称为事务的ACID属性。session
4种隔离级别比较数据结构
隔离级别并发 |
读数据一致性post |
脏读优化 |
不可重复读 |
幻读 |
未提交读(Read uncommitted) |
最低级别,只能保证不读取物理上损坏的数据 |
是 |
是 |
是 |
已提交度(Read committed) |
语句级 |
否 |
是 |
是 |
可重复读(Repeatable read) |
事务级 |
否 |
否 |
是 |
可序列化(Serializable) |
最高级别,事务级 |
否 |
否 |
否 |
数据库的事务隔离越严格,并发反作用越小,但付出的代价也就越大,由于事务隔离实质上就是使事务在必定程度上 “串行化”进行,这显然与“并发”是矛盾的。同时,不一样的应用对读一致性和事务隔离程度的要求也是不一样的,好比许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。
1、PROPAGATION_REQUIRES_NEW:建立新事务,不管当前存不存在事务,都建立新事务。
2、PROPAGATION_REQUIRED:支持当前事务,若是当前存在事务,就加入该事务,若是当前没有事务,就建立一个新事务。
3、PROPAGATION_SUPPORTS:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就以非事务执行。‘
4、PROPAGATION_MANDATORY:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就抛出异常。
5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER:以非事务方式执行,若是当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与PROPAGATION_REQUIRED相似的操做。
InnoDB存储引擎既支持行级锁,也支持表级锁,默认状况下采用行级锁。
InnoDB的锁类型有:
另外,为了容许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。
InnoDB行锁模式兼容性列表
锁 |
X |
IX |
S |
IS |
X |
冲突 |
冲突 |
冲突 |
冲突 |
IX |
冲突 |
兼容 |
冲突 |
兼容 |
S |
冲突 |
冲突 |
兼容 |
兼容 |
IS |
冲突 |
兼容 |
兼容 |
兼容 |
意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;
若将上锁的对象当作一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先须要对粗粒度的对象上锁。以下图,当咱们要对记录加上排他锁时,那么咱们先要对数据库A、表、页都加上意向排他锁,最后再对记录加上排他锁,若其中任何一部分致使等待,那么该操做须要等待粗粒度锁的完成。
InnoDB存储引擎有3种行锁的算法设计,分别是:
Record Lock老是会去锁住索引记录。若是InnoDB存储引擎表创建的时候没有设置任何一个索引,这时InnoDB存储引擎会使用隐式的主键来进行锁定。
InnoDB中对于行的查询都是采用Next-Key Lock锁定算法。对于不一样SQL查询语句,可能设置共享的(Share) Next-Key Lock和排他的(exlusive) Next-Key Lock,其主要目的也是为了解决幻行问题。
但当查询的索引含有所有惟一属性(主键或惟一索引)时InnoDB存储引擎会对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引自己,而不是范围,从而提升应用的并发性。
可是若是查询的条件中包含的是辅助索引,则状况会彻底不一样,会产生以下锁状况:
若是查询条件没有索引,则全表扫描,且每条记录之间加上了gap锁,为了防止幻读。
下面是一个示例:
咱们知道InnoDB默认的事务隔离级别为REPEATABLE READ模式,而REPEATABLE READ模式下,Next-Key Lock算法又是默认的行记录锁定算法,因此就能够避免幻读的现象。
InnoDB行锁是经过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不一样,后者是经过在数据块中对相应数据行加锁来实现的。
InnoDB这种行锁实现特色意味着:只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁!
1)在不经过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。
InnoDB存储引擎的表在不使用索引时使用表锁例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_no_index where id = 1 ; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_no_index where id = 2 ; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
mysql> select * from tab_no_index where id = 1 for update; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
|
mysql> select * from tab_no_index where id = 2 for update; 等待 |
session_1只给一行加了排他锁,但session_2在请求其余行的排他锁时,却出现了锁等待!缘由就是在没有索引的状况下,InnoDB只能使用表锁。当咱们给其增长一个索引后,InnoDB就只锁定了符合条件的行。
InnoDB存储引擎的表在使用索引时使用行锁例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_with_index where id = 1 ; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from tab_with_index where id = 2 ; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
mysql> select * from tab_with_index where id = 1 for update; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
|
mysql> select * from tab_with_index where id = 2 for update; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
2)因为MySQL的行锁是针对索引加的锁,不是针对记录加的锁,因此虽然是访问不一样行的记录,可是若是是使用相同的索引键,是会出现锁冲突的。
InnoDB存储引擎使用相同索引键的阻塞例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from tab_with_index where id = 1 and name = '1' for update; +------+------+ | id | name | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) |
|
虽然session_2访问的是和session_1不一样的记录,可是由于使用了相同的索引,因此须要等待锁: mysql> select * from tab_with_index where id = 1 and name = '4' for update; 等待 |
3)当表有多个索引的时候,不一样的事务可使用不一样的索引锁定不一样的行,另外,不管是使用主键索引、惟一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
InnoDB存储引擎的表使用不一样索引的阻塞例子
session_1 |
session_2 |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from tab_with_index where id = 1 for update; +------+------+ | id | name | +------+------+ | 1 | 1 | | 1 | 4 | +------+------+ 2 rows in set (0.00 sec) |
|
Session_2使用name的索引访问记录,由于记录没有被索引,因此能够得到锁: mysql> select * from tab_with_index where name = '2' for update; +------+------+ | id | name | +------+------+ | 2 | 2 | +------+------+ 1 row in set (0.00 sec) |
|
因为访问的记录已经被session_1锁定,因此等待得到锁。: mysql> select * from tab_with_index where name = '4' for update; |
4) 即使在条件中使用了索引字段,可是否使用索引来检索数据是由MySQL经过判断不一样执行计划的代价来决定的,若是MySQL认为全表扫描效率更高,好比对一些很小的表,它就不会使用索引,这种状况下InnoDB将使用表锁,而不是行锁。所以,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。