久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      實(shí)踐(2)–MySQL性能優(yōu)化

      實(shí)踐(2)--MySQL性能優(yōu)化

      相關(guān)學(xué)習(xí)推薦:mysql教程

      前言

      • MySQL索引底層數(shù)據(jù)結(jié)構(gòu)與算法
      • MySQL性能優(yōu)化原理-前篇
      • 實(shí)踐(1)–MySQL性能優(yōu)化

      上一篇 《實(shí)踐(1)–MySQL性能優(yōu)化》我們講了數(shù)據(jù)庫(kù)表設(shè)計(jì)的一些原則,Explain工具的介紹、SQL語(yǔ)句優(yōu)化索引的最佳實(shí)踐,本篇繼續(xù)來(lái)聊聊 MySQL 如何選擇合適的索引。

      MySQL Trace 工具

      MySQL 最終是否選擇走索引或者一張表涉及多個(gè)索引,最終是如何選擇索引,可以使用 trace 工具來(lái)一查究竟,開(kāi)啟 trace工具會(huì)影響 MySQL 性能,所以只能臨時(shí)分析 SQL 使用,用完之后立即關(guān)閉。

      案例分析

      講 trace 工具之前我們先來(lái)看一個(gè)案例:

      # 示例表CREATE TABLE`employees`(`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',`age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',`position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位',`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時(shí)間', PRIMARY KEY (`id`), KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE  )ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='員工記錄表';  INSERT INTO employees(name,age,position,hire_time)VALUES('ZhangSan',23,'Manager',NOW());INSERT INTO employees(name,age,position,hire_time)VALUES('HanMeimei', 23,'dev',NOW());INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());復(fù)制代碼

      MySQL 如何選擇合適的索引

      EXPLAIN select * from employees where name > 'a';復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      如果用name索引需要遍歷name字段聯(lián)合索引樹(shù),然后還需要根據(jù)遍歷出來(lái)的主鍵值去主鍵索引樹(shù)里再去查出最終數(shù)據(jù),成本比全表掃描還高,可以用覆蓋索引優(yōu)化,這樣只需要遍歷name字段的聯(lián)合索引樹(shù)就能拿到所有結(jié)果,如下:

      EXPLAIN select name,age,position from employees where name > 'a' ;復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化
      EXPLAIN select * from employees where name > 'zzz' ;復(fù)制代碼

      對(duì)于上面這兩種 name>'a'name>'zzz' 的執(zhí)行結(jié)果,mysql最終是否選擇走索引或者一張表涉及多個(gè)索引,mysql最終如何選擇索引,我們可以用trace工具來(lái)一查究竟,開(kāi)啟trace工具會(huì)影響mysql性能,所以只能臨時(shí)分析sql使用,用完之后立即關(guān)閉。

      trace工具用法

      開(kāi)啟/關(guān)閉Trace

      #開(kāi)啟traceset session optimizer_trace="enabled=on",end_markers_in_json=on; #關(guān)閉traceset session optimizer_trace="enabled=off";復(fù)制代碼

      案例1

      執(zhí)行這兩句sql

      select * from employees where name >'a' order by position;sELECT * FROM information_schema.OPTIMIZER_TRACE;  復(fù)制代碼

      提出來(lái)trace值,詳見(jiàn)注釋

      {  "steps": [     {      "join_preparation": { --第一階段:SQL準(zhǔn)備階段        "select#": 1,        "steps": [           {            "expanded_query": "/* select#1 */ select `employees`.`id` AS `id`,`employees`.`name` AS `name`,`employees`.`age` AS `age`,`employees`.`position` AS `position`,`employees`.`hire_time` AS `hire_time` from `employees` where (`employees`.`name` > 'a') order by `employees`.`position`"           }         ] /* steps */       } /* join_preparation */     },     {      "join_optimization": { --第二階段:SQL優(yōu)化階段        "select#": 1,        "steps": [           {            "condition_processing": { --條件處理              "condition": "WHERE",              "original_condition": "(`employees`.`name` > 'a')",              "steps": [                 {                  "transformation": "equality_propagation",                  "resulting_condition": "(`employees`.`name` > 'a')"                 },                 {                  "transformation": "constant_propagation",                  "resulting_condition": "(`employees`.`name` > 'a')"                 },                 {                  "transformation": "trivial_condition_removal",                  "resulting_condition": "(`employees`.`name` > 'a')"                 }               ] /* steps */             } /* condition_processing */           },           {            "substitute_generated_columns": {             } /* substitute_generated_columns */           },           {            "table_dependencies": [ --表依賴詳情               {                "table": "`employees`",                "row_may_be_null": false,                "map_bit": 0,                "depends_on_map_bits": [                 ] /* depends_on_map_bits */               }             ] /* table_dependencies */           },           {            "ref_optimizer_key_uses": [             ] /* ref_optimizer_key_uses */           },           {            "rows_estimation": [ --預(yù)估表的訪問(wèn)成本               {                "table": "`employees`",                "range_analysis": {                  "table_scan": { --全表掃描                    "rows": 3,  --掃描行數(shù)                    "cost": 3.7 --查詢成本                   } /* table_scan */,                  "potential_range_indexes": [ --查詢可能使用的索引                     {                      "index": "PRIMARY",   --主鍵索引                      "usable": false,                      "cause": "not_applicable"                     },                     {                      "index": "idx_name_age_position", --輔助索引                      "usable": true,                      "key_parts": [                        "name",                        "age",                        "position",                        "id"                       ] /* key_parts */                     },                     {                      "index": "idx_age",                      "usable": false,                      "cause": "not_applicable"                     }                   ] /* potential_range_indexes */,                  "setup_range_conditions": [                   ] /* setup_range_conditions */,                  "group_index_range": {                    "chosen": false,                    "cause": "not_group_by_or_distinct"                   } /* group_index_range */,                  "analyzing_range_alternatives": { --分析各個(gè)索引使用成本                    "range_scan_alternatives": [                       {                        "index": "idx_name_age_position",                        "ranges": [                          "a < name"    --索引使用范圍                         ] /* ranges */,                        "index_pes_for_eq_ranges": true,                        "rowid_ordered": false, --使用該索引獲取的記錄是否按照主鍵排序                        "using_mrr": false,                        "index_only": false,    --是否使用覆蓋索引                        "rows": 3,  --索引掃描行數(shù)                        "cost": 4.61, --索引使用成本                        "chosen": false, --是否選擇該索引                        "cause": "cost"                       }                     ] /* range_scan_alternatives */,                    "analyzing_roworder_intersect": {                      "usable": false,                      "cause": "too_few_roworder_scans"                     } /* analyzing_roworder_intersect */                   } /* analyzing_range_alternatives */                 } /* range_analysis */               }             ] /* rows_estimation */           },           {            "considered_execution_plans": [               {                "plan_prefix": [                 ] /* plan_prefix */,                "table": "`employees`",                "best_access_path": { --最優(yōu)訪問(wèn)路徑                  "considered_access_paths": [ --最終選擇的訪問(wèn)路徑                     {                      "rows_to_scan": 3,                      "access_type": "scan",    --訪問(wèn)類型:為sacn,全表掃描                      "resulting_rows": 3,                      "cost": 1.6,                      "chosen": true,   --確定選擇                      "use_tmp_table": true                     }                   ] /* considered_access_paths */                 } /* best_access_path */,                "condition_filtering_pct": 100,                "rows_for_plan": 3,                "cost_for_plan": 1.6,                "sort_cost": 3,                "new_cost_for_plan": 4.6,                "chosen": true               }             ] /* considered_execution_plans */           },           {            "attaching_conditions_to_tables": {              "original_condition": "(`employees`.`name` > 'a')",              "attached_conditions_computation": [               ] /* attached_conditions_computation */,              "attached_conditions_summary": [                 {                  "table": "`employees`",                  "attached": "(`employees`.`name` > 'a')"                 }               ] /* attached_conditions_summary */             } /* attaching_conditions_to_tables */           },           {            "clause_processing": {              "clause": "ORDER BY",              "original_clause": "`employees`.`position`",              "items": [                 {                  "item": "`employees`.`position`"                 }               ] /* items */,              "resulting_clause_is_simple": true,              "resulting_clause": "`employees`.`position`"             } /* clause_processing */           },           {            "reconsidering_access_paths_for_index_ordering": {              "clause": "ORDER BY",              "index_order_summary": {                "table": "`employees`",                "index_provides_order": false,                "order_direction": "undefined",                "index": "unknown",                "plan_changed": false               } /* index_order_summary */             } /* reconsidering_access_paths_for_index_ordering */           },           {            "refine_plan": [               {                "table": "`employees`"               }             ] /* refine_plan */           }         ] /* steps */       } /* join_optimization */     },     {      "join_execution": {   --第三階段:SQL執(zhí)行階段                                                            "select#": 1,        "steps": [           {            "filesort_information": [               {                "direction": "asc",                "table": "`employees`",                "field": "position"               }             ] /* filesort_information */,            "filesort_priority_queue_optimization": {              "usable": false,              "cause": "not applicable (no LIMIT)"             } /* filesort_priority_queue_optimization */,            "filesort_execution": [             ] /* filesort_execution */,            "filesort_summary": {              "rows": 3,              "examined_rows": 3,              "number_of_tmp_files": 0,              "sort_buffer_size": 200704,              "sort_mode": "<sort_key, packed_additional_fields>"             } /* filesort_summary */           }         ] /* steps */       } /* join_execution */     }   ] /* steps */ }復(fù)制代碼

      結(jié)論:全表掃描的成本低于索引掃描,所以MySQL最終選擇全表掃描。

      案例2

      select * from employees where name > 'zzz' order by position;SELECT * FROM information_schema.OPTIMIZER_TRACE;  復(fù)制代碼

      結(jié)論:查看trace字段可知索引掃描的成本低于全表掃描,所以MySQL最終選擇索引掃描。

      常見(jiàn)SQL深入優(yōu)化

      Order byGroup by 優(yōu)化

      案例1

      EXPLAIN select * from employees where name = 'ZhangSan' and position = 'dev' order by age復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      分析:

      利用最左前綴法則:中間字段不能斷,因此查詢用到了 name索引 ,從 key_len = 74 也能看出,age 索引列用在排序過(guò)程過(guò)程中,因?yàn)?Extra 字段里沒(méi)有 using filesort 。

      案例2

      EXPLAIN select * from employees where name = 'ZhangSan' order by position復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      分析:

      從 explain 的執(zhí)行結(jié)果來(lái)看:key_len = 74,查詢使用了 name 索引,由于用了 position 進(jìn)行排序,跳過(guò)了 age,出現(xiàn)了 Using filesort。

      案例3

      EXPLAIN select * from employees where name = 'ZhangSan' order by age,position復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      分析:

      查詢只用到索引name,age 和 position 用于排序,無(wú)Using filesort。

      案例4

      EXPLAIN select * from employees where name = 'ZhangSan' order by position,age復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      分析:

      和案例3中explain的執(zhí)行結(jié)果一樣,但是出現(xiàn)了Using filesort ,因?yàn)樗饕膭?chuàng)建順序?yàn)?name,age,position , 但是排序的時(shí)候 age 和 position 顛倒位置了。

      案例5

      EXPLAIN select * from employees where name = 'ZhangSan' and age = 18 order by position,age復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      分析:

      與案例4對(duì)比,在Extra中并未出現(xiàn)** Using filesort **,因?yàn)?age 為常量,在排序中被優(yōu)化,所以索引未顛倒,不會(huì)出現(xiàn) Using filesort

      案例6

      EXPLAIN select * from employees where name = 'ZhangSan' order by age asc, position desc;復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      分析:

      雖然排序的字段列與索引順序一樣,且 order by 默認(rèn)升序,這里 position desc 變成列降序,導(dǎo)致與索引的排序方式不同,從而產(chǎn)生 Using filesort 。MySQL8 以上版本有降序索引可以支持該種查詢方式。

      案例7

      EXPLAIN select * from employees where name in ('ZhangSan', 'hjh') order by age, position;復(fù)制代碼

      分析:

      對(duì)于排序來(lái)說(shuō),多個(gè)相等條件也是范圍查詢。

      案例8

      EXPLAIN select * from employees where name > 'a' order by name;復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      可以用覆蓋索引優(yōu)化

      EXPLAIN select name,age,position from employees where name > 'a' order by name;復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      優(yōu)化總結(jié)

      1. MySQL支持兩種方式的排序 filesortindex。Using index 是指MySQL 掃描索引本身完成排序。index 效率高,filesort 效率低。
      2. order by 滿足兩種情況會(huì)使用 Using index.
        • order by 語(yǔ)句使用索引最左前例。
        • 使用 where 子句與 order by 子句條件列組合滿足索引最左前例。
      3. 盡量在索引列上完成排序,遵循索引建立索引創(chuàng)建的順序)時(shí)的最左前綴法則。
      4. 如果 order by 的條件不在索引列上,就會(huì)產(chǎn)生 Using filesort。
      5. 能用覆蓋索引盡量用覆蓋索引。
      6. group by 和 order by 很類似,其實(shí)質(zhì)是先排序后分組,遵循索引創(chuàng)建順序的最左前綴法則。對(duì)于 group by 的優(yōu)化如果不需要排序的可以加上 order by null 禁止排序。注意:where 高于 having,能寫在 where 中的限定條件就不要去 having 限定了。

      Using filesort文件排序原理

      filesort文件排序方式

      • 單路排序:是一次性取出滿足條件行的所有字段,然后在 sort buffer 中進(jìn)行排序;用 trace 工具可以看到 sort_mode 信息里顯示 < sort_key, additional_fields > 或者 < sort_key, packed_additional_fields >。
      • 雙路排序(又叫回表排序模式):是首先根據(jù)相應(yīng)的條件取出相應(yīng)的排序字段可以直接定位運(yùn)行數(shù)據(jù)的行ID,然后在 sort buffer 中進(jìn)行排序,排序完后需要再次取回其它需要的字段;用 trace 工具可以看到 sort_mode 信息里顯示 < sort_key, rowid >

      MySQL 通過(guò)比較系統(tǒng)變量 max_length_for_sort_data (默認(rèn)1024字節(jié)) 的大小和需要查詢的字段總大小來(lái)判斷使用那種排序模式。

      • 如果max_length_for_sort_data 比查詢的字段的總長(zhǎng)度大,那么使用單路排序模式;
      • 如果max_length_for_sort_data 比查詢字段的總長(zhǎng)度小,那么使用雙路排序模式。

      驗(yàn)證各種排序方式

      EXPLAIN select * from employees where name = 'ZhangSan' order by position;復(fù)制代碼
      實(shí)踐(2)--MySQL性能優(yōu)化

      查看下這條sql對(duì)應(yīng)trace結(jié)果如下(只展示排序部分):

      set session optimizer_trace="enabled=on",end_markers_in_json=on; #開(kāi)啟traceselect * from employees where name = 'ZhangSan' order by position;select * from information_schema.OPTIMIZER_TRACE;復(fù)制代碼
            "join_execution": { --SQL執(zhí)行階段         "select#": 1,         "steps": [           {             "filesort_information": [               {                 "direction": "asc",                 "table": "`employees`",                 "field": "position"               }             ] /* filesort_information */,             "filesort_priority_queue_optimization": {               "usable": false,               "cause": "not applicable (no LIMIT)"             } /* filesort_priority_queue_optimization */,             "filesort_execution": [             ] /* filesort_execution */,             "filesort_summary": { --文件排序信息               "rows": 1, --預(yù)計(jì)掃描行數(shù)               "examined_rows": 1, --參數(shù)排序的行               "number_of_tmp_files": 0, --使用臨時(shí)文件的個(gè)數(shù),這個(gè)只如果為0代表全部使用的sort_buffer內(nèi)存排序,否則使用的磁盤文件排序               "sort_buffer_size": 200704, --排序緩存的大小               "sort_mode": "<sort_key, packed_additional_fields>" --排序方式,這里用的單路排序             } /* filesort_summary */           }         ] /* steps */       } /* join_execution */復(fù)制代碼

      修改系統(tǒng)變量 max_length_for_sort_data (默認(rèn)1024字節(jié)) ,employees 表所有字段長(zhǎng)度總和肯定大于10字節(jié)

      set max_length_for_sort_data = 10;  select * from employees where name = 'ZhangSan' order by position;select * from information_schema.OPTIMIZER_TRACE;復(fù)制代碼

      trace排序部分結(jié)果:

            "join_execution": {         "select#": 1,         "steps": [           {             "filesort_information": [               {                 "direction": "asc",                 "table": "`employees`",                 "field": "position"               }             ] /* filesort_information */,             "filesort_priority_queue_optimization": {               "usable": false,               "cause": "not applicable (no LIMIT)"             } /* filesort_priority_queue_optimization */,             "filesort_execution": [             ] /* filesort_execution */,             "filesort_summary": {               "rows": 1,               "examined_rows": 1,               "number_of_tmp_files": 0,               "sort_buffer_size": 53248,               "sort_mode": "<sort_key, rowid>" --排序方式,這里用餓的雙路排序             } /* filesort_summary */           }         ] /* steps */       } /* join_execution */    復(fù)制代碼

      單路排序的詳細(xì)過(guò)程:

      1. 從索引 name 找到第一個(gè)滿足 name='ZhangSan' 條件的主鍵 id;
      2. 根據(jù)主鍵id取出整行,取出所有字段的值,存入sort_buffer中;
      3. 從索引name找到下一個(gè)滿足 name='ZhangSan' 條件的主鍵 id;
      4. 重復(fù)步驟2、3直到不滿足 name='ZhangSan';
      5. 對(duì) sort_buffer 中的數(shù)據(jù)按照字段 position 進(jìn)行排序;
      6. 返回結(jié)果給客戶端

      雙路排序的詳細(xì)過(guò)程:

      1. 從索引 name 找到第一個(gè)滿足 name='ZhangSan' 的主鍵id;
      2. 根據(jù)主鍵id取出整行,把排序字段 position 和 主鍵id 這兩個(gè)字段放到 sort_buffer 中;
      3. 從索引 name 取下一個(gè)滿足 name='ZhangSan' 記錄的主鍵id;
      4. 重復(fù)步驟3、4直到不滿足 name='ZhangSan';
      5. 對(duì) sort_buffer 中的字段 position 和 主鍵id按照 position 進(jìn)行排序;
      6. 遍歷排序好的 id 和 字段 position,按照 id 的值回到原表中取出所有的字段的值返回給客戶端。

      對(duì)比兩個(gè)排序模式,單路排序會(huì)把所有需要查詢的字段都放到 sort_buffer 中,而雙路排序只會(huì)把主鍵和需要排序的字段放到 sort_buffer 中進(jìn)行排序,然后再通過(guò)主鍵回到原表查詢需要的字段。

      如果MySQL排序內(nèi)存配置的比較小并且沒(méi)有條件繼續(xù)增加了,可以適當(dāng)把 max_length_for_sort_data 配置小點(diǎn),讓優(yōu)化器選擇使用雙路排序算法,可以在 sort_buffer 中一次排序

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)