初识 MySQL 5.6 新特性、功能
背景:
之前介绍过 MySQL 5.5 新功能、参数,现在要用MySQL5.6,所以就学习和了解下MySQL5.6新的特性和功能,尽量避免踩坑。在后续的学习过程中文章也会不定时更新。
一:参数默认值的改变。
Table Changes to Server Defaults in MySQL 5.6
其中比较重要的参数:
表空间增长大小:innodb_autoextend_increment
是否独享表空间:innodb_file_per_table
查询information_schema元数据库里的表时,Innodb还会随机提取其他数据库每个表索引页的部分数据:innodb_stats_on_metadata
主从复制时候的刷写:sync_master_info、sync_relay_log、sync_relay_log_info
二 功能提升:
1)字段类型Timestamp,datetime的改变:时间类型字段,4个字节。更多的信息见官方文档。可以为表设置多个Timestamp的特性,且datetime类型支持DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP。 参考:MySQL5.6中TIMESTAMP的变化
时间戳。范围是'1970-01-01 00:00:00'到2037年。 TIMESTAMP列用于INSERT或UPDATE操作时记录日期和时间。如果你不分配一个值,表中的第一个TIMESTAMP列自动设置为最近操作的日期和时间。也可以通过分配一个NULL值,将TIMESTAMP列设置为当前的日期和时间。 TIMESTAMP值返回后显示为'YYYY-MM-DD HH:MM:SS'格式的字符串,显示宽度固定为19个字符。如果想要获得数字值,应在TIMESTAMP 列添加+0。
在MySQL 5.6.6之前,TIMESTAMP的默认行为:
- TIMESTAMP列如果没有明确声明NULL属性,默认为NOT NULL。(而其他数据类型,如果没有显示声明为NOT NULL,则允许NULL值。)设置TIMESTAMP的列值为NULL,会自动存储为当前timestamp。
- 表中的第一个TIMESTAMP列,如果没有声明NULL属性、DEFAULT或者 ON UPDATE,会自动分配 DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP 属性。
- 表中第二个TIMESTAMP列,如果没有声明为NULL或者DEFAULT子句,默认自动分配’0000-00-00 00:00:00′。插入行时没有指明改列的值,该列默认分配’0000-00-00 00:00:00′,且没有警告。
Timestamp在5.5和5.6中的表现:5.6取消了一张表只有一个timestamp特性的限制。
MySQL 5.5: zjy@192.168.210.245 : test 05:58:13>create table tmp_a(id int,a timestamp,b timestamp,c timestamp)engine = innodb default charset utf8; Query OK, 0 rows affected (0.33 sec) zjy@192.168.210.245 : test 05:58:50>show create table tmp_a\G; *************************** 1. row *************************** Table: tmp_a Create Table: CREATE TABLE `tmp_a` ( `id` int(11) DEFAULT NULL, `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `b` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `c` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.01 sec) #已经有了一个timestamp熟悉,再创建一个。 zjy@192.168.210.245 : test 05:58:57>alter table tmp_a modify b timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; ERROR 1293 (HY000): Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause #失败 MySQL 5.6: mysql> create table tmp_a(id int,a timestamp,b timestamp,c timestamp)engine = innodb default charset utf8; Query OK, 0 rows affected (0.02 sec) mysql> show create table tmp_a\G; *************************** 1. row *************************** Table: tmp_a Create Table: CREATE TABLE `tmp_a` ( `id` int(11) DEFAULT NULL, `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `b` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `c` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) #已经有了一个timestamp熟悉,再创建一个。 mysql> alter table tmp_a modify b timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> alter table tmp_a modify c timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 #成功
datetime 类型改进:支持DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAM。
mysql> show create table tmp_time\G; *************************** 1. row *************************** Table: tmp_time Create Table: CREATE TABLE `tmp_time` ( `id` int(11) DEFAULT NULL, `day` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `cc` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=latin1 mysql> select * from tmp_time; +------+---------------------+---------------------+ | id | day | cc | +------+---------------------+---------------------+ | 1 | 2015-08-21 15:50:12 | 2015-08-21 15:50:12 | | 2 | 2015-08-21 15:50:12 | 2015-08-21 15:50:12 | +------+---------------------+---------------------+
特别注意:在MySQL5.6里,特别为timestamp新增了一个参数:explicit_defaults_for_timestamp。该参数默认是关闭的,所以在启动的时候,错误日志里面会出现:
[Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
上面5.6的测试都是在默认情况下进行的,下面开启这个参数试试,在my.cnf里的mysqld选项组里添加:
explicit_defaults_for_timestamp = 1
测试参数:
mysql> create table tmp_a(id int,a timestamp,b timestamp,c timestamp not null,d timestamp)engine = innodb default charset utf8; Query OK, 0 rows affected (0.02 sec) mysql> show create table tmp_a\G; *************************** 1. row *************************** Table: tmp_a Create Table: CREATE TABLE `tmp_a` ( `id` int(11) DEFAULT NULL, `a` timestamp NULL DEFAULT NULL, #开启参数之后,timestamp可以为NULL了。 `b` timestamp NULL DEFAULT NULL, `c` timestamp NOT NULL, `d` timestamp NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) #开启参数也一样可以支持多个timestamp特性 mysql> alter table tmp_a modify a timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> alter table tmp_a modify b timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> insert into tmp_a(id) values(1),(2); Query OK, 2 rows affected, 1 warning (0.00 sec) Records: 2 Duplicates: 0 Warnings: 1 mysql> show warnings; +---------+------+----------------------------------------+ | Level | Code | Message | +---------+------+----------------------------------------+ | Warning | 1364 | Field 'c' doesn't have a default value | +---------+------+----------------------------------------+ 1 row in set (0.00 sec) mysql> show create table tmp_a\G; *************************** 1. row *************************** Table: tmp_a Create Table: CREATE TABLE `tmp_a` ( `id` int(11) DEFAULT NULL, `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `b` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `c` timestamp NOT NULL, #不为null的值且没有默认值,写入会报warning,用“0000-00-00 00:00:00”填充。 `d` timestamp NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) mysql> select * from tmp_a; +------+---------------------+---------------------+---------------------+------+ | id | a | b | c | d | +------+---------------------+---------------------+---------------------+------+ | 1 | 2015-08-20 06:21:53 | 2015-08-20 06:21:53 | 0000-00-00 00:00:00 | NULL | | 2 | 2015-08-20 06:21:53 | 2015-08-20 06:21:53 | 0000-00-00 00:00:00 | NULL | +------+---------------------+---------------------+---------------------+------+ 2 rows in set (0.01 sec)
所以MySQL5.6开启该参数之后,timestamp字段类型的行为是:
- TIMESTAMP如果没有显示声明NOT NULL,是允许NULL值的,可以直接设置改列为NULL,而没有默认填充行为。
- TIMESTAMP不会默认分配DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP属性。
- 声明为NOT NULL且没有默认子句的TIMESTAMP列是没有默认值的。往数据表中插入列,又没有给TIMESTAMP列赋值时,如果是严格SQL模式,会抛出一个错误,如果严格SQL模式没有启用,该列会赋值为’0000-00-00 00:00:00′,同时出现一个警告。(这和MySQL处理其他时间类型数据一样,如DATETIME)
2)InnoDB的增强
① 全文索引的支持(Fulltext index):MySQL5.6中内置了对Innodb的全文索引支持。5.6.4之前版本的MySQL只有MyISAM支持全文搜索。对于全文索引的概念和使用方法可以自行在网上搜索,比如MySQL必知必会第18章。
全文搜索的基础是FULLTEXT索引,在MySQL5.6.4以后的版本中可以在Innodb,MyISAM数据表里的CHAR,VARCHAR和TEXT字段创建。 全文搜索在MyISAM表中,将忽略至少在一半数据行中出现的单词(Innodb没有此限制)。 全文搜索会忽略一些常见单词,如“a”,“the”等,这些单词被成为“停止词”。(可以修改停止词) FULLTEXT索引可以为一个或多个数据列创建。如果搜索涉及多个数据列,必须为多个数据索引。FULLTEXT(title,address)和FULLTEXT(address,title)效果是一样的,搜索时使用顺序也是不重要的。
mysql> show variables like '%ft%'; +---------------------------------+----------------+ | Variable_name | Value | +---------------------------------+----------------+ | ft_boolean_syntax | + -><()~*:""&| | | ft_max_word_len | 84 | | ft_min_word_len | 4 | | ft_query_expansion_limit | 20 | | ft_stopword_file | (built-in) | | innodb_ft_aux_table | | | innodb_ft_cache_size | 8000000 | | innodb_ft_enable_diag_print | OFF | | innodb_ft_enable_stopword | ON | | innodb_ft_max_token_size | 84 | | innodb_ft_min_token_size | 3 | | innodb_ft_num_word_optimize | 2000 | | innodb_ft_result_cache_limit | 2000000000 | | innodb_ft_server_stopword_table | | | innodb_ft_sort_pll_degree | 2 | | innodb_ft_total_cache_size | 640000000 | | innodb_ft_user_stopword_table | | +---------------------------------+----------------+
现在来说明下MyISAM和InnoDB对全文索引的使用。
1、索引长度的设置:
#MyISAM ft_min_word_len = 1 ft_max_word_len =84 #InnoDB innodb_ft_min_token_size= 1 innodb_ft_max_token_size=84
2、停止词的设置(stopword):
#MyISAM 停止词存放在文件 ft_stopword_file = /var/lib/mysql/stopword.txt #InnoDB,停止词存放在表 innodb_ft_server_stopword_table = dbname/ft_stopwords innodb_ft_user_stopword_table = dbname/ft_stopwords
3、测试
mysql> show create table tmp_mfulltext\G; *************************** 1. row *************************** Table: tmp_mfulltext Create Table: CREATE TABLE `tmp_mfulltext` ( `id` int(11) DEFAULT NULL, `name` varchar(30) DEFAULT NULL, `content` text, FULLTEXT KEY `idx_content` (`content`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 mysql> show create table tmp_ifulltext\G; *************************** 1. row *************************** Table: tmp_ifulltext Create Table: CREATE TABLE `tmp_ifulltext` ( `id` int(11) DEFAULT NULL, `name` varchar(30) DEFAULT NULL, `content` text, FULLTEXT KEY `idx_content` (`content`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 mysql> select * from tmp_mfulltext where MATCH(content) AGAINST('Auxiliary'); #正常 +------+------+---------------------------------------------------------------------------------+ | id | name | content | +------+------+---------------------------------------------------------------------------------+ | 2 | jin | Auxiliary index table names are prefixed with FTS_ and postfixed with INDEX_*. | +------+------+---------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> select * from tmp_ifulltext where MATCH(content) AGAINST('Auxiliary'); #正常 +------+------+---------------------------------------------------------------------------------+ | id | name | content | +------+------+---------------------------------------------------------------------------------+ | 2 | jin | Auxiliary index table names are prefixed with FTS_ and postfixed with INDEX_*. | +------+------+---------------------------------------------------------------------------------+ 1 row in set (0.01 sec) mysql> select * from tmp_mfulltext where MATCH(content) AGAINST('when'); #搜索长度大于ft_min_word_len,但是没有找到结果。 Empty set (0.00 sec) mysql> select * from tmp_ifulltext where MATCH(content) AGAINST('when'); #搜索长度大于innodb_ft_min_token_size,但是没有找到结果。 Empty set (0.00 sec) 原因可能就停止词(stopword)的问题,之前介绍过:MySQL全文检索Stopwords的设置 手动指定停止词后: mysql> repair table tmp_mfulltext; #需要修复下才能进行match +------------------------+--------+----------+----------+ | Table | Op | Msg_type | Msg_text | +------------------------+--------+----------+----------+ | dba_test.tmp_mfulltext | repair | status | OK | +------------------------+--------+----------+----------+ 1 row in set (0.00 sec) mysql> select count(*) from tmp_mfulltext where MATCH(content) AGAINST('when');#停止词的问题,指定好之后有结果。 +----------+ | count(*) | +----------+ | 1 | +----------+ 1 row in set (0.00 sec) mysql> select * from tmp_ifulltext where MATCH(content) AGAINST('when'); #没有结果,这里说明Innodb的全部文索停止词和MyISAM的不一样。 Empty set (0.01 sec)
关于InnoDB fulltext的停止词相关的设置,默认的停止词表是:
mysql> select * from INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD; 36 rows in set (0.00 sec)
手动设置停止表:
#因为InnoDB的停止词是以表形式存的,先建立停止词表:停止词表必须是InnoDB表,只包含一个VARCHAR类型名为VALUE的列 mysql> create table innodb_ft_stopwords(value varchar(30))ENGINE=INNODB; #结构和INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD一样 Query OK, 0 rows affected (0.01 sec) #插入停止词: mysql> INSERT INTO innodb_ft_stopwords(value) VALUES('a'); Query OK, 1 row affected (0.00 sec) #加载停止词表,注意格式 mysql> SET GLOBAL innodb_ft_server_stopword_table = 'dba_test/innodb_ft_stopwords'; Query OK, 0 rows affected (0.00 sec) mysql> SET GLOBAL innodb_ft_user_stopword_table = 'dba_test/innodb_ft_stopwords'; Query OK, 0 rows affected (0.00 sec) #因为fulltext的创建是在加载停止词之后的,之前建立的fulltext找不到结果。 mysql> select * from tmp_ifulltext where MATCH(content) AGAINST('when'); Empty set (0.00 sec) #optimize table 之后可以 mysql> optimize table tmp_ifulltext; +------------------------+----------+----------+-------------------------------------------------------------------+ | Table | Op | Msg_type | Msg_text | +------------------------+----------+----------+-------------------------------------------------------------------+ | dba_test.tmp_ifulltext | optimize | note | Table does not support optimize, doing recreate + analyze instead | | dba_test.tmp_ifulltext | optimize | status | OK | +------------------------+----------+----------+-------------------------------------------------------------------+ 2 rows in set (0.07 sec) #搜索出结果 mysql> select count(*) from tmp_ifulltext where MATCH(content) AGAINST('when'); +----------+ | count(*) | +----------+ | 1 | +----------+ 1 row in set (0.00 sec)
这里需要注意的是在创建全文索引之前就应先设置好停止词表,不然就得执行optimize。停止词表确定之后,请加入到配置文件中:
innodb_ft_server_stopword_table = dba_test/innodb_ft_stopwords innodb_ft_user_stopword_table = dba_test/innodb_ft_stopwords
Tips:
· 全文搜索在MyISAM表中,将忽略至少在一半数据行中出现的单词,而Innodb没有此限制:
mysql> select count(*) from tmp_ifulltext where MATCH(content) AGAINST('the'); +----------+ | count(*) | +----------+ | 6 | +----------+ 1 row in set (0.00 sec) mysql> select count(*) from tmp_mfulltext where MATCH(content) AGAINST('the'); +----------+ | count(*) | +----------+ | 0 | +----------+ 1 row in set (0.00 sec)
关于更多的信息请查看官方文档和MySQL5.6 InnoDB FULLTEXTIndexes研究测试。
#生成测试数据 mysql> SHOW VARIABLES LIKE 'innodb_file_per_table'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_file_per_table | ON | +-----------------------+-------+ 1 row in set (0.00 sec) mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) DATA DIRECTORY = '/tmp/mysqldata/'; #会在该目录下再生成一个以数据库命名的目录 Query OK, 0 rows affected (0.02 sec) mysql> insert into t1 values(1),(2),(3); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 #查看物理文件 root@zjy:~# ls -lh /var/lib/mysql3/dba_test/ -rw-rw---- 1 mysql mysql 8.4K 8月 21 00:44 t1.frm -rw-rw---- 1 mysql mysql 30 8月 21 00:44 t1.isl root@zjy:~# ls -lh /tmp/mysqldata/dba_test/ -rw-rw---- 1 mysql mysql 96K 8月 21 00:45 t1.ibd
上面被看到数据文件.ibd和表结构定义文件.frm已分离。注意:表空间分离表会产生一个
的连接。table_name
.isl
③ 导入和导出表空间:复制文件比mysqldump&restore快
#生成测试数据 server1: mysql> CREATE TABLE test (c1 INT PRIMARY KEY)engine = innodb; Query OK, 0 rows affected (0.02 sec) mysql> insert into test values(1),(2),(3); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 server2: mysql> CREATE TABLE test (c1 INT PRIMARY KEY)engine = innodb; Query OK, 0 rows affected (0.02 sec) mysql> insert into test values(1),(3); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 #接着把server2的表空间给discard掉: root@zjy:/var/lib/mysql/dba_test# ls -lh test.* -rw-rw---- 1 mysql mysql 8.4K 8月 21 01:37 test.frm -rw-rw---- 1 mysql mysql 96K 8月 21 01:37 test.ibd #discard 表空间 mysql> ALTER TABLE test DISCARD TABLESPACE; Query OK, 0 rows affected (0.00 sec) #表空间没了 root@zjy:/var/lib/mysql/dba_test# ls -lh test.* -rw-rw---- 1 mysql mysql 8.4K 8月 21 01:37 test.frm #然后把server1的表空间复制给server2: mysql> FLUSH TABLES test FOR EXPORT; #必须要生成cfg metadata文件 Query OK, 0 rows affected (0.00 sec) root@zjy:/var/lib/mysql3/dba_test# ls -lh test.* -rw-rw---- 1 mysql mysql 330 8月 21 01:40 test.cfg -rw-rw---- 1 mysql mysql 8.4K 8月 21 01:36 test.frm -rw-rw---- 1 mysql mysql 96K 8月 21 01:36 test.ibd #需要把ibd和cfg文件复制到server2上 root@zjy:/var/lib/mysql3/dba_test# cp test.cfg /var/lib/mysql/dba_test/ root@zjy:/var/lib/mysql3/dba_test# cp test.ibd /var/lib/mysql/dba_test/ #最后修改server2文件权限和import表空间: root@zjy:/var/lib/mysql/dba_test# chown -R mysql.mysql * mysql> ALTER TABLE test IMPORT TABLESPACE; Query OK, 0 rows affected (0.02 sec) #值从1,3变成了1,2,3。数据已成功迁移过来 mysql> select * from test; +----+ | c1 | +----+ | 1 | | 2 | | 3 | +----+ #最后解锁server1上的锁: mysql> unlock tables;
3)innodb内部性能增强:
① 将flushing操作独立出主线程,减少核心互斥锁。可设置多个清除线程,减少内存资源争夺。回收UNDO,通过innodb_purge_threads 来开启,在5.5中只能也只有1可以设置。5.6可以设置大于1。刷写脏页,5.6之后支持,Page cleaner线程,从Master Thread独立出来,减轻了Master Thread 的工作和对用户查询的阻塞。进一步提高Innodb 存储引擎的性能和并发。
② 检测死锁算法增强。在非递归情况下死锁检测:死锁信息不止可以通过show engine innodb status一种方法查看,还可以记录到 error 日志,并且可以查看历史的死锁,方便分析。开启参数:innodb_print_all_deadlocks,默认关闭。
如果一台高负荷的机器重启后,buffer pool中的热数据被丢失,此时就会重新从磁盘加载到Buffer_Pool缓冲池里,这样当高峰期间,性能就会变得很差,连接数就会很高,应用的性能也受到影响。MySQL5.6里,一个新特性避免的这种问题的出现,在配置文件里添加:
#在关闭时把热数据dump到本地磁盘 #5.7.7默认开启 innodb_buffer_pool_dump_at_shutdown = 1 #在启动时把热数据加载到内存 #5.7.7默认开启 innodb_buffer_pool_load_at_startup = 1 #采用手工方式把热数据dump到本地磁盘 innodb_buffer_pool_dump_now = 1 #采用手工方式把热数据加载到内存 innodb_buffer_pool_load_now = 1
参数:innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup 是数据库关闭开启时候起作用的:
当数据库正常关闭/重启之后,错误日志里面会记录:会生一个ib_buffer_pool的文件,这个文件名字有参数 innodb_buffer_pool_filename 控制。
2015-08-21 19:55:07 7fe34dff8700 InnoDB: Dumping buffer pool(s) to .//ib_buffer_pool 2015-08-21 19:55:07 7fe34dff8700 InnoDB: Buffer pool(s) dump completed at 150821 19:55:07 2015-08-21 19:55:08 31943 [Note] InnoDB: Shutdown completed; log sequence number 3052155687
当数据库重新起来之后,错误日志里会记录:
2015-08-21 19:55:59 7f5eaf7f8700 InnoDB: Buffer pool(s) load completed at 150821 19:55:59
参数:innodb_buffer_pool_dump_now和innodb_buffer_pool_load_now 是需要手动执行的:
当数据库正在运行时执行:
查看BP里dump文件的时间: root@zjy:/var/lib/mysql# ls -lh ib_buffer_pool -rw-rw---- 1 mysql mysql 52K 8月 25 18:30 ib_buffer_pool 执行: mysql> SET GLOBAL innodb_buffer_pool_dump_now=ON; Query OK, 0 rows affected (0.01 sec) 查看BP里dump文件的时间:时间变成当前时间,说明dump成功。 root@zjy:/var/lib/mysql# ls -lh ib_buffer_pool -rw-rw---- 1 mysql mysql 52K 8月 25 19:02 ib_buffer_pool load dump文件到BP: mysql> SET GLOBAL innodb_buffer_pool_load_now=ON; Query OK, 0 rows affected (0.01 sec)
显示BP导入、导出的状态:重启数据库虽然在日志文件里面可以查看到dump和load的一些信息,但在运行时,也可以监控是否成功:
#监控查看BP dump的状态:Innodb_buffer_pool_dump_status,手动执行BP的dump动作 mysql> select now();SET GLOBAL innodb_buffer_pool_dump_now=ON;SHOW STATUS LIKE 'Innodb_buffer_pool_dump_status'; +---------------------+ | now() | +---------------------+ | 2015-08-25 19:08:49 | +---------------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) +--------------------------------+--------------------------------------------------+ | Variable_name | Value | +--------------------------------+--------------------------------------------------+ | Innodb_buffer_pool_dump_status | Buffer pool(s) dump completed at 150825 19:08:49 | #和dump执行的时间一致,说明dump成功,是上一个dump的结果 +--------------------------------+--------------------------------------------------+ 1 row in set (0.00 sec) #监控查看BP load的状态:Innodb_buffer_pool_load_status,手动执行load dump文件动作 mysql> select now();SET GLOBAL innodb_buffer_pool_load_now=ON;SHOW STATUS LIKE 'Innodb_buffer_pool_load_status'; +---------------------+ | now() | +---------------------+ | 2015-08-25 19:10:21 | +---------------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) +--------------------------------+--------------------------------------------------+ | Variable_name | Value | +--------------------------------+--------------------------------------------------+ | Innodb_buffer_pool_load_status | Buffer pool(s) load completed at 150825 19:10:21 | #和load执行的时间一致,说明load成功,是还原上一个dump文件 +--------------------------------+--------------------------------------------------+ 1 row in set (0.00 sec)
通过show status 查看之外,还可以通过在information_schema里查看:
dump: SELECT variable_value FROM information_schema.global_status WHERE variable_name = 'INNODB_BUFFER_POOL_DUMP_STATUS'; load: SELECT variable_value FROM information_schema.global_status WHERE variable_name = 'INNODB_BUFFER_POOL_LOAD_STATUS';
此外,也可以中止BP的load,参数:innodb_buffer_pool_load_abort,中止缓冲池加载操作。
#在MySQL里运行: mysql> set global innodb_buffer_pool_load_abort = on; Query OK, 0 rows affected (0.00 sec) #也可以在配置文件里添加: innodb_buffer_pool_load_abort = 1
另外,innodb_buffer_pool_dump_pct控制备份buffer pool的百分比,5.7中支持。5.6默认dump所有的Buffer pool。
总之,引入看BP的自动预热解决了数据库维护重启的时候导致性能变差的问题。
注意:只有在正常关闭MySQL服务,或者pkill mysql时,会把热数据dump到内存。机器宕机或者pkill -9 mysql,是不会dump。
④ 新增参数innodb_page_size设置页大小。指定在一个MySQL实例的所有InnoDB表空间的页大小。该值是在创建实例时设置的,并且在随后的情况中保持不变。你可以使用16K指定页面大小(默认),8K,或4K。或者,您可以指定字节的页大小(4096,8192,16384)。16K页面适合大部分场景,特别是表扫描、DML操作和批量更新;较小的页面可能更是适合OLTP和SSD存储设备。
⑤ undo log可独立出系统表空间,原来存在ibd系统表空间里。undo log分离到独立的表空间,并放到单独的文件目录下。对于并发写入型负载,我们可以把undo文件部署到单独的高速存储设备上。缺点就是不能进行回收(收缩)空间大小。直到MySQL5.7 ,才支持在线收缩。由参数innodb_undo_tablespaces和innodb_undo_directory控制:需要在数据库实例建立之后就开启,否则会初始化innodb 失败,开启之后不能修改。具体信息可以看这篇文章
在配置文件里设置: innodb_undo_directory = /var/lib/mysql4/undolog/ #undo log 存放的目录 innodb_undo_tablespaces = 10 #默认表空间的个数,大小默认是10M,以undo001形式存在,值为0则表示使用系统表空间。
另外一个参数是:inodb_undo_logs:代替了先前的参数innodb_rollback_segments,控制着回滚段的数量(注意范围是0-128) 默认不指定的时候是128个回 滚段。(注意要想增加回滚段的时候必须要重启mysql)。
要是开启的时候遇到innodb数据初始化失败:Plugin 'InnoDB' init function returned error。则需要先把数据dump出来,删除掉ibdata、ib_logfile等文件,再开启。
⑥ 优化器统计持续化:重启不丢失。缓冲池flush算法增强、支持read-only事务、redo log最大增至512G。
⑦ 支持使用enosql api访问innodb表、redo log最大增至512G、INFORMATION_SCHEMA新增相关字典表若干。 InnoDB更多的改进:http://dev.mysql.com/doc/refman/5.6/en/optimizing-innodb.html
4)复制、日志功能增强
① binlog_row_image 参数控制Row模式下的binlog记录格式。在MySQL5.6只之前,Row模式记录所有列,即使是修改一个列。这样就存在一个问题,如果表中包含很多的大字段,表的单行长度就会非常长,这样每次update就会导致大量的 binlog空间生成,具体的可以看这里。该参数可以控制ROW下的记录格式,可以动态修改,3个值可以设置:
full:默认行为,记录所有的列,和MySQL5.6之前的版本一样。 minimal:记录被修改的列,其他未修改的列不记录。 noblob :记录所有的列,除text、blob列外。
测试:
BINLOG: binlog_format = ROW binlog_row_image = minimal SQL: update doctors set username ='XXXYYYZZZ',cellphone='110110110110',modifyTime=now() where id = 101; LOG: #记录了纸杯修改的三个列 ### UPDATE `dba_test`.`doctors` ### WHERE ### @1=101 /* INT meta=0 nullable=0 is_null=0 */ ### SET ### @4='XXXYYYZZZ' /* VARSTRING(192) meta=192 nullable=0 is_null=0 */ ### @13='110110110110' /* VARSTRING(48) meta=48 nullable=1 is_null=0 */ ### @19='2015-08-28 11:20:09' /* DATETIME(0) meta=0 nullable=1 is_null=0 */ 其他2个可以自行测试。
所以在5.6中binlog_row_image设置为minimal,这样就可以大大减小了binlog的长度,进而减少了空间的使用。但不能通过脚本实现回滚了。
② log_bin_basename、log_bin_index
、relay_log_basename,显示binlog和relay log 日志名字,直接用log_bin指定。5.5和5.6的区别如下:
mysql> show global variables like 'log%'; +---------------------------------+-------------------------------+ | Variable_name | Value | +---------------------------------+-------------------------------+ | log_bin | ON | #看不到,需要到配置文件、目录里找。 +---------------------------------+-------------------------------+ mysql> show global variables like 'log%'; +----------------------------------------+------------------------------------+ | Variable_name | Value | +----------------------------------------+------------------------------------+ | log_bin | ON | | log_bin_basename | /var/lib/mysql/mysql-bin_06 | #可以看到名称和路径,即log_bin配置。 | log_bin_index | /var/lib/mysql/mysqld-bin_06.index | #索引文件 +----------------------------------------+------------------------------------+ zjy@192.168.100.2 : analyze_data 05:12:57>show variables like 'relay%'; +-----------------------+-----------------------+ | Variable_name | Value | +-----------------------+-----------------------+ | relay_log | mysqld-relay-bin-3306 | #同上 | relay_log_index | | +-----------------------+-----------------------+ mysql> show global variables like 'relay%'; +---------------------------+-------------------------------------------+ | Variable_name | Value | +---------------------------+-------------------------------------------+ | relay_log | mysqld-relay-bin3306 | | relay_log_basename | /var/lib/mysql/mysqld-relay-bin3306 | #同上 | relay_log_index | /var/lib/mysql/mysqld-relay-bin3306.index | +---------------------------+-------------------------------------------+
③ master.info和relay-log.info支持存储在表中,通过参数:master-info-repository、relay-log-info-repository控制,默认是存文件(file):
mysql> show variables like '%info%'; +---------------------------+----------------+ | Variable_name | Value | +---------------------------+----------------+ | master_info_repository | FILE | | relay_log_info_repository | FILE | +---------------------------+----------------+ 在配置文件里添加: master_info_repository = table relay_log_info_repository = table mysql> show variables like '%info%'; +---------------------------+----------------+ | Variable_name | Value | +---------------------------+----------------+ | master_info_repository | TABLE | | relay_log_info_repository | TABLE | +---------------------------+----------------+
这里需要知道的是,设置成Table之后,表存储在mysql数据库中。对应的表名是:slave_master_info,slave_relay_log_info。
④ mysqlbinlog命令支持binlog备份。在5.6之前mysqlbinlog只是读取binglog日志,并解析出一个文本格式的内容。5.6增加了一个--raw参数,作用是读取binglog日志,并重新生成binlog格式的文件。其原理就是开启一个IO(dump)线程来拉取binlog日志,下面通过使用方法介绍下:
A Server: mysql> SHOW BINARY LOGS; +---------------------+-----------+ | Log_name | File_size | +---------------------+-----------+ | mysql-bin_06.000001 | 174 | | mysql-bin_06.000002 | 201 | | mysql-bin_06.000003 | 201 | | mysql-bin_06.000004 | 1864 | | mysql-bin_06.000005 | 1420 | | mysql-bin_06.000006 | 191 | +---------------------+-----------+ B Server: 备份1~6的二进制日志: 1:备份001和002的二进制日志,再备份其他的就继续指定binlog name mysqlbinlog --read-from-remote-server --user=zjy --password=123456 --host=127.0.0.1 --raw mysql-bin_06.000001 mysql-bin_06.000002 2:从000001开始备份所有的二进制日志: mysqlbinlog --read-from-remote-server --user=zjy --password=123456 --host=127.0.0.1 --raw --to-last-log mysql-bin_06.000001 3:从000001开始备份所有的二进制日志,命令行不退出,一直监控着日志的轮巡,一有新的event就马上备份。 mysqlbinlog --read-from-remote-server --user=zjy --password=123456 --host=127.0.0.1 --raw --stop-never mysql-bin_06.000001
4:--stop-never-slave-server-id=
。id
,指定binlog server的id,开启多个binlog server,可以用不同的server-id,默认是65535
mysqlbinlog --read-from-remote-server--stop-never-slave-server-id=1
--user=zjy --password=123456 --host=127.0.0.1 --raw --stop-never mysql-bin_06.000001
5:--result-file,指定一个文件目录。
mysqlbinlog --read-from-remote-server --user=zjy --password=123456 --host=127.0.0.1 --raw --stop-never mysql-bin_06.000001 --result-file=/home/zhoujy/
在Server B里面看到从Server A上备份过来的binlog日志,具体的使用方法请见官方文档和这篇说明。
⑤ 支持多线程复制:slave_parallel_workers:多线程复制是基于库的,一个线程也只针对一个数据库。注意:当开启多线程复制后,需要关闭slave_transaction_retries,设置其为0,否则会在start/stop slave中遇到:
slave_transaction_retries is not supported in multi-threaded slave mode. In the event of a transient failure, the slave will not retry the transaction and will stop.
5.6以前的从服务器,有一个io线程负责接收binary log,还有一个sql线程负责执行binary log中的sql语句。如果主服务器的数据更新相当频繁,而从服务器由于某些原因跟不上,会导致从服务器落后比较长的时间。5.6之后采用多个sql线程,每个sql线程处理不同的database,提高了并发性能,即使某database的某条语句暂时卡住,也不会影响到后续对其它的database进行操作。
要把多线程改成单线程,即 slave_parallel_workers 从非0改成0,则执行:
zjy@192.168.110.xx : (none) 02:28:49>stop slave; Query OK, 0 rows affected (0.30 sec) zjy@192.168.110.xx : (none) 02:28:55>START SLAVE UNTIL SQL_AFTER_MTS_GAPS; Query OK, 0 rows affected (0.02 sec) zjy@192.168.110.xx : (none) 02:29:07>SET @@GLOBAL.slave_parallel_workers = 0; Query OK, 0 rows affected (0.01 sec) zjy@192.168.110.xx : (none) 02:29:29>START SLAVE SQL_THREAD; Query OK, 0 rows affected (0.11 sec)
查看,只有一个线程:
更多的信息见官方文档。
⑥ 支持延迟复制,MASTER_DELAY。在change的时候指定,单位是秒。
CHANGE master TO MASTER_DELAY=600;
mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Master_Host: 127.0.0.1 Master_User: rep Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin3306.000006 Read_Master_Log_Pos: 23940536 ... ... SQL_Delay: 0 ...
⑦ 新增GTID复制,详细信息见:MySQL5.6 新特性之GTID。
⑧ binlog支持crash-safe:
binlog_checksum=CRC32 :事件写入二进制进行校验,默认CRC32。
master_verify_checksum =1 :检查二进制日志,bin log。默认禁止。
slave_sql_verify_checksum =1 :检查中继日志,relay log。默认禁止。
⑨ thread_pool_size 的引入:在引入线程池之前,MySQL支持的线程处理方式(thread_handling参数控制)有no-threads和one-thread-per-connection两种方式,no-threads方式是指任一时刻最多只有一个连接可以连接到server,一般用于实验性质。 one-thread-per-connection是指针对每个连接创建一个线程来处理这个连接的所有请求,直到连接断开,线程 结束。是thread_handling的默认方式。one-thread-per-connection存在的问题就是需要为每个连接创建一个新的thread,当并发连接数达到一定程度,性能会有明显下降,因为过多的线程会导致频繁的上下文切换,CPU cache命中率降低和锁的竞争 更加激烈。解决one-thread-per-connection的方法就是降低线程数,这样就需要多个连接共用线程,这便引入了线程池的概念。
thread_handling = pool-of-threads
5)优化器增强 :可以看这里
mysql >show variables like 'optimizer_switch'\G; *************************** 1. row *************************** Variable_name: optimizer_switch Value: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on
① MRR优化(Multi-Range Read):mrr=on,mrr_cost_based=on 。MRR的优化用于range, ref, eq_ref, and Batched Key Access访问方法
Multi-Range Read 多范围读(MRR) 它的作用是基于辅助/第二索引的查询,减少随机IO,并且将随机IO转化为顺序IO,提高查询效率。在没有MRR之前(MySQL5.6之前),先根据where条件中的辅助索引获取辅助索引与主键的集合,再通过主键来获取对应的值。辅助索引获取的主键来访问表中的数据会导致随机的IO(辅助索引的存储顺序并非与主键的顺序一致),不同主键不在同一个page里面时必然导致多次IO 和随机读。使用MRR优化(MySQL5.6之后),先根据where条件中的辅助索引获取辅助索引与主键的集合,再将结果集放在buffer里面(read_rnd_buffer_size 大小直到buffer满了),然后对结果集按照pk_column排序,得到有序的结果集rest_sort。最后利用已经排序过的结果集,访问表中的数据,此时是顺序IO。即MySQL 将根据辅助索引获取的结果集根据主键进行排序,将无序化为有序,可以用主键顺序访问基表,将随机读转化为顺序读,多页数据记录可一次性读入或根据此次的主键范围分次读入,以减少IO操作,提高查询效率。
具体参考:http://blog.itpub.net/22664653/viewspace-1673682/
② ICP优化(Index Condition Pushdown):index_condition_pushdown=on。ICP的优化用于range, ref, eq_ref, and ref_or_null访问方法
Index Condition Pushdown (ICP)是MySQL用索引去表里取数据的一种优化。禁用ICP(MySQL5.6之前),引擎层会利用索引在基表中寻找数据行,然后返回给MySQL Server层,再去为这些数据行进行WHERE后的条件的过滤(回表)。启用ICP(MySQL5.6之后),如果部分WHERE条件能使用索引中的字段,MySQL会把这部分下推到引擎层。存储引擎通过使用索引把满足的行从表中读取出。ICP减少了引擎层访问基表的次数和MySQL Server 访问存储引擎的次数。总之是 ICP的优化在引擎层就能够过滤掉大量的数据,减少io次数,提高查询语句性能。
具体参考:http://blog.itpub.net/22664653/viewspace-1678779/和http://www.cnblogs.com/zhoujinyi/archive/2013/04/16/3016223.html
③ Limit优化 :
SELECT … FROM single_table … ORDER BY non_index_column [DESC] LIMIT N [OFFSET M];
当有足够的内存(sort_buffer_size)来存储N+M行记录时, 会在内存中创建一个优先队列来存储数据,这意味着一次扫描表就可以获得想要的数据,避免了创建/写入临时表及归并排序操作(之前版本的逻辑)
大概逻辑为:
1.扫描表,将数据有序的插入到优先队列中,如果队列满了,则按顺序移除多余的记录
2.返回队列中的N行记录,如果指定了OFFSET M,则忽略开始的M条记录,然后返回剩下的N条
④ Index Extension优化 :use_index_extensions=on。
InnoDB的二级索引都包含主键信息,隐性的包含了主键列,可以通过在二级索引上添加主键列来查看(显性的添加主键列到二级索引,索引大小不变)。在MySQL5.6之前,MySQL优化器选择索引时不会考虑到这些隐性的主键列,一般都需要手动添加。而在MySQL5.6之后,优化器选择时,会考虑到二级索引上的primary key。
⑤ BKA优化(Batched Key Access):mrr=on,mrr_cost_based=off,batched_key_access=on.
提高表join性能的算法,在介绍BKA之前,先了解下Join表的优化历史:
Nested Loop Join算法:将驱动表/外部表的结果集作为循环基础数据,然后循环该结果集,每次获取一条数据作为下一个表的过滤条件查询数据,然后合并结果,获取结果集返回给客户端。Nested-Loop一次只将一行传入内层循环, 所以外层循环(的结果集)有多少行, 内存循环便要执行多少次,效率非常差。
Block Nested-Loop Join算法:将外层循环的行/结果集存入join buffer, 内层循环的每一行与整个buffer中的记录做比较,从而减少内层循环的次数。主要用于当被join的表上无索引。
Batched Key Access算法:当被join的表能够使用索引时,就先排好顺序,然后再去检索被join的表。对这些行按照索引字段进行排序,因此减少了随机IO。如果被Join的表上没有索引,则使用老版本的BNL策略(BLOCK Nested-loop)。BKA默认是关闭的,要使用BKA,必须调整系统参数optimizer_switch的值,batched_key_access设置为on,因为BKA使用了MRR,因此也要打开MRR,但是基于成本优化MRR算法不是特别准确官方文档推荐关闭mrr_cost_based,将其设置为off。
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on'
具体参考:http://blog.itpub.net/22664653/viewspace-1715511/
⑥ explain支持对insert、delete、update、replace语句,并且支持json格式的 optimizer_trace。
mysql>set optimizer_trace='enabled=on'; mysql>select * from information_schema.optimizer_trace\G;
具体参考:http://hidba.ga/2014/09/24/in-and-range/
⑦ 子查询优化 :subquery_materialization_cost_based
更多的信息见:http://dev.mysql.com/doc/refman/5.6/en/index.html
⑧ in、range优化: eq_range_index_dive_limit
在较多等值查询(例如多值的IN查询)情景中,预估可能会扫描的记录数,从而选择相对更合适的索引。用于优化in(),以确认是否直接使用索引统计,在where条件中列的等值条件个数小于这个值时,使用index dive来估算行数,否则使用index statistics来估算;设置为0则禁用index statistics, index dive更准确但效率低
更多的信息见:http://myrock.github.io/2014/09/24/in-and-range/
...
6)参数说明 : http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html
5.6的新参数会在另一篇文章说明。
...
三 总结
上面大概介绍了5.6的一些新的特性,后续会不定时更新,尽量避免5.6的一些问题。
四 参考文章
http://dev.mysql.com/doc/refman/5.6/en/index.html
http://www.ttlsa.com/mysql/summary-of-the-new-features-of-mysql5_6/