MySQL存储引擎之Innodb

在MySQL5.5以后版本默认引擎为Innodb。

和MyISAM不同,Innodb是一种支持事务的存储引擎。Innodb使用表空间进行存储数据,具体存储在哪个存储空间是由innodb_file_per_table参数决定的。
当参数为ON时,则会为每个Innodb表建立一个以tablename.ibd命名的独立表空间。
当参数为OFF时,则会把数据存储到共享表空间(系统表空间)ibdataX(X代表数字)

查看当前数据库参数:

MySQL [test]> show variables like 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_file_per_table | ON    |
+-----------------------+-------+
1 row in set (0.05 sec)

新建一张innodb的数据表:

MySQL [test]> create table myinnodb(id int,c1 varchar(10))engine='innodb';
Query OK, 0 rows affected (0.07 sec)

查看文件系统中的存储方式:

[root@wangerxiao test]# ls -lh   myinno*
-rw-r----- 1 mysql mysql 8.4K Mar 14 19:58 myinnodb.frm
-rw-r----- 1 mysql mysql  96K Mar 14 19:58 myinnodb.ibd

frm文件同样是表结构文件,ibd文件就是表的存储空间。

现在我们能看当参数为OFF的时候,表的存储方式:
先将参数设置为OFF状态:

MySQL [test]> set  global innodb_file_per_table=off;
Query OK, 0 rows affected (0.00 sec)

MySQL [test]> show variables like 'innodb_file_per_table';                
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_file_per_table | OFF   |
+-----------------------+-------+
1 row in set (0.00 sec)

新建一张数据表

MySQL [test]> create table myinnodb_g(id int,c1 varchar(10))engine='innodb';
Query OK, 0 rows affected (0.03 sec)

查看文件系统保存方式

[root@wangerxiao test]# ls -lh   myinnodb*
-rw-r----- 1 mysql mysql 8.4K Mar 14 19:58 myinnodb.frm
-rw-r----- 1 mysql mysql 8.4K Mar 14 20:03 myinnodb_g.frm
-rw-r----- 1 mysql mysql  96K Mar 14 19:58 myinnodb.ibd

我们发现只有 frm文件,而没有ibd文件。
实际上数据存储到了数据的共享表空间(ibdata1中)中:

[root@wangerxiao test]# cd ..
[root@wangerxiao var]# ls  -lh 
total 218M
-rwxr-xr-x  1 mysql mysql  16K Oct  8 20:29 aria_log.00000001
-rwxr-xr-x  1 mysql mysql   52 Oct  8 20:29 aria_log_control
-rwxr-xr-x. 1 mysql mysql   56 Aug 19  2016 auto.cnf
-rw-r-----  1 mysql mysql  557 Dec 22 18:06 ib_buffer_pool
-rwxr-xr-x. 1 mysql mysql  74M Mar 14 20:03 ibdata1
-rwxr-xr-x. 1 mysql mysql  64M Mar 14 20:03 ib_logfile0
-rwxr-xr-x. 1 mysql mysql  64M Aug 19  2016 ib_logfile1

系统表空间和独立表空间要如何选择:

比较:
* 系统表空间无法简单的收缩文件大小(非常麻烦)
* 独立表空间可以通过optimize table命令收缩文件系统文件
在清理大表数据之后,可以只对这一个表进行optimize操作,也会对表进行重建,但是不重启数据库服务器,不影响访问
* 系统表空间会产生IO瓶颈
因为只存在一个表空间,因此当同时对多个表空间进行数据刷新时,在文件系统 上时顺序执行的,会产生一定得IO瓶颈。
* 独立表空间可以同时向多个文件刷新数据

建议:
* 对Innodb使用独立表空间(5.6版本之后已是默认)

如何把之前的版本的的共享表空间的数据表转换到独立表空间:
步骤:
1. 使用mysqldump导出所有数据库表数据数据(数据库中使用存储过程,触发器,计划事件等一起导出)
2. 停止服务MySQL服务,修改参数,并删除原来Innodb相关文件(磁盘空间的回收)
3. 重启MySQL服务,重建Innodb系统表空间
4. 重新导入数据

当我们把数据表从共享空间转移到独立表空间中,在共享表空间中还存在着一些重要的东西:Innodb 数据字典信息,Undo回滚段

