zoukankan      html  css  js  c++  java
  • MySQL 使用经验

    本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/10

    在索引中完成排序

    SELECT thread_id FROM thread_5 WHERE id = 11074781 AND status = 0 AND thread_type = 0 ORDER BY post_time LIMIT 0, 50

    增加在id 、status、thread_type 、post_time上的4字段复合索引。

    原因:通过优化前后的执行计划对比可发现,优化后消除了filesort,直接利用索引的有序性避开额外的排序操作

    image
    image

    对filesort的解释:

    当不能使用索引生成排序结果的时候,MYSQL需要自己进行排序,如果数据量小则在内存中进行,如果数据量则需要使用磁盘,不过MYSQL将这个过程统一称为文件排序(filesort),即使完全是内存排序不需要任何磁盘文件时也是如此。
    如果需要排序的数据量小于排序缓冲区,MYSQL使用内存进行快速排序操作,如果内存不够排序,那么MYSQL会先将数据分块,对独立的块使用快速排序进行排序,并将各个块的排序结果放在磁盘上,然后将各个排序好的块进行合并,最后返回排序结果。

    MYSQL有2中排序算法:(不再赘述2种排序的意思)
    2次排序传输
    单次排序传输

    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

    image
    image
    image

    合理建立索引

    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

    mysql 普通日志与慢速日志

    http://www.cnblogs.com/coser/archive/2011/11/08/2241674.html

    mysql子查询

    http://www.cnblogs.com/zhengyun_ustc/archive/2013/11/29/slowquery3.html
    依赖子查询
    select_type为DEPENDENT SUBQUERY,子查询的第一个select依赖外部的查询。

    mysql replace into与on duplicate key update区别

    replace没有保留旧值,而on duplicate key update类似于update,保留了旧值

    Handler_%

    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表排序工作如下,假定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表中指定的表、列和程序级权限。

    EXPLAIN Impossible WHERE noticed after reading const tables

    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

    查看事务锁

    常用查看锁的方式

    show engine innodb status

    没有明确指出谁持有锁,但是可以显示出事务在等待锁

    innoodb_locks

    当前出现的锁,但是无法得出哪个事务持有锁,哪个事务等待锁

    innodb_lock_waits

    显示哪个事务持有锁,哪个事务等待锁,但是无法显示正在阻塞中的事务的MySQL进程ID

    innodb_trx

    可以查看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/

    innodb_autoinc_lock_mode

    翻译自: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,可能分配给该语句的值没有被用到(更新)

    innodb_autoinc_lock_mode不同取值

    0("traditional” lock mode)

    表锁

    1("consecutive” lock mode)

    “块插入”使用表锁。
    简单插入仅锁定分配自增值的过程,一次性分配所需的数量

    2("interleaved lock mode)

    均不使用表锁,仅锁定分配自增值的过程。
    仅能保证唯一并且单调自增,但是不保证连续。
    但是可能会造成主从不一致。

    Index Merge

    explain extended

    http://dev.mysql.com/doc/refman/5.1/en/explain-extended.html

    filtered

    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 LEFT JOIN B ON 条件表达式

    要想过滤掉A中的数据,必须使用where,即使on中包含有A表中的列的限制条件,也不会过滤A的任何数据

    Created_%

    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的全局值都会增加。

    NLJ BNL MRR

    ICP

    翻译自: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是干嘛的了:

    image

    ICP BUG

    Foreground and Background Threads

    前台线程的程序,必须等所有的前台线程运行完毕后才能退出;而后台线程的程序,只要前台的线程都终止了,那么后台的线程就会自动结束并推出程序。
    一般前台线程用于需要长时间等待的任务,比如监听客户端的请求;后台线程一般用于处理时间较短的任务,比如处理客户端发过来的请求信息。

    Performance_Schema.threads

    每一个服务器端进程在该表中都有一行,表明是否启用监控和历史事件日志

    mysql> SELECT * FROM threadsG
    *************************** 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的区别:

    • threads表不需要锁资源,对服务器性能有最小的影响。相反,INFORMATION_SCHEMA.PROCESSLIST与SHOW PROCESSLIST对服务器性能有着负面的影响因为他们需要锁资源。
    • threads表提供的信息更加全面。
    • 监控是可控制的(可以被关闭)
    • INFORMATION_SCHEMA.PROCESSLIST与SHOW PROCESSLIST需要用户有PROCESS权限,threads表只需要用户有SELECT权限

    重要的字段:

    • PROCESSLIST_ID:PROCESSLIST_ID 与INFORMATION_SCHEMA.PROCESSLIST的ID值、SHOW PROCESSLIST的ID值相同、select connection_id()返回值相同

    • INSTRUMENTED:被线程执行的事件是否被MySQL监控(Whether events executed by the thread are instrumented,不知道理解的对不对。。。),值为YES或者NO

      • 对于前台线程,INSTRUMENTED 的初始值是由setup_actors中匹配的行决定的,匹配是根据 PROCESSLIST_USER和PROCESSLIST_HOST的值匹配的。
      • 对于后台线程,INSTRUMENTED的默认值是YES。setup_actors对此值没有影响,因为后台线程没有相对应得用户。
      • 对于任何线程,INSTRUMENTED的值都是可以在线程的生命周期内更改的。

      当被监控的事件执行时,一定会有如下的事实:

      • setup_consumers 对应的值是YES
      • threads.INSTRUMENTED的值是YES
      • setup_instruments对应的值是YES
    • History:与INSTRUMENTED类似

    其余字段的值看参考资料吧。

    参考资料:http://dev.mysql.com/doc/refman/5.7/en/threads-table.html

    Performance_Schema.setup_instruments

    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

    Performance_Schema.setup_consumers

    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

    Performance_Schema.setup_actors

    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

    Performance_Schema.events_statements_history_long

    Performance Schema Instrument Naming Conventions

    http://dev.mysql.com/doc/refman/5.7/en/performance-schema-instrument-naming.html

    Query Profiling Using Performance Schema

    如下例子示例了如何像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

    MySQL命令行参数

    http://blog.51yip.com/mysql/1056.html

    key_len长度计算方法

    变长字段需要额外的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

    MySQL的语句执行顺序

    image
    http://www.cnblogs.com/rollenholt/p/3776923.html

    varbinary vs varchar

    http://blog.csdn.net/sxingming/article/details/52628531
    http://blog.itpub.net/7728585/viewspace-2132521
    http://blog.sina.com.cn/s/blog_4de07d5e01010jc4.html

  • 相关阅读:
    Python的if判断与while循环
    python基础之数据类型与变量
    网络基础之网络协议篇
    操作系统简介
    计算机基础之计算机硬件系统
    从头开始编写一个Orchard网上商店模块(3)
    从头开始编写一个Orchard网上商店模块(2)
    从头开始编写一个Orchard网上商店模块(1)
    var和dynamic的区别及如何正确使用dynamic ?
    [DOM Event Learning] Section 3 jQuery事件处理基础 on(), off()和one()方法使用
  • 原文地址:https://www.cnblogs.com/zhangyachen/p/8035707.html
Copyright © 2011-2022 走看看