概述
[上文](https://www.jianshu.com/p/d38a5d2ccbe2我们具体的深入Mysql 数据结构优化的相关方案,本文将深入Mysql innodb优化的方案。
初始化脚本
通过如下脚本,在mysql数据库中新建对应的实验表和数据。
CREATE TABLE `user_info` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '',
`age` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `name_index` (`name`)
)
ENGINE = INNODB
DEFAULT CHARSET = utf8;
INSERT INTO user_info (NAME, age) VALUES ('xys', 20);
INSERT INTO user_info (NAME, age) VALUES ('a', 21);
INSERT INTO user_info (NAME, age) VALUES ('b', 23);
INSERT INTO user_info (NAME, age) VALUES ('c', 50);
INSERT INTO user_info (NAME, age) VALUES ('d', 15);
INSERT INTO user_info (NAME, age) VALUES ('e', 20);
INSERT INTO user_info (NAME, age) VALUES ('f', 21);
INSERT INTO user_info (NAME, age) VALUES ('g', 23);
INSERT INTO user_info (NAME, age) VALUES ('h', 50);
INSERT INTO user_info (NAME, age) VALUES ('i', 15);
CREATE TABLE `order_info` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`user_id` BIGINT(20) DEFAULT NULL,
`product_name` VARCHAR(50) NOT NULL DEFAULT '',
`productor` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
)
ENGINE = INNODB
DEFAULT CHARSET = utf8;
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p2', 'WL');
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p1', 'DX');
INSERT INTO order_info (user_id, product_name, productor) VALUES (2, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (2, 'p5', 'WL');
INSERT INTO order_info (user_id, product_name, productor) VALUES (3, 'p3', 'MA');
INSERT INTO order_info (user_id, product_name, productor) VALUES (4, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (6, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (9, 'p8', 'TE');
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p199', 'WHH99');
-- 验证分区
CREATE TABLE user_temp (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
NAME VARCHAR(20),
PRIMARY KEY (id)
)ENGINE = INNODB PARTITION BY KEY (id) PARTITIONS 3;
INSERT INTO user_temp VALUES(1,"hello") ;
INSERT INTO user_temp VALUES(2,"world") ;
INSERT INTO user_temp VALUES(3,"nice") ;
DROP PROCEDURE IF EXISTS batchInsert;
-- 批量插入数据
DELIMITER //
CREATE PROCEDURE batchInsert()
BEGIN
DECLARE num INT;
SET num=20;
WHILE num<=100000 DO
INSERT INTO user_info (id, NAME, age) VALUES (num ,CONCAT('xys',num) , 20);
INSERT INTO order_info (user_id, product_name, productor) VALUES (num, CONCAT('p',num) , CONCAT('WHH',num));
SET num=num+1;
END WHILE;
END
//
DELIMITER ; #恢复;表示结束
CALL batchInsert;
SELECT COUNT(1) FROM `user_info`;
SELECT COUNT(1) FROM `order_info`;
-- user_info增加一列
ALTER TABLE user_info ADD COLUMN `oid` BIGINT(20);
UPDATE user_info u , order_info o SET u.oid = o.user_id WHERE o.user_id = u.id ;
ALTER TABLE order_info ADD COLUMN `description` VARCHAR(255) ;
-- Index Prefixes
CREATE TABLE test(blob_col BLOB);
SHOW VARIABLES LIKE 'InnoDB_large_prefix';
SET GLOBAL InnoDB_large_prefix=off;
ALTER TABLE test ADD INDEX idx_blob_col(blob_col(7600));
--
CREATE TABLE t1 (
i1 INT NOT NULL DEFAULT 0,
i2 INT NOT NULL DEFAULT 0,
d DATE DEFAULT NULL,
PRIMARY KEY (i1, i2),
INDEX k_d (d)
) ENGINE = INNODB;
INSERT INTO t1 VALUES
(1, 1, '1998-01-01'), (1, 2, '1999-01-01'),
(1, 3, '2000-01-01'), (1, 4, '2001-01-01'),
(1, 5, '2002-01-01'), (2, 1, '1998-01-01'),
(2, 2, '1999-01-01'), (2, 3, '2000-01-01'),
(2, 4, '2001-01-01'), (2, 5, '2002-01-01'),
(3, 1, '1998-01-01'), (3, 2, '1999-01-01'),
(3, 3, '2000-01-01'), (3, 4, '2001-01-01'),
(3, 5, '2002-01-01'), (4, 1, '1998-01-01'),
(4, 2, '1999-01-01'), (4, 3, '2000-01-01'),
(4, 4, '2001-01-01'), (4, 5, '2002-01-01'),
(5, 1, '1998-01-01'), (5, 2, '1999-01-01'),
(5, 3, '2000-01-01'), (5, 4, '2001-01-01'),
(5, 5, '2002-01-01');
优化存储引擎
InnoDB
InnoDB 是 MySQL 客户通常在生产数据库中使用的存储引擎,其可靠性和并发性非常重要。是 MySQL 中的默认存储引擎。
-
Optimizing Storage Layout for InnoDB Tables
-
一旦你的数据达到一个稳定的大小,或者一个正在增长的表已经增加了几十兆或几百兆字节,考虑使用 OPTIMIZE TABLE 语句重新组织表并压缩任何浪费的空间。重新组织的表执行全表扫描需要较少的磁盘 i/o。这是一种简单的技术,可以在其他技术(如改进索引使用或调优应用程序代码)不实用时提高性能。
SHOW VARIABLES LIKE 'innodb_file_per_table'; SHOW TABLE STATUS LIKE 'order_info'; ALTER TABLE order_info ENGINE=INNODB; ANALYZE TABLE order_info; SHOW TABLE STATUS LIKE 'order_info';
在 InnoDB 中,拥有一个长的 primarykey (一个具有长值的列,或者几个组成长合成值的列)会浪费大量磁盘空间。行的主键值在指向同一行的所有辅助索引记录中重复。
使用 VARCHAR 数据类型而不是 CHAR 来存储可变长度的字符串或具有许多 NULL 值的列。CHAR (n)列总是使用 n 个字符来存储数据,即使字符串比较短或其值为 NULL。较小的表更适合buffer pool缓冲池并减少磁盘 i/o。当使用 COMPACT 行格式(默认的 InnoDB 格式)和可变长度字符集(如 utf8或 sjis)时,CHAR (n)列占用了可变的空间,但仍然至少有 n 个字节。
-
对于较大的表,或者包含大量重复的文本或数值数据的表,可以考虑使用 COMPRESSED 行格式。将数据带入buffer pool缓冲池或执行全表扫描所需的磁盘 i/o 较少。
SHOW CREATE TABLE t1; ALTER TABLE t1 ROW_FORMAT=COMPRESSED; SHOW TABLE STATUS LIKE 't1'; -- 查看表的详细信息
-
-
Optimizing InnoDB Transaction Management
要优化 InnoDB 事务处理,需要在事务特性的性能开销和服务器的工作负载之间找到理想的平衡点。例如,如果应用程序每秒提交数千次,则可能会遇到性能问题; 如果应用程序每2-3小时提交一次,则可能会遇到不同的性能问题。
对于只包含单个 SELECT 语句的事务,打开 AUTOCOMMIT 可以帮助 InnoDB 识别只读事务并对其进行优化。
-
避免在插入、更新或删除大量行之后执行回滚。如果一个大事务降低了服务器的性能,回滚它会使问题变得更糟,可能需要几倍于原始数据更改操作的时间来执行。关闭数据库进程无济于事,因为回滚将在服务器启动时重新启动。为了尽量减少发生这一问题的机会,可以进行如下操作:
- 增加缓冲池的大小,以便可以缓存所有数据更改,而不是立即写入磁盘。
- 设置 innodb _ change _ buffering = all,这样除了插入之外,更新和删除操作也可以被缓冲。
- 考虑在大数据更改操作期间定期发出 COMMIT 语句,可能会将单个删除或更新分解为多个语句,这些语句对较小的行数进行操作。
- 如果您能够承受在意外退出时丢失一些最新提交的事务,那么可以将 innodb_flush_log_at_trx_commit 参数设置为0。innodb_flush_log_at_trx_commit 参数值的选择:
- 0: 由mysql的main_thread每秒将存储引擎log buffer中的redo日志写入到log file,并调用文件系统的sync操作,将日志刷新到磁盘。
- 1:每次事务提交时,将存储引擎log buffer中的redo日志写入到log file,并调用文件系统的sync操作,将日志刷新到磁盘。
- 2:每次事务提交时,将存储引擎log buffer中的redo日志写入到log file,并由存储引擎的main_thread 每秒将日志刷新到磁盘。
-
Optimizing InnoDB Read-Only Transactions
InnoDB 可以避免与为已知为只读的事务设置事务 ID (TRX _ ID 字段)相关的开销。只有可能执行写操作或锁定读取(如 SELECT.forupdate)的事务才需要事务 ID。消除不必要的事务 id 可以减少内部数据结构的大小,每次查询或数据更改语句构造读视图时都要查询这些内部数据结构。
START TRANSACTION [transaction_characteristic [, transaction_characteristic] ...] transaction_characteristic: { WITH CONSISTENT SNAPSHOT | READ WRITE | READ ONLY } BEGIN [WORK] COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE] ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE] SET autocommit = {0 | 1}
-
Optimizing InnoDB Redo Logging
使您的重做日志文件大,甚至与缓冲池一样大。当 InnoDB 写满了重做日志文件时,它必须将修改后的缓冲池内容写入一个检查点中的磁盘。小的重做日志文件会导致许多不必要的磁盘写操作。尽管过去大的重做日志文件会导致冗长的恢复时间,但现在恢复速度快得多,您可以自信地使用大的重做日志文件。
考虑增加日志缓冲区的大小。大型日志缓冲区使大型事务能够在事务提交之前运行,而无需将日志写入磁盘。因此,如果您的事务更新、插入或删除了许多行,那么使用 innodb_Log_buffer_size 配置选项使日志缓冲区变大可以节省磁盘 i/o 日志缓冲区大小。
-
Bulk Data Loading for InnoDB Tables InnoDB
当将数据导入 InnoDB 时,关闭自动提交模式,因为它对每次插入执行日志刷新到磁盘。若要在导入操作期间禁用自动提交,请使用 SET autocommit 和 COMMIT 语句将其包围,如下所示,Mysqldump 选项 -- opt 创建转储文件,这些文件可以快速导入 InnoDB 表,甚至不需要用 SET autocommit 和 COMMIT 语句包装它们。
SET autocommit=0; ... SQL import statements ... COMMIT;
如果在辅助键上有 UNIQUE 约束,可以通过在导入会话期间临时关闭唯一性检查来加速表导入,对于大型表,这可以节省大量的磁盘 i/o,因为 InnoDB 可以使用其更改缓冲区来批量写入辅助索引记录。请确保数据中不包含重复的键。
SET unique_checks=0; ... SQL import statements ... SET unique_checks=1;
如果表中有 foreignkey 约束,可以在导入会话期间关闭外键检查,从而加快表导入,对于大型表,这可以节省大量磁盘 i/o。
SET foreign_key_checks=0; ... SQL import statements ... SET foreign_key_checks=1;
MyISAM
MyISAM 存储引擎在处理主要为读取的数据或低并发操作时性能最好,因为表锁限制了执行同步更新的能力。在 MySQL 中,InnoDB 是默认的存储引擎,而不是 MyISAM。
-
Optimizing MyISAM Queries
为了帮助 MySQL 更好地优化查询,可以使用 ANALYZE TABLE 或运行 myisamchk ——在数据加载后对表进行分析。
尽量避免在经常更新的 MyISAM 表上进行复杂的 SELECT 查询,以避免由于读取器和编写器之间的争用而出现表锁定问题。
对于经常更改的 MyISAM 表,尽量避免使用所有可变长度的列(VARCHAR、 BLOB 和 TEXT)。如果表甚至包含一个可变长度的列,则使用动态行格式。
定期使用 OPTIMIZE TABLE 来避免动态格式化 MyISAM 表的碎片化。
为了提高 MyISAM 表的性能,对于 LOAD DATA 和 INSERT,可以通过增加key_buffer_size 系统变量来扩大键缓存。
MEMORY
对于经常访问、只读或很少更新的非关键数据,请考虑使用 MEMORY 表。
参考
Optimizing