• 欢迎访问微视觉网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入微视觉微视觉-影视后期交流
  • 本站全面支持自动充值,目的是更好的服务大家!
  • 本站全面开启SSL服务,请放心使用!
  • 如果您觉得本站对你非常有用,那么赶紧使用Ctrl+D 收藏吧

细聊MySQL的Innodb存储引擎(一)

数据库 Jason.w.wei 2年前 (2016-09-03) 996次浏览 已收录 0个评论

MySQL5.5 开始,Innodb 就成为MySQL的默认存储引擎了。可想而知,Innodb 已经成为MySQL的主要生产方式。那 Innodb 到底有什么本事能够击败其它几位存储引擎而荣登宝座呢?下面,我就来和大家一起探讨探讨牛逼的 Innodb 引擎。Innodb 涉及到的知识点比较多,所以我会分几篇来叙述,此篇主要介绍 Innodb 的基本概念和架构。

要了解 Innodb,首先需要了解MySQL的 ACID 模型。何为 ACID?ACID 指的是事务的原子性(A)、一致性(C)、隔离性(I)、持久性(D)。
原子性表示事务是不可分割的。比如一个事务中包括一个插入操作和一个更新操作,那么这两个操作要么一起完成,要么一起撤销,不能某一个完成,另一个未完成。
一致性指的是关系一致性(个人理解),比如 A 表与 B 表有外键约束的关系,A 表的主键是 B 表的外键,那么当 A 表某一个记录被删除时,B 表引用此记录的行也要被删除,从而保证数据一致。
隔离性指某个事务内的操作对外界不可见,除非此事务被提交。比如同时有两个客户端对同一个表进行操作。A 客户端首先读取了 test 表,查看到有一项记录 t1=1,此时 B 客户端对 test 表进行更新操作,使 t1=2,在 B 客户端进行更新操作后,A 客户端再次读取 test 表,此时查看到的结果仍然是 t1=1,这就表明 B 的操作对 A 是不可见的,这就叫做隔离性。
持久性,这个很好理解,就是指数据操作后要保持它的状态,无论服务器重启、关机。

Innodb 是支持事务的存储引擎,为了使事务具有以上所描述的 ACID 特性。Innodb 使出了浑身解数,运用了各种空间管理及锁管理技术,在保证运行效率的基础上实现了事务特性。下面,我们就来研究下 Innodb 到底是使用了哪些技术来实现这些特性的。
Innodb 的锁
锁的作用是保证数据的一致性、隔离性与原子性。MySQL有行级锁与表级锁。行级锁可以对数据中的某一行加锁,当进程获取行级锁时,其它进程可对同一表中的其它行进行操作。而表级锁只能对整个表进行加锁。Innodb 是实现行级锁的,当某一行被处理它的进程获取锁时,其它的进程就不能处理这一行了,而必须等待持有锁的进程释放锁后才能处理。这样,数据在某一时刻或某一事务中就只能被一个会话修改,从而保证数据的一致性。其原理和互斥资源的访问差不多。
Innodb 实现了标准的行级锁,行级锁有两种类型,一种为共享锁,另一种为独占锁。共享锁允许多个会话同时读某一行,独占锁则不允许,必须等待直到持有锁的会话释放锁后才能读取。而对于写操作,共享锁与独占锁都必须等待持有锁的会话释放锁后才能获取锁,进而进行操作。
此外,MySQL为了支持更高粒度的锁机制,还设计了意向锁。意向锁是为正式加锁前做准备的。意向锁分为共享意向锁与独占意向锁。比如需要更新一条记录,那么,如果在执行更新记录前加上独占意向锁,那么在更新时会立即获得独占锁。设计意向锁的主要目的是向其它的会话展示当前会话正准备获取锁。意向锁为表级锁,并且它不会阻塞任何操作。下表主要展示行级锁与意向锁之间的兼容性。如果锁之间兼容,则事务可以同时获取,否则事务只能等待当前持有的锁释放才能获取。
细聊 MySQL 的 Innodb 存储引擎(一)

X 为独占锁 IX 为意向独占锁 S 为共享锁 IS 为意向共享锁

锁之间的冲突可能会导致死锁,如果两个会话互相等待对方释放锁,而自身又没有主动释放锁时就会导致死锁。以下是一个死锁的例子:

首先创建表并插入测试数据

mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
mysql> INSERT INTO t (i) VALUES(1);

A 会话:

mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;

B 会话:

mysql> START TRANSACTION;
mysql> DELETE FROM t WHERE i = 1;

首先 A 会话获取共享锁,然后 B 会话删除记录,由于删除记录需要独占锁,而要获取独占锁,此行需先 Innodb 锁类型
Innodb 的行级锁是几种不同类型的锁共同作用的。这几种类型分别为

Record lock
Gap lock
Next-key lock

下面简单介绍下这几种锁
Record lock 是在索引记录上加锁,如果一个表没有设置索引,就用主键当索引,如果没有主键,则 MySQL 会生成一个隐藏的聚簇索引。
Gap lock 是一个间隔锁,在某段索引记录的范围上加锁。如一个索引记录有这样几个值,1,5,10,13。那么当某个事务 A 插入值 11 时,这时事务 A 可能在 5~13 这个范围上加 gap 锁。如果事务 B 插入值 12 时,因为在 5~13 这个范围内,所以不允许插入。如果事务 B 插入值 2,由于在 gap 锁范围外,则此操作将被允许。以下是我本机一个关于 Gap 锁的例子:
客户端 A

