在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应用(并且支持全文索引,和空间函数)