本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/10html
SELECT thread_id FROM thread_5 WHERE id = 11074781 AND status = 0 AND thread_type = 0 ORDER BY post_time LIMIT 0, 50mysql
增长在id 、status、thread_type 、post_time上的4字段复合索引。git
缘由:经过优化先后的执行计划对比可发现,优化后消除了filesort,直接利用索引的有序性避开额外的排序操做github
当不能使用索引生成排序结果的时候,MYSQL须要本身进行排序,若是数据量小则在内存中进行,若是数据量则须要使用磁盘,不过MYSQL将这个过程统一称为文件排序(filesort),即便彻底是内存排序不须要任何磁盘文件时也是如此。
若是须要排序的数据量小于排序缓冲区,MYSQL使用内存进行快速排序操做,若是内存不够排序,那么MYSQL会先将数据分块,对独立的块使用快速排序进行排序,并将各个块的排序结果放在磁盘上,而后将各个排序好的块进行合并,最后返回排序结果。算法
MYSQL有2中排序算法:(再也不赘述2种排序的意思)
2次排序传输
单次排序传输sql
MYSQL在进行文件排序的时候须要使用的临时存储空间可能比想象的大许多。由于MYSQL在排序时,对每个排序记录都会分配一个足够长的定长空间来存放。这个定长空间必须足够长以容纳最长的字符串。数据库
在关联查询的时候若是须要排序,MYSQL会分为2中状况来处理:若是order by子句的全部列都来自关联的第一个表,那么MYSQL在关联处理第一个表的时候就进行文件排序,Explain的结果能够看到Extra字段有using filesort。除此以外的状况,MYSQL将关联的结果放到一个临时表中,而后在全部关联结束以后,再进行文件排序。Extra的结果时using temporary;using filesort。若是有limit,也在排序以后应用,因此即便返回较少的数据,也会很是耗时。服务器
引用自:http://s.petrunia.net/blog/?p=24并发
SELECT c.cust_id cust_id FROM tb_aband_cust_contact c, tb_aband_phone p WHERE c.contact_id = contact_or_recipId AND p.full_phone=88123121 AND c.del_flag=0 AND p.flag=0 AND p.del_flag=0;函数
创建full_phone、flag、del_flag上的3字段复合索引
MYSQL一次查询只能使用一个索引
DELETE FROM mydownload WHERE create_time<1271913480;
创建基于create_time的索引,并使用LIMIT修改成分批的执行,减小锁的时间。
DELETE FROM mydownload WHERE create_time<1271913480 LIMIT 50000
删除操做不会从新整理整个表,只是把行标记为删除,在表中留下“空洞”。
尽管MyISAM是表级锁,可是依然能够一边读取,一边并发追加新行。这种状况下只能读取到查询开始时的全部数据,新插入的数据时不可见的。这样能够避免不一致读。
然而表中间的数据变更的话,仍是难以提供一致读。MVCC是解决这个问题的最流行的办法。但是MyISAM不支持MVCC,除非插入操做在表的末尾,不然不能支持并发插入。
经过配置concurrent_insert变量,能够配置MyISAM打开并发插入
0:不容许并发插入,全部插入都会对表加互斥锁。
1:只要表中没有空洞,MyISAM就容许并发插入
2:5.0及之后,强制插入到表的末尾,即便表中有空洞。若是没有线程从表中读取数据,MySQL将把新行放在空洞里。
MyISAM存储引擎的读锁和写锁是互斥的,读写操做是串行的。那么,一个进程请求某个 MyISAM表的读锁,同时另外一个进程也请求同一表的写锁,MySQL如何处理呢?答案是写进程先得到锁。不只如此,即便读请求先到锁等待队列,写请求后 到,写锁也会插到读锁请求以前!这是由于MySQL认为写请求通常比读请求要重要。
max_write_lock_count:
缺省状况下,写操做的优先级要高于读操做的优先级,即使是先发送的读请求,后发送的写请求,此时也会优先处理写请求,而后再处理读请求。这就形成一个问题:一旦我发出若干个写请求,就会堵塞全部的读请求,直到写请求全都处理完,才有机会处理读请求。此时能够考虑使用max_write_lock_count:
max_write_lock_count=1
有了这样的设置,当系统处理一个写操做后,就会暂停写操做,给读操做执行的机会。
low-priority-updates:
咱们还能够更干脆点,直接下降写操做的优先级,给读操做更高的优先级。
low-priority-updates=1
http://www.cnblogs.com/coser/archive/2011/11/08/2241674.html
http://www.cnblogs.com/zhengyun_ustc/archive/2013/11/29/slowquery3.html
依赖子查询
select_type为DEPENDENT SUBQUERY,子查询的第一个select依赖外部的查询。
replace没有保留旧值,而on duplicate key update相似于update,保留了旧值
http://www.path8.net/tn/archives/5613
http://blog.itpub.net/26250550/viewspace-1076292/
http://dev.mysql.com/doc/refman/5.0/en/server-status-variables.html
http://www.fromdual.com/mysql-handler-read-status-variables
我的理解:
Handler_read_first:从头至尾扫描一个索引的次数
Handler_read_key:访问索引的次数
Handler_read_last:从尾到头扫描一个索引的次数
Handler_read_next:按照索引的顺序读取下一行次数,常常发生在范围查找或者扫描索引的状况中。
Handler_read_prev:按照索引的顺序读取前一行次数,常常发生在ORDER BY ... DESC
Handler_read_rnd:这个变量比较费解。。。,手册中说:
The number of requests to read a row based on a fixed position. This value is high if you are doing a lot of queries that require sorting of the result. You probably have a lot of queries that require MySQL to scan entire tables or you have joins that do not use keys properly.
全表扫描该值不会增长,不利用索引的排序也不会增长,只有在临时表中进行排序该值才会增长。
Handler_read_rnd_next:从数据文件中读取下一行的次数。
最后两个值应该越大越很差。
select user(),current_user()
user()函数会返回当前用户链接到服务器使用的链接参数。
current_user()会返回从权限表中选择的与访问权限相关的用户名和主机名对
user表排序工做以下,假定user表看起来像这样:
+-----------+----------+-
| Host | User | …
+-----------+----------+-
| % | root | …
| % | jeffrey | …
| localhost | root | …
| localhost | | …
+-----------+----------+-
当服务器读取表时,它首先以最具体的Host值排序。主机名和IP号是最具体的。'%'意味着“任何主机”而且是最不特定的。有相同Host值的条目首先以最具体的User值排序(空User值意味着“任何用户”而且是最不特定的)。最终排序的user表看起来像这样:
+-----------+----------+-
| Host | User | …
+-----------+----------+-
| localhost | root | … ...
| localhost | | … ...
| % | jeffrey | … ...
| % | root | … ...
+-----------+----------+-
当客户端试图链接时,服务器浏览排序的条目并使用找到的第一匹配。对于由jeffrey从localhost的链接,表内有两个条目匹配:Host和User值为'localhost'和''的条目,和值为'%'和'jeffrey'的条目。'localhost'条目首先匹配,服务器可使用。
mysqld启动时,全部受权表的内容被读进内存而且今后时生效。
当服务器注意到受权表被改变了时,现存的客户端链接有以下影响:
表和列权限在客户端的下一次请求时生效。
数据库权限改变在下一个USE db_name命令生效。
全局权限的改变和密码改变在下一次客户端链接时生效。
若是用GRANT、REVOKE或SET PASSWORD对受权表进行修改,服务器会注意到并当即从新将受权表载入内存。
若是你手动地修改受权表(使用INSERT、UPDATE或DELETE等等),你应该执行mysqladmin flush-privileges或mysqladmin reload告诉服务器再装载受权表,不然你的更改将不会生效,除非你重启服务器。
若是你直接更改了受权表但忘记重载,重启服务器后你的更改方生效。这样可能让你迷惑为何你的更改没有什么变化!
当你修改受权表的内容时,确保你按你想要的方式更改权限设置是一个好主意。要检查给定帐户的权限,使用SHOW GRANTS语句。例如,要检查Host和User值分别为pc84.example.com和bob的帐户所授予的权限,应经过语句:
mysql> SHOW GRANTS FOR 'bob'@'pc84.example.com';
一个有用的诊断工具是mysqlaccess脚本,由Carlier Yves 提供给MySQL分发。使用--help选项调用mysqlaccess查明它怎样工做。注意:mysqlaccess仅用user、db和host表检查存取。它不检查tables_priv、columns_priv或procs_priv表中指定的表、列和程序级权限。
mysql> explain select * from recent_answer where id=7;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE noticed after reading const tables |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
mysql实际访问一遍primary key或者unique key(sql语句必须用到了primary key或者unique key),发现没有这条记录,返回Impossible WHERE noticed after reading const tables
没有明确指出谁持有锁,可是能够显示出事务在等待锁
当前出现的锁,可是没法得出哪一个事务持有锁,哪一个事务等待锁
显示哪一个事务持有锁,哪一个事务等待锁,可是没法显示正在阻塞中的事务的MySQL进程ID
能够查看MySQL进程的ID
show engine innodb status
查找LATEST DETECTED DEADLOCK
关注WAITING FOR THIS LOCK TO BE GRANTED(事务在等待哪一个锁)和HOLDS THIS LOCKS(阻塞事务的锁的信息)
当事务开始时,它会获取全部须要使用的表上的元数据锁,并在事务结束后释放锁,全部其余想要修改这些表定义的线程都须要等待事务结束。(5.5.3版本以后)
http://blog.itpub.net/26250550/viewspace-1071987/
翻译自:http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html
插入的行数会被提早计算出来
例如:不包括嵌套子查询的insert和replace
插入的行数不会被提早计算出来
例如:INSERT ... SELECT, REPLACE ... SELECT, LOAD DATA
包括:在插入时指定自增列的值
INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d'); //c1是自增列
INSERT ... ON DUPLICATE KEY UPDATE,可能分配给该语句的值没有被用到(更新)
表锁
“块插入”使用表锁。
简单插入仅锁定分配自增值的过程,一次性分配所需的数量
均不使用表锁,仅锁定分配自增值的过程。
仅能保证惟一而且单调自增,可是不保证连续。
可是可能会形成主从不一致。
http://dev.mysql.com/doc/refman/5.1/en/explain-extended.html
The filtered column indicates an estimated percentage of table rows that will be filtered by the table condition. That is, rows shows the estimated number of rows examined and rows × filtered / 100 shows the number of rows that will be joined with previous tables. Before MySQL 5.7.3, this column is displayed if you use EXPLAIN EXTENDED. As of MySQL 5.7.3, extended output is enabled by default and the EXTENDED keyword is unnecessry.
要想过滤掉A中的数据,必须使用where,即便on中包含有A表中的列的限制条件,也不会过滤A的任何数据
Created_tmp_disk_tables:在磁盘上创建临时表的次数。若是没法在内存上创建临时表,MySQL则将临时表创建到磁盘上。内存临时表的最大值是tmp_table_size和max_heap_table_size values中偏小的那个。
Created_tmp_files:MySQL创建的临时文件。
Created_tmp_tables:创建临时表的数目。另外,每当执行 SHOW STATUS,都会使用一个内部的临时表,Created_tmp_tables的全局值都会增长。
翻译自:http://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html
ICP应用的场合是当MySQL利用索引获取表数据。在没有ICP时,存储引擎利用索引定位数据,将数据返回给服务器,在服务器端利用where条件过滤数据。当ICP启用时,MySQL能够在索引端利用where条件过滤不须要的数据,不须要在服务器端过滤。
MySQL只有当type值为range、 ref、 eq_ref或者ref_or_null而且须要去表中取数据时才会用到ICP(在MySQL5.6中分区表不能使用ICP,在5.7中修复)。当表是InnoDB时,索引必须是二级索引(一级索引能够直接取数据,没必要去表中取数据)
ICP是默认开启的,经过设置optimizer_switch变量里的index_condition_pushdown 标记来开启ICP
mysql > set optimizer_switch=’index_condition_pushdown=on|off
假设有一张表,有以下索引:key(zipcode, lastname, firstname),sql语句以下:
SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%etrunia%' AND address LIKE '%Main Street%';
若是没有ICP,会在表中扫描全部zipcode='95054'的用户,索引不会帮助过滤不符合lastname LIKE '%etrunia%'的行。当启用ICP时,MySQL会在索引中过滤lastname LIKE '%etrunia%'的行(我的认为能够从explain的key_len字段看出来是否使用了lastname字段来过滤),减小了IO操做
取自http://s.petrunia.net/blog/?p=101 的一张图,其实看了这张图就知道ICP是干吗的了:
前台线程的程序,必须等全部的前台线程运行完毕后才能退出;然后台线程的程序,只要前台的线程都终止了,那么后台的线程就会自动结束并推出程序。
通常前台线程用于须要长时间等待的任务,好比监听客户端的请求;后台线程通常用于处理时间较短的任务,好比处理客户端发过来的请求信息。
每个服务器端进程在该表中都有一行,代表是否启用监控和历史事件日志
mysql> SELECT * FROM threads\G *************************** 1. row *************************** THREAD_ID: 1 NAME: thread/sql/main TYPE: BACKGROUND PROCESSLIST_ID: NULL PROCESSLIST_USER: NULL PROCESSLIST_HOST: NULL PROCESSLIST_DB: NULL PROCESSLIST_COMMAND: NULL PROCESSLIST_TIME: 80284 PROCESSLIST_STATE: NULL PROCESSLIST_INFO: NULL PARENT_THREAD_ID: NULL ROLE: NULL INSTRUMENTED: YES HISTORY: YES CONNECTION_TYPE: NULL THREAD_OS_ID: 489803 ... *************************** 4. row *************************** THREAD_ID: 51 NAME: thread/sql/one_connection TYPE: FOREGROUND PROCESSLIST_ID: 34 PROCESSLIST_USER: isabella PROCESSLIST_HOST: localhost PROCESSLIST_DB: performance_schema PROCESSLIST_COMMAND: Query PROCESSLIST_TIME: 0 PROCESSLIST_STATE: Sending data PROCESSLIST_INFO: SELECT * FROM threads PARENT_THREAD_ID: 1 ROLE: NULL INSTRUMENTED: YES HISTORY: YES CONNECTION_TYPE: SSL/TLS THREAD_OS_ID: 755399 ...
当Performance Schema库初始化时,会将已经存在的线程填充到threads表中。从那以后,每当服务器新建立一个线程,就对应得在threads表中新增长一行。
在新建立的线程中,INSTRUMENTED和HISTORY列的值由setup_actors表决定。
当线程结束时,在threads表中对应的行将被移除。
和INFORMATION_SCHEMA.PROCESSLIST与SHOW PROCESSLIST的区别:
重要的字段:
其他字段的值看参考资料吧。
参考资料:http://dev.mysql.com/doc/refman/5.7/en/threads-table.html
setup_instruments存储着一系列监控器对象,表明着什么事件会被收集。(The setup_instruments table lists classes of instrumented objects for which events can be collected)
mysql> SELECT * FROM setup_instruments; +------------------------------------------------------------+---------+-------+ | NAME | ENABLED | TIMED | +------------------------------------------------------------+---------+-------+ ... | wait/synch/mutex/sql/LOCK_global_read_lock | YES | YES | | wait/synch/mutex/sql/LOCK_global_system_variables | YES | YES | | wait/synch/mutex/sql/LOCK_lock_db | YES | YES | | wait/synch/mutex/sql/LOCK_manager | YES | YES | ... | wait/synch/rwlock/sql/LOCK_grant | YES | YES | | wait/synch/rwlock/sql/LOGGER::LOCK_logger | YES | YES | | wait/synch/rwlock/sql/LOCK_sys_init_connect | YES | YES | | wait/synch/rwlock/sql/LOCK_sys_init_slave | YES | YES | ... | wait/io/file/sql/binlog | YES | YES | | wait/io/file/sql/binlog_index | YES | YES | | wait/io/file/sql/casetest | YES | YES | | wait/io/file/sql/dbopt | YES | YES | ...
每个在源码中的监控器都会在表中有一行,即便该监控器代码没有被执行。当一个监控器代码被启用而且执行,就会创造一个监控器实例,在 *_instances表中可见。
参考资料:http://dev.mysql.com/doc/refman/5.7/en/setup-instruments-table.html
setup_consumers表用于配置事件的消费者类型,即收集的事件最终会写入到哪些统计表中。(The setup_consumers table lists the types of consumers for which event information can be stored and which are enabled)
mysql> SELECT * FROM setup_consumers; +----------------------------------+---------+ | NAME | ENABLED | +----------------------------------+---------+ | events_stages_current | NO | | events_stages_history | NO | | events_stages_history_long | NO | | events_statements_current | YES | | events_statements_history | YES | | events_statements_history_long | NO | | events_transactions_current | NO | | events_transactions_history | NO | | events_transactions_history_long | NO | | events_waits_current | NO | | events_waits_history | NO | | events_waits_history_long | NO | | global_instrumentation | YES | | thread_instrumentation | YES | | statements_digest | YES | +----------------------------------+---------+
参考资料:http://dev.mysql.com/doc/refman/5.7/en/setup-consumers-table.html
http://www.cnblogs.com/cchust/p/5022148.html
setup_actors表决定着是否为前台进程(和客户端链接有关的进程)开启监控和历史事件日志。最多有100记录,能够经过修改performance_schema_setup_actors_size系统变量最大值。
mysql> SELECT * FROM setup_actors; +------+------+------+---------+---------+ | HOST | USER | ROLE | ENABLED | HISTORY | +------+------+------+---------+---------+ | % | % | % | YES | YES | +------+------+------+---------+---------+
对于每个前台进程,会根据用户名和主机名在setup_actors表中进行匹配。若是匹配成功,ENABLED和HISTORY列的值就会被分别赋给threads表中的INSTRUMENTED和HISTORY列。这可以让用户启用监控器和历史事件日志。若是没有在setup_actors表中匹配到,ENABLED和HISTORY列的值会被赋值为NO。
对于setup_actors表的改变不会影响现有的线程,只会改变现有线程建立的子线程。若是想要改变现有的线程,改变threads表中对应行的 INSTRUMENTED和HISTORY的值。
参考资料:http://dev.mysql.com/doc/refman/5.7/en/setup-actors-table.html
http://dev.mysql.com/doc/refman/5.7/en/performance-schema-instrument-naming.html
以下例子示例了如何像SHOW PROFILES和SHOW PROFILE同样分析数据。
1.默认的,MySQL容许全部前台线程监控和收集历史事件。
mysql> SELECT * FROM setup_actors; +------+------+------+---------+---------+ | HOST | USER | ROLE | ENABLED | HISTORY | +------+------+------+---------+---------+ | % | % | % | YES | YES | +------+------+------+---------+---------+
能够作以下改变:
mysql> UPDATE performance_schema.setup_actors SET ENABLED = 'NO', HISTORY = 'NO' -> WHERE HOST = '%' AND USER = '%'; mysql> INSERT INTO performance_schema.setup_actors (HOST,USER,ROLE,ENABLED,HISTORY) -> VALUES('localhost','test_user','%','YES','YES');
mysql> SELECT * FROM performance_schema.setup_actors; +-----------+-----------+------+---------+---------+ | HOST | USER | ROLE | ENABLED | HISTORY | +-----------+-----------+------+---------+---------+ | % | % | % | NO | NO | | localhost | test_user | % | YES | YES | +-----------+-----------+------+---------+---------+
2.确保statement and stage监控器在setup_instruments表中启用。
mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' -> WHERE NAME LIKE '%statement/%'; mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' -> WHERE NAME LIKE '%stage/%';
3.确保events_statements_* 和events_stages_*启用。
mysql> UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' -> WHERE NAME LIKE '%events_statements_%'; mysql> UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' -> WHERE NAME LIKE '%events_stages_%';
4.运行想要分析的语句:
mysql> SELECT * FROM employees.employees WHERE emp_no = 10001; +--------+------------+------------+-----------+--------+------------+ | emp_no | birth_date | first_name | last_name | gender | hire_date | +--------+------------+------------+-----------+--------+------------+ | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 | +--------+------------+------------+-----------+--------+------------+
5.经过events_statements_history_long表肯定EVENT_ID
mysql> SELECT EVENT_ID, TRUNCATE(TIMER_WAIT/1000000000000,6) as Duration, SQL_TEXT -> FROM performance_schema.events_statements_history_long WHERE SQL_TEXT like '%10001%'; +----------+----------+--------------------------------------------------------+ | event_id | duration | sql_text | +----------+----------+--------------------------------------------------------+ | 31 | 0.028310 | SELECT * FROM employees.employees WHERE emp_no = 10001 | +----------+----------+--------------------------------------------------------+
6.一个语句极可能会触发不少事件,这些事件都是循环嵌套的,每一个阶段的事件记录有一个nesting_event_id列,包含父表的event_id。(Query the events_stages_history_long table to retrieve the statement's stage events. Stages are linked to statements using event nesting. Each stage event record has a NESTING_EVENT_ID column that contains the EVENT_ID of the parent statement.
求翻译,MLGB)
mysql> SELECT event_name AS Stage, TRUNCATE(TIMER_WAIT/1000000000000,6) AS Duration -> FROM performance_schema.events_stages_history_long WHERE NESTING_EVENT_ID=31; +--------------------------------+----------+ | Stage | Duration | +--------------------------------+----------+ | stage/sql/starting | 0.000080 | | stage/sql/checking permissions | 0.000005 | | stage/sql/Opening tables | 0.027759 | | stage/sql/init | 0.000052 | | stage/sql/System lock | 0.000009 | | stage/sql/optimizing | 0.000006 | | stage/sql/statistics | 0.000082 | | stage/sql/preparing | 0.000008 | | stage/sql/executing | 0.000000 | | stage/sql/Sending data | 0.000017 | | stage/sql/end | 0.000001 | | stage/sql/query end | 0.000004 | | stage/sql/closing tables | 0.000006 | | stage/sql/freeing items | 0.000272 | | stage/sql/cleaning up | 0.000001 | +--------------------------------+----------+ 15 rows in set (0.00 sec)
参考资料:http://dev.mysql.com/doc/refman/5.7/en/performance-schema-query-profiling.html
http://blog.51yip.com/mysql/1056.html
变长字段须要额外的2个字节,固定长度字段不须要额外的字节。而null都须要1个字节的额外空间,因此之前有个说法:索引字段最好不要为NULL,由于NULL让统计更加复杂,而且须要额外的存储空间。这个结论在此获得了证明。
key_len的长度计算公式:
varchr(10)变长字段且容许NULL:10 * (Character Set:utf8=3,gbk=2,latin1=1) + 1(NULL) + 2(变长字段)
varchr(10)变长字段且不容许NULL:10 * (Character Set:utf8=3,gbk=2,latin1=1) + 2(变长字段)
char(10)固定字段且容许NULL:10 * (Character Set:utf8=3,gbk=2,latin1=1) + 1(NULL)
char(10)固定字段且容许NULL:10 * (Character Set:utf8=3,gbk=2,latin1=1)
参考资料:http://www.ittang.com/2014/0612/13360.html
http://dev.mysql.com/doc/refman/5.7/en/dynindex-sysvar.html
http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html
http://www.cnblogs.com/rollenholt/p/3776923.html
http://blog.csdn.net/sxingming/article/details/52628531 http://blog.itpub.net/7728585/viewspace-2132521 http://blog.sina.com.cn/s/blog_4de07d5e01010jc4.html