mysql> select * from b where id<=9 for update;
+----+------+
| id | name |
+----+------+
|  1 | wang |
|  7 | eeee |
|  2 | wei  |
|  3 | ak47 |
|  9 | ffff |
+----+------+
5 rows in set (0.00 sec)

客户端 B

mysql> insert into b (id,name) values (123,'hhh');
Query OK, 1 row affected (0.00 sec)

mysql> insert into b (id,name) values (6,'hhh');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

这就是由于 B 客户端获取不到 Gap 锁导致的。
Next-key lock 是 Record lock 与 Gap lock 相结合后的锁。Next-key 首先会使用 Gap lock 锁定范围,然后使用 Record lock 锁定具体的行。这是 REPEATABLE-READ 隔离级别的默认处理方式。

Innodb 的隔离等级,也就是事务在隔离性上可以设置的隔离粒度。Innodb 可以设置四个隔离级别,通过 transaction-isolation 参数设置,四个隔离级分别为:
REPEATABLE-READ(可重复读)
READ-COMMITTED (读提交)
READ-UNCOMMITTED(读未提交)
SERIALIZABLE(序列化)

下面,我们通过几个例子来叙述这四个隔离级别到底有哪些区别。首先将隔离级别设置为 REPEATABLE-READ,由于 Innodb 默认的隔离级别就是 REPEATABLE-READ,所以也可以不设置 transaction-isolation 参数。
按如下方式设计测试用例

任意客户端:

mysql > use test;
mysql > CREATE TABLE `b` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
mysql > insert into b values (1,wangweiak47),(2,wangweim4a1);

测试一:

A 客户端

mysql > use test; 步骤一
mysql > start transaction; 步骤二
mysql > select * from b; 步骤三(观察点 1)
mysql > insert into b (id,name) values (3,’google’); 步骤四
mysql > select * from b; 步骤五 (观察点 2)

B 客户端
mysql > use test; 步骤六
mysql > start transaction; 步骤七
mysql > select * from b 步骤八(观察点 3)

测试二:

A 客户端

mysql > use test; 步骤一
mysql > start transaction; 步骤二
mysql > select * from b; 步骤三(观察点 1)
mysql > insert into b (id,name) values (3,’google’); 步骤四
mysql > select * from b; 步骤五(观察点 2)
mysql > commit; 步骤六

B 客户端

mysql > use test; 步骤七
mysql > start transaction; 步骤八
mysql > select * from b 步骤九(观察点 3)

1、首先以 REPEATABLE-READ 模式启动服务器,按顺序执行以上步骤。
测试一中的观察点 1 的结果集为

id	   name
1	username01
2	username02

测试一中的观察点 2 的结果集为

id	   name
1	username01
2	username02
3       google

测试一中的观察点 3 的结果集为

id	   name
1	username01
2	username02

可知在 A 事务为提交的时候,B 事务对 A 事务的插入操作是不可见的。
测试二中的观察点 1 的结果集为

id	   name
1	username01
2	username02

测试二中的观察点 2 的结果集为

id	   name
1	username01
2	username02
3       google

测试二中的观察点 3 的结果集为

id	   name
1	username01
2	username02

可知在 A 事务提交后,如果 B 事务未提交,则 B 事务对 A 事务的插入操作仍然是不可见的。在 REPEATABLE-READ 隔离级别,事务以第一次的查询快照为准,别的事务不管提交与否,对本事务均是不可见的。

2、以 READ-COMMITTED 模式启动服务器。注意,当设置 READ-COMMITTED 与 READ-UNCOMMITTED 隔离等级时,需设置参数 binlog_format=row。
执行测试用例可知,测试一中的结果与 REPEATABLE-READ 隔离级别的测试结果相同。在测试二中,结果如下:

测试二中的观察点 1 的结果集为

id	   name
1	username01
2	username02

测试二中的观察点 2 的结果集为

id	   name
1	username01
2	username02
3       google

测试二中的观察点 3 的结果集为

id	   name
1	username01
2	username02
3       google

在测试二中,当 A 事务提交后,B 事务就能获取 A 事务最新插入的数据。如果 B 事务在 A 事务提交之前用相同的查询条件查询过结果集,那么 B 事务两次获取的结果集就会不一样,导致脏读。
3、以 READ-UNCOMMITTED 模式启动服务器,按测试用例执行后可知,在测试一中,当 A 事务进行插入操作未提交时,B 事务就能获取 A 事务插入的数据。所以,它的隔离性更差。

4、SERIALIZABLE 模式为最强的隔离等级,因为服务器是将事务串行化来逐个处理的,但这个模式下


微视觉 , 版权所有丨如未注明 , 均为网络收集丨本网站采用BY-NC-SA协议进行授权 , 转载请注明细聊 MySQL 的 Innodb 存储引擎(一)
喜欢 (6)
[wuwei967@126.com]
分享 (0)

您必须 登录 才能发表评论!