Innodb存储引擎的特性:
* Innodb是一种事务性存储引擎
* 完全支持事务的ACID特性
*Redo Log 和 Undo Log
Redo Log 主要用于实现事务的持久性,由两部分组成:内存中的重做日志缓冲区,重做日志文件。
Undo Log 主要用于未提交事务进行回滚和实现mvcc。

查看缓冲区的大小:(字节为单位,每一秒就把缓冲区刷新到磁盘上)

MySQL [test]> 
MySQL [test]> show variables like 'innodb_log_buffer_size';
+------------------------+---------+
| Variable_name          | Value   |
+------------------------+---------+
| innodb_log_buffer_size | 8388608 |
+------------------------+---------+
1 row in set (0.00 sec)

重做日志文件的设置:

MySQL [test]> show variables like 'innodb_log_files_in_group';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| innodb_log_files_in_group | 2     |
+---------------------------+-------+
1 row in set (0.00 sec)

我们看到有两个log
[root@wangerxiao var]# ls  -lh 
total 218M
-rwxr-xr-x  1 mysql mysql  16K Oct  8 20:29 aria_log.00000001
-rwxr-xr-x  1 mysql mysql   52 Oct  8 20:29 aria_log_control
-rwxr-xr-x. 1 mysql mysql   56 Aug 19  2016 auto.cnf
-rw-r-----  1 mysql mysql  557 Dec 22 18:06 ib_buffer_pool
-rwxr-xr-x. 1 mysql mysql  74M Mar 14 20:03 ibdata1
-rwxr-xr-x. 1 mysql mysql  64M Mar 14 20:03 ib_logfile0
-rwxr-xr-x. 1 mysql mysql  64M Aug 19  2016 ib_logfile1
* Innodb 支持行级锁
* 行级锁可以最大程度的支持并发
    进行写操作时,需要锁定的资源更少
* 行级锁是由存储引擎层实现的

什么是锁:

锁是数据库系统区别文件系统的一个重要特性

  • 锁的主要作用是管理共享资源的并发访问(邮件服务,如果没有锁对邮箱进行控制,两个邮件同时对同一个邮箱进行投递,就会造成两封邮件数据重叠。有了锁之后,就会形成阻塞)
  • 锁用于实现事务的隔离性(未提交的事务,锁定的数据是不能被其他的事务所查询到的)

锁的类型

  • 共享锁(读锁)
    不会被阻塞的,多个线程可以在同一时间读取同一个资源
  • 独占锁(写锁)
    写锁是独占的,排他的,会阻塞其他的写锁或读锁,只有一个线程能够写

写锁和其他的锁都是不兼容的,读锁是和读锁是兼容的。读锁和写锁都是行级锁。

实际小栗子:
我们开启两个连接,开启两个事务,我们在其中一个连接中开启一个事务,并为第一行添加一个独占锁。
查看数据表的内容:

MySQL [test]> select * from  myinnodb;
+------+------+
| id   | c1   |
+------+------+
|    2 | bb   |
|    3 | cc   |
+------+------+
2 rows in set (0.00 sec)

开启事务,新增锁,但是不提交:

MySQL [test]> begin;
Query OK, 0 rows affected (0.01 sec)

MySQL [test]> update myinnodb set c1="bbbb" where id = 2;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1  Changed: 1  Warnings: 0

我们在另一个连接中查看修改的数据:

MySQL [test]> select  * from myinnodb where id = 2;
+------+------+
| id   | c1   |
+------+------+
|    2 | bb   |
+------+------+
1 row in set (0.01 sec)

我们发现,独占锁并没有阻塞其他的查询,这是因为Innodb利用了上面说的Undolog,我们在这里查到的数据实际上存储的是Undolog的数据。并不是第一个连接修改后的数据。

锁的粒度

指的是被加锁的资源的最小单位。比如在行上加锁,锁的最小单位是行,锁被称为行级锁。还有页级锁,表级锁。一种提高资源并发性的方式是让锁定义的方向尽可能的小。任何时候,锁定的资源越少,并发性越高。

  • 表级锁
    开销最小的策略(开销小,则并发性底)在MySQL服务器层使用。

  • 行级锁
    行级锁可以最大程度的支持并发处理,同时锁的开销也比表级锁的要大,行级锁只在存储引擎中实现,而MySQL服务器层并没有实现。

实际栗子:
我们看下建表的结构:

