标签:并且 plain when page tar mysq 优先级 filter pushd
mysql> show global status like ‘%handler%‘; +----------------------------+------------+ | Variable_name | Value | +----------------------------+------------+ | Handler_commit | 26193722 | | Handler_delete | 142125 | | Handler_discover | 0 | | Handler_external_lock | 32309758 | | Handler_mrr_init | 0 | | Handler_prepare | 1636072 | | Handler_read_first | 211046 | | Handler_read_key | 174345074 | | Handler_read_last | 2597 | | Handler_read_next | 240098234 | | Handler_read_prev | 223 | | Handler_read_rnd | 32443997 | | Handler_read_rnd_next | 7469027328 | | Handler_rollback | 189 | | Handler_savepoint | 0 | | Handler_savepoint_rollback | 0 | | Handler_update | 1362134 | | Handler_write | 719730388 | +----------------------------+------------+
Handler_savepoint 执行savepoint的语句数量
savepoint 语法
SAVEPOINT identifier ROLLBACK [WORK] TO [SAVEPOINT] identifier RELEASE SAVEPOINT identifier
在事务中可以设置保存点生成当前事务快照,在当前事务中通过rollback可恢复到任一保存点那一时刻的数据
测试:连续插入三条数据然后删除然后再插入,每个query执行后设置一个保存点
select * from t1; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 2 | 3 | 5 | NULL | | 4 | 4 | 5 | NULL | | 5 | 6 | 5 | 1 | | 8 | 8 | 5 | 2 | | 9 | NULL | NULL | NULL | +----+------+------+------+ 5 rows in set (0.00 sec) 06:41:48[test](;)> insert into t1 values(); Query OK, 1 row affected (0.01 sec) 06:42:06[test](;)> savepoint ch_in1; Query OK, 0 rows affected (0.00 sec) 06:42:13[test](;)> insert into t1 values(); Query OK, 1 row affected (0.00 sec) 06:42:15[test](;)> savepoint ch_in2; Query OK, 0 rows affected (0.00 sec) 06:42:17[test](;)> insert into t1 values(); Query OK, 1 row affected (0.00 sec) 06:42:19[test](;)> savepoint ch_in3; Query OK, 0 rows affected (0.00 sec) 06:42:21[test](;)> select * from t1; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 2 | 3 | 5 | NULL | | 4 | 4 | 5 | NULL | | 5 | 6 | 5 | 1 | | 8 | 8 | 5 | 2 | | 9 | NULL | NULL | NULL | | 10 | NULL | NULL | NULL | | 11 | NULL | NULL | NULL | | 12 | NULL | NULL | NULL | +----+------+------+------+ 8 rows in set (0.00 sec) 06:42:26[test](;)> delete from t1 where col1 is null; Query OK, 4 rows affected (0.00 sec) 06:42:49[test](;)> savepoint ch_de1; Query OK, 0 rows affected (0.00 sec) 06:42:54[test](;)> insert into t1 values(); Query OK, 1 row affected (0.00 sec) 06:43:01[test](;)> savepoint ch_in4; Query OK, 0 rows affected (0.00 sec) 06:43:06[test](;)> select * from t1; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 2 | 3 | 5 | NULL | | 4 | 4 | 5 | NULL | | 5 | 6 | 5 | 1 | | 8 | 8 | 5 | 2 | | 13 | NULL | NULL | NULL | +----+------+------+------+ 5 rows in set (0.00 sec) 06:43:10[test](;)> rollback to ch_in3; Query OK, 0 rows affected (0.00 sec) 06:44:12[test](;)> select * from t1; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 2 | 3 | 5 | NULL | | 4 | 4 | 5 | NULL | | 5 | 6 | 5 | 1 | | 8 | 8 | 5 | 2 | | 9 | NULL | NULL | NULL | | 10 | NULL | NULL | NULL | | 11 | NULL | NULL | NULL | | 12 | NULL | NULL | NULL | +----+------+------+------+ 8 rows in set (0.00 sec) 06:44:13[test](;)> rollback to ch_in2; Query OK, 0 rows affected (0.00 sec) 06:44:25[test](;)> select * from t1; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 2 | 3 | 5 | NULL | | 4 | 4 | 5 | NULL | | 5 | 6 | 5 | 1 | | 8 | 8 | 5 | 2 | | 9 | NULL | NULL | NULL | | 10 | NULL | NULL | NULL | | 11 | NULL | NULL | NULL | +----+------+------+------+ 7 rows in set (0.00 sec) 06:44:27[test](;)> rollback to ch_in1; Query OK, 0 rows affected (0.00 sec)
可以从后往前恢复到最早的保存点,但是如果跳过中间的保存点一步恢复到最早的恢复点则中点保存点将会自动删除
如 select no_second_index_col fro mrr_test where second_key_col=x; 使用mrr后会先根据二级索引条件查询出主键pk,然后将pk在read_rnd_buffer_size中进行排序,然后根据排序后的pk返回基表区数据,因为数据时按主键的顺序存放的,相邻的数据在同一个page中,所以发生的是顺io
通过optimizer_switch来控制是否开启mrr,其中默认mrr_cost_based=on表示基于cost选择是否使用mrr,若设置为off则表示总是使用mrr07:16:25[test](;)> explain select col3 from mrr where sk>200 and sk<2000; +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+----------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+----------------------------------+ | 1 | SIMPLE | mrr | NULL | range | idx_sk | idx_sk | 5 | NULL | 222 | 100.00 | Using index condition; Using MRR | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+----------------------------------+ 1 row in set, 1 warning (0.00 sec)
MRR 适用于以下两种情况:1、 range access ; 2、 ref and eq_ref access, when they are using Batched Key Access
external_lock()
函数的次数,当打开和关闭表时会调用此函数
通常发生在扫描表的前后,不同的存储引可能返回的值不同,比如用于发现在锁发生之前有多少分区表会用到,然后对其进行lock
# for innodb CREATE TABLE `part` ( `fname` varchar(50) NOT NULL, `lname` varchar(50) NOT NULL, `region_code` tinyint(3) unsigned NOT NULL, `dob` date NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 /*!50100 PARTITION BY RANGE ( region_code) (PARTITION p0 VALUES LESS THAN (64) ENGINE = InnoDB, PARTITION p1 VALUES LESS THAN (128) ENGINE = InnoDB, PARTITION p2 VALUES LESS THAN (192) ENGINE = InnoDB, PARTITION p3 VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */ 09:51:29[test](;)> select * from part; +-------+-------+-------------+------------+ | fname | lname | region_code | dob | +-------+-------+-------------+------------+ | aa | bb | 125 | 2017-09-15 | | aa | bb | 130 | 2017-09-15 | | aa | bb | 128 | 2017-09-15 | | aa | bb | 130 | 2017-09-15 | | aa | bb | 192 | 2017-09-15 | | aa | bb | 200 | 2017-09-15 | +-------+-------+-------------+------------+ 09:53:31[test](;)> explain select * from part where region_code > 125 AND region_code < 2000; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | part | p1,p2,p3 | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.01 sec) 09:53:32[test](;)> show status like ‘Handler_external_lock‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_external_lock | 2 | +-----------------------+-------+ 1 row in set (0.00 sec) # for myisam 09:53:49[test](;)> explain select * from part_myisam where region_code > 125 AND region_code < 2000; +----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | part_myisam | p1,p2,p3 | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where | +----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) 09:53:59[test](;)> show status like ‘Handler_external_lock‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_external_lock | 8 | +-----------------------+-------+ 1 row in set (0.00 sec)
当发生全表扫描或全索引扫描时此值都会加1
当走索引时此值加1是正常的,但是在对下面Handler_read_rnd_next相关测试时,当执行全表扫描时此值也加1
06:00:22[test](;)> flush status
-> ;
Query OK, 0 rows affected (0.10 sec)
06:01:52[test](;)> select * from t1 where col1>=3 and col1<=5;
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
| 2 | 3 | 5 | NULL |
| 4 | 4 | 5 | NULL |
+----+------+------+------+
2 rows in set (0.00 sec)
06:01:58[test](;)> show status like ‘handler_read%‘;
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 2 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
7 rows in set (0.00 sec)
06:02:01[test](;)> explain select * from t1 where col1>=3 and col1<=5;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | range | udk_col1 | udk_col1 | 5 | NULL | 2 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.01 sec)
如走索引扫描且倒叙排序,Handler_read_last会+1,Handler_read_prev会+读取的行数;如果加limit n限制,Handler_read_prev则+ n-1
05:11:41[test](;)> explain select col1 from t1 order by col1 desc; +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t1 | NULL | index | NULL | udk_col1 | 5 | NULL | 6 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+ 05:11:40[test](;)> show status like ‘handler_read%‘;
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 1 |
| Handler_read_next | 0 |
| Handler_read_prev | 6 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
05:24:52[test](;)> explain select col2 from t1 order by col2 desc limit 4;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | index | NULL | idx_col2 | 5 | NULL | 4 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
05:26:49[test](;)> show status like ‘handler_read%‘;
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 1 |
| Handler_read_next | 0 |
| Handler_read_prev | 3 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
如果是索引扫描加filesort则依然会视为全表扫描而增加相关状态值,如下,因为联合索引中的col3是无序的,索引以col3排序,需要全索引扫描后再排序一次
05:40:58[test](;)> explain select col3 from t1 order by col3 desc limit 2; +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+-----------------------------+ | 1 | SIMPLE | t1 | NULL | index | NULL | udk_col2_col3 | 10 | NULL | 6 | 100.00 | Using index; Using filesort | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+-----------------------------+ 05:44:45[test](;)> show status like ‘handler_read%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 1 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 0 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 7 | +-----------------------+-------+ 7 rows in set (0.00 sec)
走索引扫描,Handler_read_next增加的值为索引扫描行数
03:21:52[test](;)> explain select * from t1 where col1>8; +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | range | udk_col1 | udk_col1 | 5 | NULL | 2 | 100.00 | Using index condition | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+ 03:22:28[test](;)> select * from t1 where col1>8; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 9 | 9 | 9 | 9 | | 10 | 10 | 10 | 10 | +----+------+------+------+ 2 rows in set (0.00 sec) 03:22:31[test](;)> show status like ‘handler_read%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 0 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 2 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | +-----------------------+-------+ 7 rows in set (0.00 sec)
06:48:50[test](;)> explain select id from t1;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | index | NULL | idx_id | 4 | NULL | 6 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 1 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 6 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0
但是如果扫描的只有一行,则不会增加此值
06:11:22[test](;)> select * from t1 where col1>=3 and col1<=3; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 2 | 3 | 5 | NULL | +----+------+------+------+ 1 row in set (0.00 sec) 06:11:25[test](;)> show status like ‘handler_read%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 0 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 0 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | +-----------------------+-------+ 06:00:06[test](;)> explain select * from t1 where col1>=3 and col1<=3; +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | range | udk_col1 | udk_col1 | 5 | NULL | 1 | 100.00 | Using index condition | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
当发生全表扫描时,Handler_read_rnd_next会增加 表行数+1,1是指文件结束符,也会计数,读到eof表示已经扫描完成
02:33:43[test](;)> flush status -> ; Query OK, 0 rows affected (0.10 sec) 02:51:11[test](;)> show status like ‘handler_read%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 0 | | Handler_read_key | 0 | | Handler_read_last | 0 | | Handler_read_next | 0 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | +-----------------------+-------+ 7 rows in set (0.00 sec) 02:51:16[test](;)> select * from t1; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 2 | 3 | 5 | NULL | | 4 | 4 | 5 | NULL | | 5 | 6 | 5 | 1 | | 8 | 8 | 5 | 2 | | 9 | 9 | 9 | 9 | | 10 | 10 | 10 | 10 | +----+------+------+------+ 6 rows in set (0.00 sec) 02:51:29[test](;)> show status like ‘handler_read%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 1 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 0 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 7 | +-----------------------+-------+ 7 rows in set (0.00 sec)
什么情况下差生临时表
1、union
01:11:45[test](;)> explain select * from t2 union select * from t2 ; +----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+ | 1 | PRIMARY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL | | 2 | UNION | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL | | NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+ 3 rows in set, 1 warning (0.00 sec) 01:11:34[test](;)> explain select * from t2 union all select * from t2 ; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | PRIMARY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL | | 2 | UNION | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+ 2 rows in set, 1 warning (0.00 sec)
2、group by
第一个语句是不通过索引排序就借用临时表进行;第二个通过order by null避免排序;第三个直接通过索引进行分组
03:10:20[test](;)> explain select * from t1 ignore index(udk_col2_col3,idx_col2) group by col2; +----+-------------+-------+------------+------+------------------------+------+---------+------+------+----------+---------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+------------------------+------+---------+------+------+----------+---------------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | udk_col2_col3,idx_col2 | NULL | NULL | NULL | 2 | 100.00 | Using temporary; Using filesort | +----+-------------+-------+------------+------+------------------------+------+---------+------+------+----------+---------------------------------+ 1 row in set, 1 warning (0.00 sec) 03:20:25[test](;)> explain select * from t1 ignore index(udk_col2_col3,idx_col2) group by col2 order by null; +----+-------------+-------+------------+------+------------------------+------+---------+------+------+----------+-----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+------------------------+------+---------+------+------+----------+-----------------+ | 1 | SIMPLE | t1 | NULL | ALL | udk_col2_col3,idx_col2 | NULL | NULL | NULL | 2 | 100.00 | Using temporary | +----+-------------+-------+------------+------+------------------------+------+---------+------+------+----------+-----------------+ 1 row in set, 1 warning (0.00 sec) 03:20:37[test](;)> explain select * from t1 group by col2; +----+-------------+-------+------------+-------+------------------------+----------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+------------------------+----------+---------+------+------+----------+-------+ | 1 | SIMPLE | t1 | NULL | index | udk_col2_col3,idx_col2 | idx_col2 | 5 | NULL | 2 | 100.00 | NULL | +----+-------------+-------+------------+-------+------------------------+----------+---------+------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
3、distinct和order by非同一字段
如下第一个语句,因为col3非前缀索引字段,是无序的,所以全索引扫描去重后还需通过临时表再进行一次排序;第二个排序字段col2是联合索引的前缀字段所以可以通过全索引扫描一次性完成去重和排序
03:30:34[test](;)> explain select distinct col2,col3 from t1 order by col3 ; +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+----------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+----------------------------------------------+ | 1 | SIMPLE | t1 | NULL | index | udk_col2_col3 | udk_col2_col3 | 10 | NULL | 2 | 100.00 | Using index; Using temporary; Using filesort | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+----------------------------------------------+ 1 row in set, 1 warning (0.00 sec) 03:30:37[test](;)> explain select distinct col2,col3 from t1 order by col2 ; +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t1 | NULL | index | udk_col2_col3 | udk_col2_col3 | 10 | NULL | 2 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
什么情况下产生磁盘临时表
1、当内存放不下时转储为磁盘临时表,内存临时表大小受以下两个参数限制,为16M
05:22:33[test](;)> show global variables like ‘%table%size%‘; +---------------------+-----------+ | Variable_name | Value | +---------------------+-----------+ | max_heap_table_size | 134217728 | | tmp_table_size | 134217728 | +---------------------+-----------+ 05:22:56[test](;)> select 134217728/1024/1024/8; +-----------------------+ | 134217728/1024/1024/8 | +-----------------------+ | 16.000000000000 | +-----------------------+ 1 row in set (0.00 sec)
2、memory 存储引擎不支持text或blob字段类型,所以创建磁盘临时表
3、是用union时,如果字符串列中字符串长度有超过512(二进制单位为byte,非二进制单位为字符)
01:48:01[test](;)> select char_length(name) from t3 where id=2;
+-------------------+
| char_length(name) |
+-------------------+
| 976 |
+-------------------+
1 row in set (0.00 sec)
01:45:28[test](;)> select * from t3 union select * from t3;
01:45:34[test](;)> show status like ‘%tmp%‘;
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1 |
| Created_tmp_files | 0 |
| Created_tmp_tables | 1 |
+-------------------------+-------+
3 rows in set (0.00 sec)
优化:union all不会进行去重,无序临时表;对于分组去重尽量走索引,如果无法走索引则尽量select必须的字段;尽量不要使用text和blob数据类型
表示的是表连接查询的类型,包括以下几种类型
表示全为全表扫描的join查询次数;此值如果较高就需要查看连接字段上是否创建了合适的索引
关联表上走范围查询的join查询次数;可以看到以下select_scan 也加1,select_scan表示驱动表走全表扫描的join查询次数,所以加1
14:18:03[test](;)> explain select * from t1 join t2 on t2.id<7; +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL | | 1 | SIMPLE | t2 | NULL | range | idx_id | idx_id | 5 | NULL | 3 | 100.00 | Using index condition; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------------------------+ 14:24:14[test](;)> show status like ‘select%‘; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | Select_full_join | 0 | | Select_full_range_join | 1 | | Select_range | 0 | | Select_range_check | 0 | | Select_scan | 1 | +------------------------+-------+
表示驱动表走范围查询的join查询次数及单表走范围查询的次数,如
01:48:10[test](;)> explain select * from t1 where col2>=1 and col2<=4; +----+-------------+-------+------------+-------+------------------------+---------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+------------------------+---------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | range | udk_col2_col3,idx_col2 | udk_col2_col3 | 5 | NULL | 1 | 100.00 | Using index condition | +----+-------------+-------+------------+-------+------------------------+---------------+---------+------+------+----------+-----------------------+ 1 row in set, 1 warning (0.00 sec) 02:01:39[test](;)> explain select * from t1 join t2 on t1.id=t2.id where t2.id>=1 and t1.id<=7; +----+-------------+-------+------------+-------+----------------+---------+---------+------+------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+----------------+---------+---------+------+------+----------+----------------------------------------------------+ | 1 | SIMPLE | t1 | NULL | range | PRIMARY,idx_id | PRIMARY | 4 | NULL | 4 | 100.00 | Using where | | 1 | SIMPLE | t2 | NULL | ALL | idx_id | NULL | NULL | NULL | 10 | 70.00 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+-------+----------------+---------+---------+------+------+----------+----------------------------------------------------+ 2 rows in set, 1 warning (0.00 sec)
02:16:20[test](;)> show status like ‘Select_range‘;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Select_range | 2 |
+---------------+-------+
1 row in set (0.00 sec
常出现在非等式条件的连接查询中,下述的执行过程是遍历t1表,然后求t2表中的id<=t1.id的记录,在这一步无法得知是走全表扫描快还是走t2.idx_id索引快,所以需要在确定t1.id后才能够基于cost选择是否走索引执行第二步
14:16:57[test](;)> desc t1; +-------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | col1 | int(11) | YES | UNI | NULL | | | col2 | int(11) | YES | MUL | NULL | | | col3 | int(11) | YES | | NULL | | +-------+---------+------+-----+---------+----------------+ 4 rows in set (0.00 sec) 14:17:37[test](;)> desc t2; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | col1 | varchar(18) | YES | MUL | NULL | | | id | int(11) | YES | MUL | NULL | | +-------+-------------+------+-----+---------+-------+ 2 rows in set (0.00 sec) 14:15:26[test](;)> explain select * from t1,t2 where t1.id>=t2.id; +----+-------------+-------+------------+------+----------------+------+---------+------+------+----------+------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+----------------+------+---------+------+------+----------+------------------------------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | PRIMARY,idx_id | NULL | NULL | NULL | 6 | 100.00 | NULL | | 1 | SIMPLE | t2 | NULL | ALL | idx_id | NULL | NULL | NULL | 10 | 33.33 | Range checked for each record (index map: 0x2) | +----+-------------+-------+------------+------+----------------+------+---------+------+------+----------+------------------------------------------------+ 2 rows in set, 1 warning (0.00 sec) # 去除t2表上id上的索引则不走range check
需要着重关注的参数是sort_scan,表示走全表扫描排序的次数
13:18:29[test](;)> explain select * from t1 order by col3; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+ | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | Using filesort | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+ 1 row in set, 1 warning (0.00 sec)
但是走索引也可能会伴随着文件排序,如下所示,是排序列字段在索引中无序导致的
13:17:59[test](;)> explain select * from t1 where col2>=1 and col2<=4 order by col3; +----+-------------+-------+------------+-------+------------------------+---------------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+------------------------+---------------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | t1 | NULL | range | udk_col2_col3,idx_col2 | udk_col2_col3 | 5 | NULL | 1 | 100.00 | Using index condition; Using filesort | +----+-------------+-------+------------+-------+------------------------+---------------+---------+------+------+----------+---------------------------------------+ 1 row in set, 1 warning (0.00 sec)
官方文档:https://dev.mysql.com/doc/refman/5.6/en/internal-locking.html
mysql 对 MyISAM
, MEMORY
, 和MERGE表使用表锁机制,分为读锁和写锁,如当session1会变更myisam 表t时,会加table write lock,在语句执行完之后才会释放表锁,在此期间其他会话对表t的读或写都会进入排队等候阶段
模拟表锁
CREATE TABLE `t3` (
`id` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8
# session 1 mysql> show status like ‘table_lock%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 136 | | Table_locks_waited | 1 | +-----------------------+-------+ 2 rows in set (0.00 sec) mysql> insert into test.t3 values(1); Query OK, 1 row affected (5.00 sec)
同一时间开启另一个会话对t3表进行变更操作,因为磁盘io性能问题才可以测试出表锁等待(30测试机)
#session2 mysql> insert into test.t3 values(1); Query OK, 1 row affected (6.04 sec)
session2会等待session1执行完insert语句后才会进行插入,会看到所等待次数加1
mysql> show status like ‘table_lock%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 138 | | Table_locks_waited | 2 | +-----------------------+-------+
需要关注的状态为:
Creating sort index :表示mysql正在使用内部临时表来进行排序
Copying to tmp table on disk :将临时表copy到磁盘,性能很差
Sending data :发送数据到客户端,如果时间较长,则有可能是通过磁盘扫描大量的数据造成的;避免select *、通过limit减少网络流量传输;
Searching rows for update : mysql正在通过索引寻找待更新的记录,正常情况下使瞬间完成,此值应该为0,如果较高则说明索引的选择性不高过滤性较差,则会造成频繁的锁等待及CPU飙高
Waiting for table metadata lock :ddl被dml阻塞或阻塞dml,所以表结构及数据类型尽量一次性设计好,不要常做变动
session 1 01:27:50[test](;)> begin -> ; Query OK, 0 rows affected (0.00 sec) 01:27:53[test](;)> update t1 set col4=2 where col4=1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 session 2 01:29:13[test](;)> alter table t1 drop col4; 01:28:35[test](;)> show full processlist; +-------+------+-----------+------+---------+------+---------------------------------+--------------------------+ | Id | User | Host | db | Command | Time | State | Info | +-------+------+-----------+------+---------+------+---------------------------------+--------------------------+ | 87859 | root | localhost | test | Query | 0 | starting | show full processlist | | 87866 | root | localhost | test | Sleep | 1503 | | NULL | | 87887 | root | localhost | test | Query | 3 | Waiting for table metadata lock | alter table t1 drop col4 | | 89762 | pmm | localhost | NULL | Sleep | 0 | | NULL | +-------+------+-----------+------+---------+------+---------------------------------+--------------------------+
innodb 支持行锁,上锁对象是索引记录,包括S row lock和X row lock,当事务进行update或执行select for update时会尝试在操作的行上获取X row lock,如果当前行上已经标记为有S(通过select in share mode 添加)或X存在,则会进入锁队列排队等待,X的优先级高于S
不同隔离级别锁行为也不同,当前只讨论RC隔离级别下的行锁行为(线上为RC),分析变更字段有无索引的行为
测试环境
08:00:24[test](;)> desc t1; +-------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | col1 | int(11) | YES | UNI | NULL | | | col2 | int(11) | YES | MUL | NULL | | | col3 | int(11) | YES | | NULL | | | col4 | int(11) | YES | | NULL | | +-------+---------+------+-----+---------+----------------+ 5 rows in set (0.00 sec)
08:11:12[test](;)> select * from t1;
+----+------+------+------+------+
| id | col1 | col2 | col3 | col4 |
+----+------+------+------+------+
| 1 | 1 | 1 | 1 | 1 |
| 2 | 2 | 10 | 2 | 10 |
| 3 | 3 | 10 | 3 | 10 |
+----+------+------+------+------+
主键索引
只在主键索引记录上加X row lock
update table set col4=2 where id=1;
---TRANSACTION 1978835, ACTIVE 5 sec 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1 MySQL thread id 8652, OS thread handle 139711496959744, query id 72597 localhost root cleaning up
二级索引
走二级索引col2变更数据,会在索引col2=10的记录及所对应的主键id=2和id=3的索引记录上加X row lock,总共是4个行锁
session 1 08:11:29[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:11:34[test](;)> update t1 set col4=11 where col2=10; Query OK, 2 rows affected (0.00 sec) Rows matched: 2 Changed: 2 Warnings: 0 # 锁信息 ---TRANSACTION 1979120, ACTIVE 6 sec 3 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2 MySQL thread id 9861, OS thread handle 139711497225984, query id 81552 localhost root cleaning up --------
开启另一会话尝试变更id=3的行,产生锁等待;session 2在等待主键索引上id=3上的X row lock
session 2 07:57:57[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:13:00[test](;)> update t1 set id=4 where id=3; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction #锁信息 ---TRANSACTION 1979159, ACTIVE 5 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s) MySQL thread id 8856, OS thread handle 139711496693504, query id 82866 localhost root updating update t1 set id=4 where id=3 ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 218 page no 3 n bits 72 index PRIMARY of table `test`.`t1` trx id 1979159 lock_mode X locks rec but not gap waiting Record lock, heap no 6 PHYSICAL RECORD: n_fields 7; compact format; info bits 0 0: len 4; hex 80000003; asc ;; 1: len 6; hex 0000001e32f0; asc 2 ;; 2: len 7; hex 66000001c51353; asc f S;; 3: len 4; hex 80000003; asc ;; 4: len 4; hex 8000000a; asc ;; 5: len 4; hex 80000003; asc ;; 6: len 4; hex 8000000b; asc ;; ------------------ ---TRANSACTION 1979120, ACTIVE 134 sec 3 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2 MySQL thread id 9861, OS thread handle 139711497225984, query id 81552 localhost root cleaning up --------
Innodb_row_lock_time为innodb 行锁等待时长;Innodb_row_lock_waits为产生所等待的次数
无索引
在id=1的主键记录上加X row lock,innodb先将所有行加X row lock 然后返回给server通过where条件判定,如果不符合条件则释放锁,所以最终只在符合条件的主键记录上加锁,但是也存在锁住全行的过程,所以也有可能产生死锁;
但是对于RR隔离级别,为了防止幻影读会在所有行上加X row lock
session 1 08:35:49[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:35:51[test](;)> update t1 set col4=2 where col4=1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 session 2 08:36:52[test](;)> update t1 set col4=11 where col4=10; Query OK, 2 rows affected (0.00 sec) Rows matched: 2 Changed: 2 Warnings: 0
session 1 08:35:49[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:35:51[test](;)> update t1 set col4=2 where col4=1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 session 2 08:36:52[test](;)> update t1 set col4=11 where col4=10; Query OK, 2 rows affected (0.00 sec) Rows matched: 2 Changed: 2 Warning
建议:尽量使变更操作走索引;避免大事务可以有效减缓锁资源争用
什么是ICP
mysql 存储引擎是mysql的一个组件,数据是存储引擎负责存储的,server和存储引擎进行交互取的数据,当使用索引时,ICP用于优化server读取数据的过程
如以下查询语句
CREATE TABLE `icp_test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `col1` varchar(20) DEFAULT NULL, `col2` varchar(20) DEFAULT NULL, `col3` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_col1_col2` (`col1`,`col2`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 05:31:31[test](;)> select * from icp_test; +----+------+------+------+ | id | col1 | col2 | col3 | +----+------+------+------+ | 1 | aa1 | bb1 | NULL | | 2 | aa2 | bb2 | NULL | | 3 | aa3 | bb3 | NULL | | 4 | aa3 | cc1 | NULL | | 5 | aa3 | cc2 | NULL | | 6 | aa3 | cc3 | NULL | | 7 | aa1 | dd1 | NULL | | 8 | aa1 | dd2 | NULL | +----+------+------+------+ 8 rows in set (0.01 sec) 05:16:22[test](;)>select * from icp_test where col1=‘aa1‘ and col2 like ‘%b1‘;
如果没有使用ICP:因为是前缀模糊搜索,所以col2不走索引,innodb通过idx_col1_col2索引读取出col1=‘aa1‘的行返回给server,server再根据条件筛选出col2 为‘%b1‘的记录
如果使用ICP:where 条件col1和col2上有联合索引,所以server将col2 的条件搜索也放到innodb层,innodb通过读取索引idx_col1_col2读取符合col1和col2条件key对应的主键然后回表取出完整的行返回给server;减少了innodb读取基表的次数以及server访问innodb的次数,降低物理I/O
使用ICP的表现
# 未使用ICP 05:31:48[test](;)> SET optimizer_switch = ‘index_condition_pushdown=off‘; Query OK, 0 rows affected (0.00 sec) 06:02:26[test](;)> explain select * from icp_test where col1=‘aa1‘ and col2 like ‘%b1‘; +----+-------------+----------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | icp_test | NULL | ref | idx_col1_col2 | idx_col1_col2 | 63 | const | 3 | 12.50 | Using where | +----+-------------+----------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) 06:05:39[test](;)> show status like ‘handler_read%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 0 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 3 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | +-----------------------+-------+ # 使用ICP 05:16:22[test](;)> explain select * from icp_test where col1=‘aa1‘ and col2 like ‘%b1‘; +----+-------------+----------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | icp_test | NULL | ref | idx_col1_col2 | idx_col1_col2 | 63 | const | 3 | 12.50 | Using index condition | +----+-------------+----------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+ 06:06:29[test](;)> show status like ‘handler_read%‘; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 0 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 1 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | +-----------------------+-------+
使用ICP的条件
查询类型为 range、ref、eq_ref和ref_or_null
二级索引,因为ICP的目的是减少读取整个行记录的数量从而减少I/O操作,而走主键查询会将整行数据读取到buffer pool,ICP就变得没有意义;测试表中如果无col3列也不会走ICP,同样的道理,二级索引idx_col1_col2也包括主键的
展示的是information_schema.tables中的字段data_free,为数据文件碎片即不连续的section(8个连续的页为一个区),DBA需要关注
变更边长字段的值时会产生磁盘碎片,值越大说明碎片越多,data_free/data_length*100% 低于15%不会产生性能影响
查询语句
SELECT concat( table_schema, ‘.‘, table_name ) table_name, concat(round( data_free / ( 1024 *1024 ) , 2), ‘M‘) data_free, concat( round( data_length / ( 1024 *1024 ) , 2 ) , ‘M‘ ) data_length, concat( round( index_length / ( 1024 *1024 ) , 2 ) , ‘M‘ ) index_length, concat( round( round( data_length + index_length ) / ( 1024 *1024 ) , 2 ) , ‘M‘ ) total_size FROM information_schema.TABLES where TABLE_SCHEMA not in (‘sys‘,‘mysql‘,‘test‘,‘information_schema‘,‘performance_schema‘) ORDER BY data_free DESC;
使用率是通过auto_increment/字段最大限制值*100% 计算得来的,如
mysql> SHOW TABLE STATUS like ‘biz_contacts‘\G *************************** 1. row *************************** Name: biz_contacts Engine: InnoDB Version: 10 Row_format: Dynamic Rows: 723929 Avg_row_length: 102 Data_length: 74072064 Max_data_length: 0 Index_length: 0 Data_free: 4194304 Auto_increment: 8636346196731166205 Create_time: 2017-07-12 00:11:03 Update_time: 2017-09-19 21:51:43 Check_time: NULL Collation: utf8_general_ci Checksum: NULL Create_options: Comment: 联系信息表 1 row in set (0.00 sec) mysql> desc biz_contacts; +-------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+----------------+ | ContactsID | bigint(20) | NO | PRI | NULL | auto_increment | | LinkMan | varchar(64) | YES | | NULL | | | AreaID | bigint(20) | YES | | NULL | | | FullAddress | varchar(256) | YES | | NULL | | | MobilePhone | varchar(64) | YES | | NULL | | | Phone | varchar(64) | YES | | NULL | | | Email | varchar(64) | YES | | NULL | | | QQ | varchar(64) | YES | | NULL | | | WeChat | varchar(64) | YES | | NULL | | | PostCode | varchar(64) | YES | | NULL | | | Position | varchar(64) | YES | | NULL | | +-------------+--------------+------+-----+---------+----------------+ 11 rows in set (0.00 sec) mysql> select 8636346196731166205/9223372036854775807; +-----------------------------------------+ | 8636346196731166205/9223372036854775807 | +-----------------------------------------+ | 0.9364 | +-----------------------------------------+ 1 row in set (0.00 sec) mysql> select ContactsID from qlw_nxin_com.biz_contacts limit 1; +--------------------+ | ContactsID | +--------------------+ | 636182262510132839 | +--------------------+ 1 row in set (0.00 sec)
上述展示的问题是自增值很快就会达到限制的最大值,到时表将不能有新的记录插入
建议:使用从1自增列作为主键,上述主键字段过长,很容易达到限制长度,自增值也会达到限制;过长的字段使用字符串类型
int的宽度问题
整形存储要求 :https://dev.mysql.com/doc/refman/5.7/en/integer-types.html
int(3) 和int(11)的区别只在于存储时的宽度和存储占用的空间以及长度限制都没有关系,要想看出区别可以使用zerofill,如下所示
插入的值的位数如果小于设置的宽度则左边用0填充,如果是超过或等于设置的宽度则正常显示
mysql> create table t (t int(3) zerofill); Query OK, 0 rows affected (0.00 sec) mysql> insert into t set t = 10; Query OK, 1 row affected (0.00 sec) mysql> select * from t; +——+ | t | +——+ | 010 | +——+ 1 row in set (0.11 sec) Zerofill with default width, the same as int(10): mysql> create table t (t int zerofill); Query OK, 0 rows affected (0.02 sec) mysql> insert into t set t = 10; Query OK, 1 row affected (0.02 sec) mysql> select * from t; +————+ | t | +————+ | 0000000010 | +————+ 1 row in set (0.08 sec)
标签:并且 plain when page tar mysq 优先级 filter pushd
原文地址:http://www.cnblogs.com/Bccd/p/7472282.html