MySQL [test]> show  create  table myinnodb;
+----------+--------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                   |
+----------+--------------------------------------------------------------------------------------------------------------------------------+
| myinnodb | CREATE TABLE `myinnodb` (
  `id` int(11) DEFAULT NULL,
  `c1` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
+----------+--------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

表示Innodb,因此是默认加的行级锁,我们看下如何为表添加表级锁:

我们在一个连接中创建一个表级的独占锁
MySQL [test]> lock table myinnodb write;
Query OK, 0 rows affected (0.00 sec)

然后在另一个连接中进行查询
MySQL [test]> select  * from myinnodb where id = 2;

我们把第一个连接的表进行解锁
MySQL [test]> unlock tables;
Query OK, 0 rows affected (0.00 sec)

再在另一个连接中进行查询
MySQL [test]> select  * from myinnodb where id = 2;
+------+------+
| id   | c1   |
+------+------+
|    2 | bb   |
+------+------+
1 row in set (0.00 sec)

我们发现第一个连接的表锁定时,另一个连接并没有返回查询结果,发生了阻塞。当
解锁之后,另一个连接可以正常查询。

阻塞和死锁

  • 什么是阻塞
    数据库阻塞的现象:第一个连接占有资源没有释放,而第二个连接需要获取这个资源。如果第一个连接没有提交或者回滚,第二个连接会一直等待下去,直到第一个连接释放该资源为止。对于阻塞,数据库无法处理,所以对数据库操作要及时地提交或者回滚。

  • 什么是死锁
    数据库死锁的现象:第一个连接占有资源没有释放,准备获取第二个连接所占用的资源,而第二个连接占有资源没有释放,准备获取第一个连接所占用的资源。这种互相占有对方需要获取的资源的现象叫做死锁。对于死锁,数据库处理方法:牺牲一个
    连接(占用资源少的),保证另外一个连接成功执行。可以由系统自动处理

Innodb状态检查

show engine innodb status 

这个是统计上次输出之后的平均值
查看具体输出:

MySQL [test]> show  engine innodb  status \G; 
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status: 
=====================================
2017-03-16 20:01:29 0x7efff87ce700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 22 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 22 srv_active, 0 srv_shutdown, 7262060 srv_idle
srv_master_thread log flush and writes: 7261989
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 65
OS WAIT ARRAY INFO: signal count 68
RW-shared spins 0, rounds 187, OS waits 62
RW-excl spins 0, rounds 214, OS waits 0
RW-sx spins 1, rounds 2, OS waits 0
Spin rounds per wait: 187.00 RW-shared, 214.00 RW-excl, 2.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 20413
Purge done for trx's n:o < 20413 undo n:o < 0 state: running but idle
History list length 413
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421113842911968, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421113842911056, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (read thread)
I/O thread 4 state: waiting for i/o request (read thread)
I/O thread 5 state: waiting for i/o request (read thread)
I/O thread 6 state: waiting for i/o request (write thread)
I/O thread 7 state: waiting for i/o request (write thread)
I/O thread 8 state: waiting for i/o request (write thread)
I/O thread 9 state: waiting for i/o request (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
409 OS file reads, 1325 OS file writes, 636 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 69239, node heap has 3 buffer(s)
Hash table size 69239, node heap has 1 buffer(s)
Hash table size 69239, node heap has 1 buffer(s)
Hash table size 69239, node heap has 1 buffer(s)
Hash table size 69239, node heap has 2 buffer(s)
Hash table size 69239, node heap has 8 buffer(s)
Hash table size 69239, node heap has 3 buffer(s)
Hash table size 69239, node heap has 3 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 11880615
Log flushed up to   11880615
Pages flushed up to 11880615
Last checkpoint at  11880606
0 pending log flushes, 0 pending chkp writes
223 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 274857984
Dictionary memory allocated 988021
Buffer pool size   16382
Free buffers       15518
Database pages     842
Old database pages 292
Modified db pages  0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 379, created 463, written 778
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 842, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=10477, Main thread ID=139636902983424, state: sleeping
Number of rows inserted 47284, updated 1, deleted 0, read 32
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set (0.01 sec)

ERROR: No query specified

适用场景

Innodb适合于大多数OLTP应用(并且支持全文索引,和空间函数)

你可能感兴趣的:(MySQL存储引擎之Innodb)