面试-03-数据库和事务专题
目錄
關(guān)鍵字
1 存儲引擎
1.1 Mysql 的架構(gòu)圖
1.2 選擇合適的存儲引擎
1.3 InnoDB 和 MyIsam 的對比
1.3.1 兩者的區(qū)別
1.3.2 關(guān)于兩者的總結(jié)
1.4 一張表,里面有 ID 自增主鍵,當(dāng) insert 了 17 條記錄之后,刪除了第15、16、17 條記錄,再把 Mysql 重啟,再 insert 一條記錄,這條記錄的 ID 是 18 還是 15
1.5 Mysql 服務(wù)器默認(rèn)端口是什么
1.6 LIKE 聲明中的 % 和 _ 是什么意思
2 事務(wù)的概念和 ACID 特性
2.1 概念
2.2 ACID 特性
2.2.1 原子性(Atomicity)
2.2.2 一致性(Consistency)
2.2.3 隔離性(Isolation)
2.2.4 持久性(Durability)
3 事務(wù)隔離級別詳解
3.1 四個隔離級別
3.2 隔離級別與一致性問題的關(guān)系
3.3 默認(rèn)隔離級別的理解
4 分布式事務(wù)
4.1 什么是分布式事務(wù)
4.2 CAP 定理和 Base 理論
4.3 2PC 二階段分步提交
4.4 TCC (Try Confirm Cancel)
4.5 項(xiàng)目中的使用
5 數(shù)據(jù)庫鎖和并發(fā)策略
5.1 數(shù)據(jù)庫鎖的分類
5.1.1 行級鎖
5.1.2 表級鎖
5.1.3 頁級鎖
5.2 數(shù)據(jù)庫的并發(fā)策略
5.2.1 樂觀鎖
5.2.2 悲觀鎖
5.2.3 鎖表問題
6 存儲過程和觸發(fā)器
6.1 存儲過程(特定功能的 SQL 語句集)
6.2 觸發(fā)器(一段能自動執(zhí)行的程序)
7 數(shù)據(jù)庫結(jié)構(gòu)優(yōu)化
7.1 三大范式
7.2 數(shù)據(jù)庫命名規(guī)范
7.3 建表時優(yōu)化
7.3.1 列選擇原則
7.3.2 主鍵的選擇
7.3.3 反范式設(shè)計(jì)表
7.3.4 將字段很多的表分解成多個表
7.3.5 增加中間表
7.3.6 增加冗余字段
8 查詢語句優(yōu)化
8.1 數(shù)據(jù)庫優(yōu)化概述
8.1.1 什么是優(yōu)化?
8.1.2 數(shù)據(jù)庫性能參數(shù)
8.2 常見的查詢優(yōu)化
8.2.1 緩存優(yōu)化
8.2.2 讀寫分離
8.2.3 mysql 的分庫分表
8.3 EXPLAIN
8.3.1 id
8.3.2 select_type
8.3.3 table
8.3.4 type
8.3.5 possible_keys
8.3.6 key
8.3.7 key_len
8.3.8 ref
8.3.9 rows
8.3.10 Extra
8.4 in 型子查詢引出的陷阱
8.5 from 型子查詢
8.6 count() 優(yōu)化
8.7 group by
8.8 union 優(yōu)化
8.9 巧用變量
8.10 子查詢優(yōu)化
9 索引及優(yōu)化
9.1 索引的作用
9.2 索引的分類
9.3 索引的優(yōu)點(diǎn)
9.4 索引的缺點(diǎn)
9.5 哪些情況或字段適合加索引
9.6 哪些情況不適合創(chuàng)建索引
9.7 哪些情況會造成索引失效
9.8 索引相關(guān) sql
9.8.1 創(chuàng)建索引
9.8.2 刪除索引
9.8.3 查看索引
9.9 索引原理
9.9.1 BTree 索引
9.9.2 B+Tree 索引
9.9.3 hash 索引
9.9.4 btree 索引的常見誤區(qū)
9.10 索引優(yōu)化策略
9.10.1 最左前綴原則
9.10.2 注意避免冗余索引
9.10.3 使用索引查詢需要注意
9.10.4 聚簇索引與非聚簇索引
9.10.5 對于左前綴不易區(qū)分的列,建立索引的技巧
9.10.6 多列索引
9.10.7 限制每張表上的索引數(shù)量(5 個)
9.10.8 禁止給表中的每一列都建立單獨(dú)的索引
9.10.9 每個 Innodb 表必須有個主鍵
9.10.10 常見索引列建議
9.10.11 如何選擇索引列的順序
9.10.12 避免建立冗余索引和重復(fù)索引(增加了查詢優(yōu)化器生成執(zhí)行計(jì)劃的時間)
9.10.13 對于頻繁的查詢優(yōu)先考慮使用覆蓋索引
9.10.14 索引 SET 規(guī)范
9.11 主鍵、外鍵和索引的區(qū)別
9.12 mysql:刪除表數(shù)據(jù)drop、truncate 和 delete 的用法
10 插入數(shù)據(jù)的優(yōu)化? ?
10.1 MyISAM
10.1.1 禁用索引
10.1.2 禁用唯一性檢查
10.1.3 批量插入數(shù)據(jù)
10.1.4 使用 LOAD DATA INFILE
10.2 InnoDB
10.2.1 禁用唯一性檢查
10.2.2 禁用外鍵檢查
10.2.3 禁止自動提交
11 服務(wù)器優(yōu)化
11.1 優(yōu)化服務(wù)器硬件
11.2 優(yōu)化 MySQL 的參數(shù)
12 測試定位
12.1 測試指標(biāo)
12.1.1 發(fā)現(xiàn)系統(tǒng)運(yùn)行緩慢,如何定位和分析查詢慢的 sql 語句
12.2 測試工具
12.2.1 mysqlslap
12.2.2 sysbench
12.3 查看 mysql 的進(jìn)程狀態(tài)
關(guān)鍵字
| 1.? 存儲引擎 ? 1.1? InnoDB/MyIsam ? ? ? ? ?InnoDB(默認(rèn))-->支持外鍵,支持事務(wù),提供表級鎖和行級鎖(默認(rèn)) ? ? ? ? ?MyIsam-->只有表級鎖 |
| 2.? 事務(wù)的概念和 ACID 特性 ? 2.1? 事務(wù)(Start Transaction/rollback/commit) ? 2.2? ACID ? ? ? ? C(一致性)是目的,A(原子性)、I(隔離性)、D(持久性)是手段 |
| 3.? 事務(wù)隔離級別詳解 ? 3.1? 隔離級別(讀未提交/讀已提交/可重復(fù)讀/串行化) ? 3.2? 一致性問題(更新丟失/臟讀/不可重復(fù)讀/幻讀) ? 3.3??InnoDB 存儲引擎的默認(rèn)支持的隔離級別是可重讀,達(dá)到了可串行化隔離級別 |
| 4.? 分布式事務(wù) ? 4.1? 開啟分布式事務(wù)(@EnableDistributedTransaction) ? 4.2? 使用分布式事務(wù)(@LcnTransation/@TCCTransation) |
| 5.? 數(shù)據(jù)庫鎖和并發(fā)策略 ? 5.1? 鎖(表鎖/頁級鎖/行鎖/讀鎖/寫鎖/共享鎖/排它鎖) ? 5.2? 并發(fā)策略(樂觀鎖/悲觀鎖) |
| 6.? 存儲過程和觸發(fā)器 |
| 7.? 數(shù)據(jù)庫結(jié)構(gòu)優(yōu)化 ? 7.1? 三大范式 ? 7.2? 數(shù)據(jù)庫命名規(guī)范 ? 7.3? 建表時優(yōu)化 ? ? ? ? 【列選擇原則】【主鍵的選擇】【反范式設(shè)計(jì)表】【將字段很多的表分解成多個表】 ? ? ? ? 【增加中間表】【增加冗余字段】 |
| 8.? 查詢語句優(yōu)化 ? 8.1? 讀寫分離,分庫分表 ? ? ? ? ?垂直拆分[分庫]? 水平拆分[分表] ? 8.2? explain ? ? ? ?【id】【select_type】【table】【type】【possible_keys】【key】 ? 8.3? 常見的查詢優(yōu)化 ? ? ? ?【避免直接使用 select *】【避免造成索引失效】【IN 包含的值不應(yīng)過多】【?limit 1】 ? ? ? ?【能用 inner join 就不用 left join right join,如必須使用 一定要已小表為驅(qū)動】 ? 8.4??子查詢優(yōu)化 ? ? ? ?【子查詢靈活,但執(zhí)行效率并不高,執(zhí)行子查詢時,MYSQL 需要創(chuàng)建臨時表】 ? ? ? ?【使用連接查詢 (JOIN)? 代替子查詢】 |
| 9.? 索引及優(yōu)化 ? 9.1??索引的分類 ? ? ? ?【普通索引】【主鍵索引】【唯一索引】【聯(lián)合索引】【全文索引】 ? 9.2? 索引的優(yōu)缺點(diǎn) ? ? ? ? 優(yōu)點(diǎn):【提高檢索的速度】【建立唯一索引或者主鍵索引,數(shù)據(jù)的唯一性】 ? ? ? ? ? ? ? ? ? ?【表連接的連接條件】【分組和排序字句進(jìn)行數(shù)據(jù)檢索】 ? ? ? ? 缺點(diǎn):【創(chuàng)建索引和維護(hù)索引會耗費(fèi)時間】【索引文件會占用物理空間】 ? ? ? ? ? ? ? ? ? ?【增刪改索引也要動態(tài)的維護(hù)】 9.3? 創(chuàng)建索引 ? ? ? ?【經(jīng)常需要搜索的列】【主鍵列上可以確保列的唯一性】【表與表的連接條件上】【排序,分組】 9.4? 索引失效 ? ? ? ? 【最左前綴原則】【只有or前后的字段都帶索引才生效】【like 查詢以%開頭會失效】 ? ? ? ? 【在索引的列上使用表達(dá)式或者函數(shù)會使索引失效】 ? ? ? ? 【如果列類型是字符串,那一定要在條件中將數(shù)據(jù)使用單引號引用起來,否則不使用索引】 9.5? 索引優(yōu)化 ? ? ? ? ?【最左前綴原則】【注意避免冗余索引】【聚簇索引與非聚簇索引】 ? ? ? ? ? 【既存儲了主鍵值,又存儲了行數(shù)據(jù),這種結(jié)構(gòu)稱為 "聚簇索引"、innodb為聚簇索引】 |
| 10.? 插入數(shù)據(jù)的優(yōu)化 ? 10.1? 影響插入速度的原因 ? ? ? ? ? 【索引】【唯一性校驗(yàn)】【一次插入的數(shù)據(jù)條數(shù)】 ? 10.2??MyISAM ? ? ? ? ?【禁用索引】【禁用唯一性檢查】【批量插入數(shù)據(jù)】 ? 10.3??InnoDB ? ? ? ? ? 【禁用唯一性檢查】【禁用外鍵檢查】【禁止自動提交】 |
1 存儲引擎
| 數(shù)據(jù)庫存儲引擎是數(shù)據(jù)庫底層軟件組織,數(shù)據(jù)庫管理系統(tǒng)(DBMS)使用數(shù)據(jù)引擎進(jìn)行創(chuàng)建、查詢、更新和刪除數(shù)據(jù) 不同的存儲引擎提供不同的存儲機(jī)制、索引技巧、鎖定水平等功能,使用不同的存儲引擎,還可以獲得特定的功能 現(xiàn)在許多不同的數(shù)據(jù)庫管理系統(tǒng)都支持多種不同的數(shù)據(jù)引擎 |
| 存儲引擎主要有: 1. MyIsam,?2. InnoDB, 3. Memory,4. Archive,5. Federated |
| Mysql 在 5.5.5 之前默認(rèn)存儲引擎是 MyISAM;在此之后默認(rèn)存儲引擎是 InnoDB |
1.1 Mysql 的架構(gòu)圖
| Server 層:主要包括連接器、查詢緩存、分析器、優(yōu)化器、執(zhí)行器等,所有跨存儲引擎的功能都在這一層實(shí)現(xiàn),比如存儲過程、觸發(fā)器、視圖,函數(shù)等,還有一個通用的日志模塊 binglog 日志模塊 | |
| 存儲引擎:主要負(fù)責(zé)數(shù)據(jù)的存儲和讀取,采用可以替換的插件式架構(gòu),支持 InnoDB、MyISAM、Memory 等多個存儲引擎,其中 InnoDB 引擎有自有的日志模塊 redolog 模塊 現(xiàn)在最常用的存儲引擎是 InnoDB,它從MySQL 5.5.5 版本開始就被當(dāng)做默認(rèn)存儲引擎了 |
1.2 選擇合適的存儲引擎
| 當(dāng)創(chuàng)建表時,應(yīng)根據(jù)表的應(yīng)用場景選擇適合的存儲引擎 |
| MyISAM 表最適合于大量的數(shù)據(jù)讀而少量數(shù)據(jù)更新的混合操作 |
| MyISAM 表的另一種適用情形是使用壓縮的只讀表 |
| 如果查詢中包含較多的數(shù)據(jù)更新操作,應(yīng)使用 InnoDB 其行級鎖機(jī)制和多版本的支持為數(shù)據(jù)讀取和更新的混合操作提供了良好的并發(fā)機(jī)制 |
| 可使用 MEMORY 存儲引擎來存儲非永久需要的數(shù)據(jù),或者是能夠從基于磁盤的表中重新生成的數(shù)據(jù) |
1.3 InnoDB 和 MyIsam 的對比
1.3.1 兩者的區(qū)別
| MyISAM | InnoDB | |
| count 運(yùn)算上的區(qū)別 | 因?yàn)?MyISAM 緩存有表 meta-data(行數(shù)等),因此在做 COUNT(*) 時對于一個結(jié)構(gòu)很好的查詢是不需要消耗多少資源的 | 對于 InnoDB 來說,則沒有這種緩存 |
| 是否支持事務(wù)和崩潰后的安全恢復(fù) | MyISAM 強(qiáng)調(diào)的是性能,每次查詢具有原子性,其執(zhí)行數(shù)度比 InnoDB 類型更快,但是不提供事務(wù)支持 | InnoDB 提供事務(wù)支持事務(wù),外部鍵等高級數(shù)據(jù)庫功能 具有事務(wù)(commit)、回滾(rollback)和崩潰修復(fù)能力(crash recovery capabilities)的事務(wù)安全(transaction-safe (ACIDcompliant))型表 |
| 是否支持外鍵 | MyISAM 不支持 | InnoDB 支持 |
| 是否支持行級鎖 | MyISAM 只有表級鎖(table-level locking) | InnoDB 支持行級鎖(row-level locking)和表級鎖,默認(rèn)為行級鎖 |
1.3.2 關(guān)于兩者的總結(jié)
| MyISAM 更適合讀密集的表,而 InnoDB 更適合寫密集的的表 在數(shù)據(jù)庫做主從分離的情況下,經(jīng)常選擇 MyISAM 作為主庫的存儲引擎 |
| 一般來說,如果需要事務(wù)支持,并且有較高的并發(fā)讀取頻率(MyISAM 的表鎖的粒度太大,所以當(dāng)該表寫并發(fā)量較高時,要等待的查詢就會很多了),InnoDB 是不錯的選擇 如果你的數(shù)據(jù)量很大(MyISAM 支持壓縮特性可以減少磁盤的空間占用),而且不需要支持事務(wù)時,MyISAM 是最好的選擇 |
1.4 一張表,里面有 ID 自增主鍵,當(dāng) insert 了 17 條記錄之后,刪除了第15、16、17 條記錄,再把 Mysql 重啟,再 insert 一條記錄,這條記錄的 ID 是 18 還是 15
| 如果表的類型是?MyISAM,那么是?18 因?yàn)?MyISAM 表會把自增主鍵的最大 ID 記錄到數(shù)據(jù)文件里,重啟 MySQL 自增主鍵的最大 ID 也不會丟失 |
| 如果表的類型是?InnoDB,那么是 15 InnoDB 表只是把自增主鍵的最大 ID 記錄到內(nèi)存中,所以重啟數(shù)據(jù)庫或者是對表進(jìn)行 OPTIMIZE 操作,都會導(dǎo)致最大 ID 丟失 |
| 存儲引擎不同,索引生成的文件也不同 ? Myisam 生成 3 個文件,非聚簇索引 ? ? frm 為表結(jié)構(gòu)文件 ? ? MYD 是數(shù)據(jù)文件 ? ? MYI? 是索引文件 ? InnoDB 生成 2 個文件,聚簇索引 ? ? frm 為表結(jié)構(gòu)文件 ? ? ibd 為索引+數(shù)據(jù) |
1.5 Mysql 服務(wù)器默認(rèn)端口是什么
| Mysql 服務(wù)器的默認(rèn)端口是 3306 |
1.6 LIKE 聲明中的 % 和 _ 是什么意思
| % 對應(yīng)于?0 個或更多字符 _? 只是 LIKE 語句中的一個字符 |
2 事務(wù)的概念和 ACID 特性
2.1 概念
| 事務(wù)(TRANSACTION)表示一個完整的不可分割的業(yè)務(wù),批量的 DML 語句同時成功或者同時失敗;原子性不可再分最基本單元,可以看做一個完整的事件,通常一個事務(wù)可以對應(yīng)一個完整的業(yè)務(wù)流程 |
| 可以保證多個操作原子性,要么全成功,要么全失敗 對于數(shù)據(jù)庫來說事務(wù)保證批量的 DML 要么全成功,要么全失敗 |
| 事務(wù)中存在一些概念: |
| a) 事務(wù)(Transaction):保證批量的 DML 同時成功或同時失敗;一批操作(一組 DML) b) 開啟事務(wù)(Start Transaction) c) 回滾事務(wù)(rollback) d) 提交事務(wù)(commit) e) SET AUTOCOMMIT:禁用或啟用事務(wù)的自動提交模式 |
| 當(dāng)執(zhí)行 DML 語句時其實(shí)就是開啟一個事務(wù) |
| 關(guān)于事務(wù)的回滾需要注意:只能回滾 insert、delete 和 update 語句,不能回滾 select(回滾 select 沒有任何意義),對于 create、drop、alter 這些無法回滾 |
| 事務(wù)只對 DML 有效果 |
| 注意:rollback,或者 commit 后事務(wù)就結(jié)束了 |
2.2 ACID 特性
事務(wù)具有四個特征 ACID
2.2.1 原子性(Atomicity)
| 整個事務(wù)中的所有操作,必須作為一個單元全部完成(或全部取消) |
| undo log 名為回滾日志,是實(shí)現(xiàn)原子性的關(guān)鍵,當(dāng)事務(wù)回滾時能夠撤銷所有已經(jīng)成功執(zhí)行的 sql 語句,他需要記錄你要回滾的相應(yīng)日志信息 |
2.2.2 一致性(Consistency)
| 在事務(wù)開始之前與結(jié)束之后,數(shù)據(jù)庫都保持一致狀態(tài)? |
| 從數(shù)據(jù)庫層面,數(shù)據(jù)庫通過原子性、隔離性、持久性來保證一致性 也就是說 ACID 四大特性之中,C(一致性)是目的,A(原子性)、I(隔離性)、D(持久性)是手段,是為了保證一致性,數(shù)據(jù)庫提供的手段 數(shù)據(jù)庫必須要實(shí)現(xiàn) AID 三大特性,才有可能實(shí)現(xiàn)一致性;例如,原子性無法保證,顯然一致性也無法保證 |
| 但是,如果你在事務(wù)里故意寫出違反約束的代碼,一致性還是無法保證的 例如,你在轉(zhuǎn)賬的例子中,你的代碼里故意不給 B 賬戶加錢,那一致性還是無法保證;因此,還必須從應(yīng)用層角度考慮 從應(yīng)用層面,通過代碼判斷數(shù)據(jù)庫數(shù)據(jù)是否有效,然后決定回滾還是提交數(shù)據(jù) |
2.2.3 隔離性(Isolation)
| 一個事務(wù)不會影響其他事務(wù)的運(yùn)行 |
| 事務(wù)的隔離性是利用的是鎖和 MVCC(Multi Version Concurrency Control?多版本并發(fā)控制)機(jī)制 |
2.2.4 持久性(Durability)
| 在事務(wù)完成以后,該事務(wù)對數(shù)據(jù)庫所作的更改將持久地保存在數(shù)據(jù)庫之中,并不會被回滾 |
| 事務(wù)的持久性是利用 Innodb 的 redo log |
3 事務(wù)隔離級別詳解
| 事務(wù)的隔離級別決定了事務(wù)之間可見的級別 |
3.1 四個隔離級別
| InnoDB 實(shí)現(xiàn)了四個隔離級別,用以控制事務(wù)所做的修改,并將修改通告至其它并發(fā)的事務(wù): |
| 讀未提交(READ UMCOMMITTED) 允許一個事務(wù)可以看到其他事務(wù)未提交的修改 |
| 讀已提交(READ COMMITTED) 允許一個事務(wù)只能看到其他事務(wù)已經(jīng)提交的修改,未提交的修改是不可見的 |
| 可重復(fù)讀(REPEATABLE READ) 確保如果在一個事務(wù)中執(zhí)行兩次相同的 SELECT 語句,都能得到相同的結(jié)果,不管其他事務(wù)是否提交這些修改(銀行總賬) |
| 串行化(SERIALIZABLE) 【序列化】 該隔離級別為 InnoDB 的缺省設(shè)置 將一個事務(wù)與其他事務(wù)完全地隔離 |
3.2 隔離級別與一致性問題的關(guān)系
| 當(dāng)多個客戶端并發(fā)地訪問同一個表時,可能出現(xiàn)下面的一致性問題: | |||
| 更新丟失:兩個并行操作,后進(jìn)行的操作覆蓋掉了先進(jìn)行操作的操作結(jié)果,被稱作更新丟失 | |||
| 臟讀(Dirty Read):一個事務(wù)在提交之前,在事務(wù)過程中修改的數(shù)據(jù),被其他事務(wù)讀取到了 某個事務(wù)已更新一份數(shù)據(jù),另一個事務(wù)在此時讀取了同一份數(shù)據(jù),由于某些原因,前一個RollBack了操作,則后一個事務(wù)所讀取的數(shù)據(jù)就會是不正確的 | |||
| 不可重復(fù)讀(Non-repeatable Read):一個事務(wù)在提交之前,在事務(wù)過程中讀取以前的數(shù)據(jù)卻發(fā)現(xiàn)數(shù)據(jù)發(fā)生了改變 在一個事務(wù)的兩次查詢之中數(shù)據(jù)不一致,這可能是兩次查詢過程中間插入了一個事務(wù)更新的原有的數(shù)據(jù) | |||
| 幻讀(Phantom Read):一個事務(wù)按照相同的條件重新讀取以前檢索過的數(shù)據(jù)時,卻發(fā)現(xiàn)了其他事務(wù)插入的新數(shù)據(jù) 在一個事務(wù)的兩次查詢中數(shù)據(jù)筆數(shù)不一致,例如有一個事務(wù)查詢了幾列(Row)數(shù)據(jù),而另一個事務(wù)卻在此時插入了新的幾列數(shù)據(jù),先前的事務(wù)在接下來的查詢中,就會發(fā)現(xiàn)有幾列數(shù)據(jù)是它先前所沒有的 | |||
| 通用的解決思路是更新丟失通過應(yīng)用程序完全避免 而其他的問題點(diǎn)則通過調(diào)整數(shù)據(jù)庫事務(wù)隔離級別來解決 | |||
| 事務(wù)的隔離機(jī)制的實(shí)現(xiàn)手段之一就是利用鎖 | |||
| 隔離級別 | 臟讀 | 不可重復(fù)讀 | 幻影讀 |
| 讀未提交(READ UMCOMMITTED) | √ | √ | √ |
| 讀已提交(READ COMMITTED) | × | √ | √ |
| 可重復(fù)讀(REPEATABLE READ) | × | × | √ |
| 串行化(SERIALIZABLE) | × | × | × |
3.3 默認(rèn)隔離級別的理解
| 與 SQL 標(biāo)準(zhǔn)不同的地方在于 InnoDB 存儲引擎在 REPEATABLE-READ(可重讀)事務(wù)隔離級別下使用的是Next-Key Lock 鎖算法,因此可以避免幻讀的產(chǎn)生,這與其他數(shù)據(jù)庫系統(tǒng)(如 SQL Server)是不同的 所以說 InnoDB 存儲引擎的默認(rèn)支持的隔離級別是 REPEATABLE-READ(可重讀) 已經(jīng)可以完全保證事務(wù)的隔離性要求,即達(dá)到了 SQL 標(biāo)準(zhǔn)的 SERIALIZABLE(可串行化)隔離級別 |
| 因?yàn)楦綦x級別越低,事務(wù)請求的鎖越少,所以大部分?jǐn)?shù)據(jù)庫系統(tǒng)的隔離級別都是 READ-COMMITTED(讀取提交內(nèi)容),但是你要知道的是 InnoDB 存儲引擎默認(rèn)使用 REPEATABLE-READ(可重讀)并不會有任何性能損失 |
| InnoDB 存儲引擎在分布式事務(wù)的情況下一般會用到 SERIALIZABLE(可串行化)隔離級別 |
4 分布式事務(wù)
4.1 什么是分布式事務(wù)
| 傳統(tǒng)項(xiàng)目中用一個 mysql 數(shù)據(jù)庫,mysql 數(shù)據(jù)庫當(dāng)中 InnoDB 引擎是支持 ACID 事務(wù)特性的,能夠滿足我們的事務(wù)要求,但現(xiàn)在分布式項(xiàng)目中,我們會有多個微服務(wù)處理不同業(yè)務(wù),這些微服務(wù)都是獨(dú)立的進(jìn)程,數(shù)據(jù)庫也都是相互獨(dú)立,但是事務(wù)可能要橫跨這些微服務(wù), 比如訂單功能, 如果我支付訂單成功在訂單微服務(wù)中我需要改變訂單狀態(tài), 在庫存微服務(wù)中需要減對應(yīng)的庫存, 在積分微服務(wù)中得加對應(yīng)積分, 這是一個整體的事務(wù),這種事務(wù)就是分布式事務(wù) |
4.2 CAP 定理和 Base 理論
| CAP 定理是由加州大學(xué)伯克利分校 Eric Brewer 教授提出來的,他指出 WEB 服務(wù)無法同時滿足以下三個屬性 |
| 一致性(Consistency): 客戶端知道一系列的操作都會同時發(fā)生(生效) |
| 可用性(Availability): 每個操作都必須以可預(yù)期的響應(yīng)結(jié)束 |
| 分區(qū)容錯性(Partition tolerance): 即使出現(xiàn)單個組件無法可用,操作依然可以完成 |
| 在分布式系統(tǒng)中,我們往往追求的是可用性,它的重要程序比一致性要高,那么如何實(shí)現(xiàn)高可用性呢?那就是另外一個理論 BASE 理論,它是用來對 CAP 定理進(jìn)行進(jìn)一步擴(kuò)充的 BASE 理論指的是 |
| Basically Available(基本可用):分布式系統(tǒng)在出現(xiàn)故障時,允許損失部分可用功能,保證核心功能可用 |
| Soft state(軟狀態(tài)):允許系統(tǒng)中存在中間狀態(tài),這個狀態(tài)不影響系統(tǒng)可用性,這里指的是 CAP 中的不一致 |
| Eventually consistent(最終一致性):指經(jīng)過一段時間后,所有節(jié)點(diǎn)數(shù)據(jù)都將會達(dá)到一致 |
| BASE 理論是對 CAP 中的一致性和可用性進(jìn)行一個權(quán)衡的結(jié)果,理論的核心思想就是:我們無法做到強(qiáng)一致,但每個應(yīng)用都可以根據(jù)自身的業(yè)務(wù)特點(diǎn),采用適當(dāng)?shù)姆绞絹硎瓜到y(tǒng)達(dá)到最終一致性(Eventual consistency) |
4.3 2PC 二階段分步提交
| 其核心思想是將分布式事務(wù)拆分成本地事務(wù)進(jìn)行處理 |
| 白話 各個服務(wù)在處理數(shù)據(jù)過程中,會進(jìn)行預(yù)備提交操作,預(yù)備操作可以反映出各個節(jié)點(diǎn)是否都能夠提交,預(yù)備操作 如果成功的話,那么事務(wù)協(xié)調(diào)器會執(zhí)行每個數(shù)據(jù)庫的 commit 操作 如果在預(yù)提交過程當(dāng)中有一個節(jié)點(diǎn)不能夠提交,事務(wù)協(xié)調(diào)器會執(zhí)行每個數(shù)據(jù)庫的 roll back 操作 |
| 官腔 事務(wù)管理器要求每個涉及到事務(wù)的數(shù)據(jù)庫預(yù)提交(precommit)此操作,并反映是否可以提交 事務(wù)協(xié)調(diào)器要求每個數(shù)據(jù)庫提交數(shù)據(jù),或者回滾數(shù)據(jù) |
| 實(shí)現(xiàn)的思路 消息生產(chǎn)方,需要額外建一個消息表,并記錄消息發(fā)送狀態(tài) 消息表和業(yè)務(wù)數(shù)據(jù)要在一個事務(wù)里提交,也就是說他們要在一個數(shù)據(jù)庫里面 然后消息會經(jīng)過 MQ 發(fā)送到消息的消費(fèi)方 如果消息發(fā)送失敗,會進(jìn)行重試發(fā)送消息消費(fèi)方,需要處理這個消息,并完成自己的業(yè)務(wù)邏輯 此時如果本地事務(wù)處理成功,表明已經(jīng)處理成功了,如果處理失敗,那么就會重試執(zhí)行 如果是業(yè)務(wù)上面的失敗,可以給生產(chǎn)方發(fā)送一個業(yè)務(wù)補(bǔ)償消息,通知生產(chǎn)方進(jìn)行回滾等操作 生產(chǎn)方和消費(fèi)方定時掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發(fā)送一遍 如果有靠譜的自動對賬補(bǔ)賬邏輯,這種方案還是非常實(shí)用的 這種方案遵循 BASE 理論,采用的是最終一致性 |
4.4 TCC (Try Confirm Cancel)
| Try 階段:嘗試執(zhí)行,完成所有業(yè)務(wù)檢查(一致性),預(yù)留必需業(yè)務(wù)資源(準(zhǔn)隔離性) |
| Confirm 階段:確認(rèn)真正執(zhí)行業(yè)務(wù),不作任何業(yè)務(wù)檢查,只使用 try 階段預(yù)留的業(yè)務(wù)資源,Confirm 操作滿足冪等性;要求具備冪等設(shè)計(jì),Confirm 失敗后需要進(jìn)行重試;默認(rèn)只要 Try 成功,Confirm 一定成功 |
| Cancel 階段:取消執(zhí)行,釋放 Try 階段預(yù)留的業(yè)務(wù)資源,Cancel 操作滿足冪等性;Cancel 階段的異常和Confirm 階段異常處理方案基本上一致 |
| 舉個簡單的例子:如果你用 100 元買了一瓶水, Try 階段:你需要向你的錢包檢查是否夠 100 元并鎖住這 100 元,水也是一樣的。如果有一個失敗,則進(jìn)行 Cancel(釋放這 100 元和這一瓶水),如果 Cancel 失敗不論什么失敗都進(jìn)行重試 Cancel,所以需要保持冪等。如果都成功,則進(jìn)行 Confirm,確認(rèn)這 100 元被扣,和這一瓶水被賣,如果 Confirm 失敗無論什么失敗則重試(會依靠活動日志進(jìn)行重試) |
4.5 項(xiàng)目中的使用
| 使用開源框架 TX-LCN 分布式事務(wù)框架 5.0 版本對代碼的侵入比較少,而且提供了完整的 SpringCloud 和dubbo 注解解決方案,項(xiàng)目組引入該框架的成本非常低,并且有專門的公司對這個開源項(xiàng)目進(jìn)行維護(hù) |
| 該框架主要有兩大核心部分 TM 事務(wù)的協(xié)調(diào)管理器需要獨(dú)立運(yùn)行(在源碼中有該工程,并且整合了 docker 只需要改下配置文件中 mysql、redis 的地址即可運(yùn)行) |
| 所有參與分布式事務(wù)的微服務(wù)項(xiàng)目需要引入客戶端依賴 com.codingapi.txlcn txlcn-txmsg-netty 5.0.2.RELEASE |
| 在啟動類當(dāng)中,使用 @EnableDistributedTransaction 注解開啟分布式事務(wù) |
| 在配置文件中配置好 Tx-manager 的連接地址 |
| 在需要進(jìn)行事務(wù)管理的 service 方法中 如果要使用分布式事務(wù)加上 @LcnTransation;如果要使用 TCC 的模式需要加上@TCCTransation 注解并且在該注解指定 confirm 對應(yīng)的方法以及 cancel 對應(yīng)的方法 |
5 數(shù)據(jù)庫鎖和并發(fā)策略
| https://www.jianshu.com/p/69ed3b9d858c |
| 事務(wù)的隔離機(jī)制的實(shí)現(xiàn)手段之一就是利用鎖 |
5.1 數(shù)據(jù)庫鎖的分類
| MyIsam 實(shí)現(xiàn)了表鎖 表鎖可以針對數(shù)據(jù)庫表加鎖,在鎖的靈活性上不如行鎖 表鎖分為兩種鎖:讀鎖與寫鎖 |
| InnoDB 存儲引擎實(shí)現(xiàn)了行鎖與表鎖(意向鎖) 行鎖可以以行為單位對數(shù)據(jù)集進(jìn)行鎖定 行鎖也分為兩種鎖:共享鎖與排他鎖 InnoDB 對于 Update、Delete、Insert 語句會自動給涉及的數(shù)據(jù)集隱式的加上排他鎖 對于 select 語句 InnoDB 不會加任何鎖 |
| 共享鎖:允許一個事務(wù)讀取一行,阻止其他事務(wù)獲得相同數(shù)據(jù)集的排他鎖 但允許其他事務(wù)獲取共享鎖 顯示加鎖 select * from table where ... lock in share mode |
| 共享鎖,事務(wù)加多少,都能讀 修改是唯一的,必須等待前一個事務(wù) commit,才可以下個修改 |
| 排他鎖:允許獲得排他鎖的事務(wù)更新數(shù)據(jù),阻止其他事務(wù)取得相同數(shù)據(jù)集的共享與排他鎖 但是可以對獲取了排他鎖的數(shù)據(jù)集進(jìn)行單純的查詢訪問 顯示加鎖 sql select * from table where ... for update |
| 排他鎖,不允許其它事務(wù)增加共享或排他鎖讀取 修改是唯一的,必須等待前一個事務(wù) commit,才可以下一個修改 |
| InnoDB 的行鎖的實(shí)現(xiàn)方式是基于索引項(xiàng)的 這意味著即使你嘗試獲取不同行的排他鎖,若使用了相同的索引鍵,也可能會造成鎖沖突 |
5.1.1 行級鎖
| 行級鎖是一種排他鎖,防止其他事務(wù)修改此行 行鎖的特點(diǎn)是開銷大,加鎖慢;會出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高 |
| 在使用以下語句時,Oracle 會自動應(yīng)用行級鎖: |
| 1. INSERT、UPDATE、DELETE、SELECT ? FOR UPDATE [OF columns] [WAIT n | NOWAIT]; |
| 2. SELECT ? FOR UPDATE 語句允許用戶一次鎖定多條記錄進(jìn)行更新 |
| 3. 使用 COMMIT 或 ROLLBACK 語句釋放鎖 |
5.1.2 表級鎖
| 表鎖的特點(diǎn)是開銷小,加鎖快;不會出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低 |
| 表示對當(dāng)前操作的整張表加鎖,它實(shí)現(xiàn)簡單,資源消耗較少,被大部分 MySQL 引擎支持 最常使用的 MYISAM與 INNODB 都支持表級鎖定 |
5.1.3 頁級鎖
| 頁級鎖是 MySQL 中鎖定粒度介于行級鎖和表級鎖中間的一種鎖 表級鎖速度快,但沖突多,行級沖突少,但速度慢 所以取了折衷的頁級,一次鎖定相鄰的一組記錄 BDB 支持頁級鎖 |
5.2 數(shù)據(jù)庫的并發(fā)策略
5.2.1 樂觀鎖
| 樂觀鎖認(rèn)為一個用戶讀數(shù)據(jù)的時候,別人不會去寫自己所讀的數(shù)據(jù);悲觀鎖就剛好相反,覺得自己讀數(shù)據(jù)庫的時候,別人可能剛好在寫自己剛讀的數(shù)據(jù),其實(shí)就是持一種比較保守的態(tài)度;時間戳就是不加鎖,通過時間戳來控制并發(fā)出現(xiàn)的問題 |
| 解決方案為:時間戳就是在數(shù)據(jù)庫表中單獨(dú)加一列時間戳,比如 "TimeStamp",每次讀出來的時候,把該字段也讀出來,當(dāng)寫回去的時候,把該字段加 1,提交之前 ,跟數(shù)據(jù)庫的該字段比較一次,如果比數(shù)據(jù)庫的值大的話,就允許保存,否則不允許保存,這種處理方法雖然不使用數(shù)據(jù)庫系統(tǒng)提供的鎖機(jī)制,但是這種方法可以大大提高數(shù)據(jù)庫處理的并發(fā)量 Select max(nub) ,version from biao Update biao set nub=nub+1,version=vsersion+1 where id=id and version =version |
5.2.2 悲觀鎖
| 悲觀鎖就是在讀取數(shù)據(jù)的時候,為了不讓別人修改自己讀取的數(shù)據(jù),就會先對自己讀取的數(shù)據(jù)加鎖,只有自己把數(shù)據(jù)讀完了,才允許別人修改那部分?jǐn)?shù)據(jù),或者反過來說,就是自己修改某條數(shù)據(jù)的時候,不允許別人讀取該數(shù)據(jù),只有等自己的整個事務(wù)提交了,才釋放自己加上的鎖,才允許其他用戶訪問那部分?jǐn)?shù)據(jù) |
| 悲觀鎖所說的加 "鎖",其實(shí)分為幾種鎖,分別是:排它鎖(寫鎖)和共享鎖(讀鎖) |
| 解決方案為 sql 語句后邊加上 for update 例子:select id,nam from biao for update |
5.2.3 鎖表問題
| 鎖表發(fā)生在 insert、update 、delete 中 |
| 鎖表的原理是數(shù)據(jù)庫使用獨(dú)占式鎖機(jī)制,當(dāng)執(zhí)行 insert、update 和 delete 的語句時,對表進(jìn)行鎖住,直到發(fā)生commite 或者回滾或者退出數(shù)據(jù)庫用戶 |
| 鎖表的原因 : 當(dāng)多個連接(數(shù)據(jù)庫連接)同時對一個表的數(shù)據(jù)進(jìn)行更新操作,那么速度將會越來越慢,持續(xù)一段時間后將出現(xiàn)數(shù)據(jù)表被鎖的現(xiàn)象,從而影響到其它的查詢及更新 A 程序執(zhí)行了對 tableA 的 insert ,并還未 commite 時,B 程序也對 tableA 進(jìn)行 insert 則此時會發(fā)生資源正忙的異常就是鎖表 鎖表常發(fā)生于并發(fā)而不是并行(并行時,一個線程操作數(shù)據(jù)庫時,另一個線程是不能操作數(shù)據(jù)庫的,cpu和 i/o 分配原則) |
| 減少鎖表的概率 減少 insert 、update 、delete 語句執(zhí)行到 commite 之間的時間;具體點(diǎn)批量執(zhí)行改為單個執(zhí)行、優(yōu)化 sql 自身的非執(zhí)行速度 如果異常對事物進(jìn)行回滾 |
| https://blog.csdn.net/fragrant_no1/article/details/79727263 https://www.cnblogs.com/shuilangyizu/p/7383183.html |
6 存儲過程和觸發(fā)器
6.1 存儲過程(特定功能的 SQL 語句集)
| 一組為了完成特定功能的 SQL 語句集,存儲在數(shù)據(jù)庫中,經(jīng)過第一次編譯后再次調(diào)用不需要再次編譯,用戶通過指定存儲過程的名字并給出參數(shù)(如果該存儲過程帶有參數(shù))來執(zhí)行它;存儲過程是數(shù)據(jù)庫中的一個重要對象 |
| 存儲過程優(yōu)化思路: |
| 1. 盡量利用一些 sql 語句來替代一些小循環(huán),例如聚合函數(shù),求平均函數(shù)等 |
| 2. 中間結(jié)果存放于臨時表,加索引 |
| 3. 少使用游標(biāo) sql 是個集合語言,對于集合運(yùn)算具有較高性能;而 cursors 是過程運(yùn)算;比如對一個 100 萬行的數(shù)據(jù)進(jìn)行查詢;游標(biāo)需要讀表 100 萬次,而不使用游標(biāo)則只需要少量幾次讀取 |
| 4. 事務(wù)越短越好 sqlserver 支持并發(fā)操作;如果事務(wù)過多過長,或者隔離級別過高,都會造成并發(fā)操作的阻塞,死鎖;導(dǎo)致查詢極慢,cpu 占用率極地 |
| 5. 使用 try-catch 處理錯誤異常 |
| 6. 查找語句盡量不要放在循環(huán)內(nèi) |
6.2 觸發(fā)器(一段能自動執(zhí)行的程序)
| 觸發(fā)器是一段能自動執(zhí)行的程序,是一種特殊的存儲過程,觸發(fā)器和普通的存儲過程的區(qū)別是: 觸發(fā)器是當(dāng)對某一個表進(jìn)行操作時觸發(fā) 諸如:update、insert、delete 這些操作的時候,系統(tǒng)會自動調(diào)用執(zhí)行該表上對應(yīng)的觸發(fā)器 |
| SQL Server 2005 中觸發(fā)器可以分為兩類:DML 觸發(fā)器和 DDL 觸發(fā)器,其中 DDL 觸發(fā)器它們會影響多種數(shù)據(jù)定義語言語句而激發(fā),這些語句有 create、 alter、drop 語句 |
7 數(shù)據(jù)庫結(jié)構(gòu)優(yōu)化
| 一個好的數(shù)據(jù)庫設(shè)計(jì)方案對于數(shù)據(jù)庫的性能往往會起到事半功倍的效果 |
| 需要考慮數(shù)據(jù)冗余、查詢和更新的速度、字段的數(shù)據(jù)類型是否合理等多方面的內(nèi)容 |
7.1 三大范式
| 第一范式:有主鍵,具有原子性,字段不可分割 |
| 第二范式:完全依賴,表中非主鍵列不存在對主鍵的部分依賴 要求每個表只描述一 件事情 |
| 第三范式:沒有傳遞依賴,表中的列不存在對非主鍵列的傳遞依賴 |
| 數(shù)據(jù)庫設(shè)計(jì)盡量遵循三范式,但是還是根據(jù)實(shí)際情況進(jìn)行取舍,有時可能會拿冗余換速度,最終用目的要滿足客戶需求 |
7.2 數(shù)據(jù)庫命名規(guī)范
| 所有數(shù)據(jù)庫對象名稱必須使用小寫字母并用下劃線分割 |
| 所有數(shù)據(jù)庫對象名稱禁止使用 MySQL 保留關(guān)鍵字(如果表名中包含關(guān)鍵字查詢時,需要將其用單引號括起來) |
| 數(shù)據(jù)庫對象的命名要能做到見名識意,并且最后不要超過 32 個字符 |
| 臨時庫表必須以 tmp 為前綴并以日期為后綴,備份表必須以 bak 為前綴并以日期 (時間戳) 為后綴 |
| 所有存儲相同數(shù)據(jù)的列名和列類型必須一致(一般作為關(guān)聯(lián)列,如果查詢時關(guān)聯(lián)列類型不一致會自動進(jìn)行數(shù)據(jù)類型隱式轉(zhuǎn)換,會造成列上的索引失效,導(dǎo)致查詢效率降低) |
7.3 建表時優(yōu)化
7.3.1 列選擇原則
| 1: 字段類型優(yōu)先級 整型 > date,time > char,varchar > blob |
| 2: 夠用就行,不要慷慨 (如 smallint,varchar(N)) 原因:列的字段越大,建立索引時所需要的空間也就越大,這樣一頁中所能存儲的索引節(jié)點(diǎn)的數(shù)量也就越少也越少,在遍歷時所需要的 IO 次數(shù)也就越多,索引的性能也就越差 |
| 3: 盡量避免用 NULL() 原因:索引 NULL 列需要額外的空間來保存,所以要占用更多的空間進(jìn)行比較和計(jì)算時要對 NULL 值做特別的處理 |
| 4:對于非負(fù)型的數(shù)據(jù) (如自增 ID、整型 IP) 來說,要優(yōu)先使用無符號整型來存儲 原因:無符號相對于有符號可以多出一倍的存儲空間 SIGNED INT -2147483648~2147483647 UNSIGNED INT 0~4294967295 VARCHAR(N) 中的 N 代表的是字符數(shù),而不是字節(jié)數(shù),使用 UTF8 存儲 255 個漢字 Varchar(255)=765 個字節(jié) 過大的長度會消耗更多的內(nèi)存 |
| 5:避免使用 TEXT、BLOB 數(shù)據(jù)類型,最常見的 TEXT 類型可以存儲 64k 的數(shù)據(jù) |
| 6:同財(cái)務(wù)相關(guān)的金額類數(shù)據(jù)必須使用 decimal 類型 非精準(zhǔn)浮點(diǎn):float,double 精準(zhǔn)浮點(diǎn):decimal Decimal 類型為精準(zhǔn)浮點(diǎn)數(shù),在計(jì)算時不會丟失精度 占用空間由定義的寬度決定,每 4 個字節(jié)可以存儲 9 位數(shù)字,并且小數(shù)點(diǎn)要占用一個字節(jié)可用于存儲比 bigint 更大的整型數(shù)據(jù) |
7.3.2 主鍵的選擇
| 主鍵用來區(qū)分,查找,和關(guān)聯(lián)數(shù)據(jù),非常重要 |
| 1.在 myisam 中,字符串索引會被壓縮,用字符串做主鍵性能不如整型 |
| 2. 用遞增的值,不要用離散的值,離散值會導(dǎo)致文件在磁盤的位置有間隔,浪費(fèi)空間且不易連續(xù)讀取 |
| 3. UUID,也是逐步增長的,可以去掉 "-",轉(zhuǎn)換為整數(shù) |
7.3.3 反范式設(shè)計(jì)表
| 反范式的目的--減少表的關(guān)聯(lián)查詢 |
| 常用辦法: 冗余字段和冗余表 冗余字段:表中某字段存儲另一表的統(tǒng)計(jì)信息 冗余表:表中統(tǒng)計(jì)或匯總其他表的信息,又稱匯總表 |
| 本質(zhì) 1.? 空間換時間 2.? 大任務(wù)分成小任務(wù),分散執(zhí)行 |
7.3.4 將字段很多的表分解成多個表
| 對于字段較多的表,如果有些字段的使用頻率很低,可以將這些字段分離出來形成新表 |
| 因?yàn)楫?dāng)一個表的數(shù)據(jù)量很大時,會由于使用頻率低的字段的存在而變慢 |
| 舉例:商品表和商品描述表就是把描述字段拆分成一個單獨(dú)的表 |
7.3.5 增加中間表
| Note;不是多對多的中間表 |
| 對于需要經(jīng)常聯(lián)合查詢的表,可以建立中間表以提高查詢效率 |
| 通過建立中間表,將需要通過聯(lián)合查詢的數(shù)據(jù)插入到中間表中,然后將原來的聯(lián)合查詢改為對中間表的查詢 |
7.3.6 增加冗余字段
| 設(shè)計(jì)數(shù)據(jù)表時應(yīng)盡量遵循范式理論的規(guī)約,盡可能的減少冗余字段,讓數(shù)據(jù)庫設(shè)計(jì)看起來精致、優(yōu)雅 但是,合理的加入冗余字段可以提高查詢速度 |
| 表的規(guī)范化程度越高,表和表之間的關(guān)系越多,需要連接查詢的情況也就越多,性能也就越差 |
| 注意: 冗余字段的值在一個表中修改了,就要想辦法在其他表中更新,否則就會導(dǎo)致數(shù)據(jù)不一致的問題 |
| 舉例:商品表里增加類目名稱 |
8 查詢語句優(yōu)化
| 1: sql 語句的時間花在哪兒? 答: 等待時間,執(zhí)行時間 這兩個時間并非孤立的,如果單條語句執(zhí)行的快了,對其他語句的鎖定的也就少了 所以,我們來分析如何降低執(zhí)行時間 |
| 2: sql 語句的執(zhí)行時間,又花在哪兒了? 答: a: 查 ----> 沿著索引查,甚至全表掃描 b: 取 ----> 查到行后,把數(shù)據(jù)取出來(sending data) |
| 3: sql 語句的優(yōu)化思路? 答: 不查,通過業(yè)務(wù)邏輯來計(jì)算,比如論壇的注冊會員數(shù),我們可以根據(jù)前 3 個月統(tǒng)計(jì)的每天注冊數(shù),用程序來估算 少查,盡量精準(zhǔn)數(shù)據(jù),少取行;我們觀察新聞網(wǎng)站,評論內(nèi)容等,一般一次性取列表 10-30 條左右 必須要查,盡量走在索引上查詢行 取時,取盡量少的列 比如 select * from tableA,就取出所有列,不建議 比如 select * from tableA,tableB,取出 A,B 表的所有列 |
| 4: 如果定量分析查的多少行,和是否沿著索引查? 答: 用 explain 來分析 |
8.1 數(shù)據(jù)庫優(yōu)化概述
8.1.1 什么是優(yōu)化?
| 合理安排資源、調(diào)整系統(tǒng)參數(shù)使 MySQL 運(yùn)行更快、更節(jié)省資源 |
| 優(yōu)化是多方面的,包括查詢、更新、服務(wù)器等 |
| 原則:減少系統(tǒng)瓶頸,減少資源占用,增加系統(tǒng)的反應(yīng)速度 |
8.1.2 數(shù)據(jù)庫性能參數(shù)
| 使用 SHOW STATUS 語句查看 MySQL 數(shù)據(jù)庫的性能參數(shù) SHOW STATUS LIKE 'value' |
| 常用的參數(shù): Slow_queries 慢查詢次數(shù) Com_(CRUD) 操作的次數(shù) Uptime 上線時間 |
8.2 常見的查詢優(yōu)化
| SELECT 語句務(wù)必指明字段名稱(避免直接使用 select * ) |
| SQL 語句要避免造成索引失效的寫法 |
| SQL 語句中 IN 包含的值不應(yīng)過多 |
| 當(dāng)只需要一條數(shù)據(jù)的時候,使用 limit 1 ? ?第一個參數(shù)指定第一個返回記錄行的偏移量,第二個參數(shù)指定返回記錄行的最大數(shù)目 ? ? ? SELECT * FROM table LIMIT 5,10; // 檢索記錄行 6-15 ? ?為了檢索從某一個偏移量到記錄集的結(jié)束所有的記錄行,可以指定第二個參數(shù)為 -1 ? ? ?SELECT * FROM table LIMIT 95,-1; // 檢索記錄行 96-last ? 如果只給定一個參數(shù),它表示返回最大的記錄行數(shù)目 ? ? ?SELECT * FROM table LIMIT 5; //檢索前 5 個記錄行 ? ? ?換句話說,LIIT n 等價于 LIMIT 0,n |
| 如果排序字段沒有用到索引,就盡量少排序 |
| 如果限制條件中其他字段沒有索引,盡量少用 or |
| 盡量用 union all 代替 union |
| 避免在 where 子句中對字段進(jìn)行 null 值判斷 |
| 不建議使用 % 前綴模糊查詢 |
| 避免在 where 子句中對字段進(jìn)行表達(dá)式操作 |
| Join 優(yōu)化 能用 inner join 就不用 left join right join,如必須使用 一定要已小表為驅(qū)動 |
8.2.1 緩存優(yōu)化
| 為了提高查詢速度,我們可以通過不同的方式去緩存我們的結(jié)果從而提高響應(yīng)效率 當(dāng)我們的數(shù)據(jù)庫打開了 Query Cache(簡稱 QC)功能后,數(shù)據(jù)庫在執(zhí)行 SELECT 語句時,會將其結(jié)果放到 QC 中,當(dāng)下一次處理同樣的 SELECT請求時,數(shù)據(jù)庫就會從 QC 取得結(jié)果,而不需要去數(shù)據(jù)表中查詢 如果緩存命中率非常高的話,有測試表明在極端情況下可以提高效率 238% |
8.2.2 讀寫分離
| 如果數(shù)據(jù)庫的使用場景讀的操作比較的時候,為了避免寫的操作所造成的性能影響可以采用讀寫分離的架構(gòu),讀寫分離,解決的是,數(shù)據(jù)庫的寫入,影響了查詢的效率 讀寫分離的基本原理是讓主數(shù)據(jù)庫處理事務(wù)性增、改、刪操作(INSERT、UPDATE、DELETE),而從數(shù)據(jù)庫處理 SELECT 查詢操作 數(shù)據(jù)庫復(fù)制被用來把事務(wù)性操作導(dǎo)致的變更同步到集群中的從數(shù)據(jù)庫 |
8.2.3 mysql 的分庫分表
| 數(shù)據(jù)量越來越大時,單體數(shù)據(jù)庫無法滿足要求,可以考慮分庫分表 兩種拆分方案: 垂直拆分:(分庫)業(yè)務(wù)表太多? 將業(yè)務(wù)細(xì)化不同的小業(yè)務(wù)專門用一個庫來維護(hù) 水平拆分:(分表)單個表存的數(shù)據(jù)太多,裝不下了? 將該表查分成多個 分庫分表常用工具:MyCat、Sharding-JDBC |
8.3 EXPLAIN
| 在 MySQL 中可以使用 EXPLAIN 查看 SQL 執(zhí)行計(jì)劃,用法:EXPLAIN SELECT * FROM tb_item |
8.3.1 id
| SELECT 識別符 這是 SELECT 查詢序列號 這個不重要 |
| 代表 select 語句的編號,如果是連接查詢,表之間是平等關(guān)系,select 編號都是 1,從 1 開始 如果某 select 中有子查詢,則編號遞增 |
8.3.2 select_type
| 表示 SELECT 語句的類型 有以下幾種值: |
| ? |
| 1. SIMPLE |
| 表示簡單查詢,其中不包含連接查詢和子查詢 |
| 2. PRIMARY |
| 表示主查詢,或者是最外面的查詢語句 |
| ? |
| 3. UNION |
| 表示連接查詢的第 2 個或后面的查詢語句 |
| ? |
| 4. DEPENDENT UNION |
| UNION 中的第二個或后面的 SELECT 語句,取決于外面的查詢 |
| 5. UNION RESULT |
| 連接查詢的結(jié)果 |
| 6. SUBQUERY |
| 子查詢中的第 1 個 SELECT 語句 |
| ? |
| 7. DEPENDENT SUBQUERY |
| 子查詢中的第 1 個 SELECT 語句,取決于外面的查詢 |
| 8. DERIVED |
| SELECT(FROM 子句的子查詢) |
8.3.3 table
| 表示查詢的表 有可能是 實(shí)際的表名 如 select * from t1; 表的別名 如 select * from t2 as tmp; derived 如 from 型子查詢時 null 直接計(jì)算得結(jié)果,不用走表 |
8.3.4 type
| 表示表的連接類型 以下的連接類型的順序是從最佳類型到最差類型: |
| 1. system |
| 表僅有一行,這是 const 類型的特列,平時不會出現(xiàn),這個也可以忽略不計(jì) |
| 2. const |
| 數(shù)據(jù)表最多只有一個匹配行,因?yàn)橹黄ヅ湟恍袛?shù)據(jù),所以很快,常用于 PRIMARY KEY 或者 UNIQUE 索引的查詢,可理解為 const 是最優(yōu)化的 |
| ? |
| 3. eq_ref |
| mysql 手冊是這樣說的:"對于每個來自于前面的表的行組合,從該表中讀取一行 這可能是最好的聯(lián)接類型,除了 const 類型 它用在一個索引的所有部分被聯(lián)接使用并且索引是 UNIQUE 或 PRIMARY KEY" eq_ref 可以用于使用=比較帶索引的列 |
| ? |
| 4. ref |
| 查詢條件索引既不是 UNIQUE 也不是 PRIMARY KEY 的情況 ref 可用于=或操作符的帶索引的列 |
| ? |
| 5. ref_or_null |
| 該聯(lián)接類型如同 ref,但是添加了 MySQL 可以專門搜索包含 NULL 值的行 在解決子查詢中經(jīng)常使用該聯(lián)接類型的優(yōu)化 |
| 上面這五種情況都是很理想的索引使用情況 |
| 6. index_merge |
| 該聯(lián)接類型表示使用了索引合并優(yōu)化方法 在這種情況下,key 列包含了使用的索引的清單,key_len 包含了使用的索引的最長的關(guān)鍵元素 |
| 7. unique_subquery |
| 該類型替換了下面形式的 IN 子查詢的 ref: value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery 是一個索引查找函數(shù),可以完全替換子查詢,效率更高 |
| 8. index_subquery |
| 該聯(lián)接類型類似于 unique_subquery 可以替換 IN 子查詢,但只適合下列形式的子查詢中的非唯一索引: value IN (SELECT key_column FROM single_table WHERE some_expr) |
| 9. range |
| 只檢索給定范圍的行,使用一個索引來選擇行 |
| ? |
| 10. index |
| 該聯(lián)接類型與 ALL 相同,除了只有索引樹被掃描 這通常比 ALL 快,因?yàn)樗饕募ǔ1葦?shù)據(jù)文件小 |
| 11. ALL |
| 對于每個來自于先前的表的行組合,進(jìn)行完整的表掃描(性能最差) |
8.3.5 possible_keys
| 可能用到的索引 注意: 系統(tǒng)估計(jì)可能用的幾個索引,但最終只能用 1 個 指出 MySQL 能使用哪個索引在該表中找到行 如果該列為 NULL,說明沒有使用索引,可以對該列創(chuàng)建索引來提供性能 |
8.3.6 key
| 最終用的索引 顯示 MySQL 實(shí)際決定使用的鍵(索引) 如果沒有選擇索引,鍵是 NULL |
| 可以強(qiáng)制使用索引或者忽略索引: |
| ? |
8.3.7 key_len
| 顯示 MySQL 決定使用的鍵長度 如果鍵是 NULL,則長度為 NULL |
| 注意:key_len 是確定了 MySQL 將實(shí)際使用的索引長度 使用的索引的最大長度 |
| type 列:是指查詢的方式,非常重要,是分析 ”查數(shù)據(jù)過程” 的重要依據(jù)可能的值 |
| all:意味著從表的第 1 行,往后,逐行做全表掃描,運(yùn)氣不好掃描到最后一行 |
| index:比 all 性能稍好一點(diǎn) |
| 通俗的說:all 掃描所有的數(shù)據(jù)行,相當(dāng)于 data_all index 掃描所有的索引節(jié)點(diǎn),相當(dāng)于 index_all |
8.3.8 ref
| 顯示使用哪個列或常數(shù)與 key 一起從表中選擇行 |
8.3.9 rows
| 顯示 MySQL 認(rèn)為它執(zhí)行查詢時必須檢查的行數(shù) |
8.3.10 Extra
| 該列包含 MySQL 解決查詢的詳細(xì)信息 |
| 1.? Distinct:MySQL 發(fā)現(xiàn)第 1 個匹配行后,停止為當(dāng)前的行組合搜索更多的行 2.? Not exists:MySQL 能夠?qū)Σ樵冞M(jìn)行 LEFT JOIN 優(yōu)化,發(fā)現(xiàn) 1 個匹配 LEFT JOIN 標(biāo)準(zhǔn)的行后,不再為前面的的行組合在該表內(nèi)檢查更多的行 3.? range checked for each record (index map: #):MySQL 沒有發(fā)現(xiàn)好的可以使用的索引,但發(fā)現(xiàn)如果來自前面的表的列值已知,可能部分索引可以使用 4.? Using filesort:MySQL 需要額外的一次傳遞,以找出如何按排序順序檢索行 5.? Using index:從只使用索引樹中的信息而不需要進(jìn)一步搜索讀取實(shí)際的行來檢索表中的列信息 6.? Using temporary:為了解決查詢,MySQL 需要創(chuàng)建一個臨時表來容納結(jié)果 7.? Using where:WHERE 子句用于限制哪一個行匹配下一個表或發(fā)送到客戶 8.? Using sort_union(...),Using union(...),Using intersect(...):這些函數(shù)說明如何為 index_merge 聯(lián)接類型合并索引掃描 9.? Using index for group-by:類似于訪問表的Using index 方式,Using index for group-by 表示MySQL發(fā)現(xiàn)了一個索引,可以用來查 詢 GROUP BY 或 DISTINCT 查詢的所有列,而不要額外搜索硬盤訪問實(shí)際的表 |
8.4 in 型子查詢引出的陷阱
| 改進(jìn):用連接查詢來代替子查詢 |
| exists 子查詢 |
| 優(yōu)化 1: 在 group 時,用帶有索引的列來 group,速度會稍快一些,另外,用 int 型 比 char 型分組,也要快一些 |
| 優(yōu)化 2:在 group 時,我們假設(shè)只取了 A 表的內(nèi)容,group by 的列,盡量用 A 表的列,會比 B 表的列要快 |
| 優(yōu)化 3:從語義上去優(yōu)化 |
8.5 from 型子查詢
| 注意:內(nèi)層 from 語句查到的臨時表,是沒有索引的 |
| 所以 from 的返回內(nèi)容要盡量少 |
| min/max 優(yōu)化 在表中,一般都是經(jīng)過優(yōu)化的? 如下地區(qū)表 |
| ? |
| 我們查 min(id),id 是主鍵,查 Min(id)非常快 |
| 但是,pid 上沒有索引,現(xiàn)在要求查詢 3113 地區(qū)的 min(id); |
| select min(id) from it_area where pid=69; |
| 試想 id 是有順序的,(默認(rèn)索引是升續(xù)排列),因此,如果我們沿著 id 的索引方向走,那么 第 1 個 pid=69 的索引結(jié)點(diǎn),他的 id 就正好是最小的 id |
| select id from it_area use index(primary) where pid=69 limit 1; |
| | 12 | 0.00128100 | select min(id) from it_area where pid=69 | |
| | 13 | 0.00017000 | select id from it_area use index(primary) where pid=69 limit 1 | |
| 改進(jìn)后的速度雖然快,但語義已經(jīng)非常不清晰,不建議這么做,僅僅是實(shí)驗(yàn)?zāi)康?/p> |
8.6 count() 優(yōu)化
| 誤區(qū): 1.? myisam 的 count() 非常快 答: 是比較快,但僅限于查詢表的 "所有行" 比較快,因?yàn)?Myisam 對行數(shù)進(jìn)行了存儲 一旦有條件的查詢,速度就不再快了,尤其是 where 條件的列上沒有索引 |
| 2: 假如,id select count(*) from lx_com where id>=100; (1000 多萬行用了 6.X 秒) 小技巧: select count(*) from lx_com; 快 select count(*) from lx_com where id<100; 快 select count(*) frol lx_com -? select count(*) from lx_com where id<100; 快 select (select count(*) from lx_com) - (select count(*) from lx_com where id<100) |
8.7 group by
| 注意: 1.? 分組用于統(tǒng)計(jì),而不用于篩選數(shù)據(jù) 比如:統(tǒng)計(jì)平均分,最高分,適合;但用于篩選重復(fù)數(shù)據(jù),則不適合 以及用索引來避免臨時表和文件排序 |
| 2.? 以 A,B 表連接為例,主要查詢 A 表的列 那么 group by,order by 的列盡量相同,而且列應(yīng)該顯示聲明為 A 的列 |
8.8 union 優(yōu)化
| 注意:union all 不過濾效率提高,如非必須,請用 union all 因?yàn)?union 去重的代價非常高,放在程序里去重 |
| limit 及翻頁優(yōu)化 limit offset,N,當(dāng) offset 非常大時,效率極低 原因是 mysql 并不是跳過 offset 行,然后單取 N 行, 而是取 offset+N 行,返回放棄前 offset 行,返回 N 行 效率較低,當(dāng) offset 越大時,效率越低 |
| 優(yōu)化辦法: 1.? 從業(yè)務(wù)上去解決 辦法:不允許翻過 100 頁 以百度為例,一般翻頁到 70 頁左右 2.? 不用 offset,用條件查詢 3.? 非要物理刪除,還要用 offset 精確查詢,還不限制用戶分頁,怎么辦? 分析:優(yōu)化思路是? 不查,少查,查索引,少取 我們現(xiàn)在必須要查,則只查索引,不查數(shù)據(jù),得到 id 再用 id 去查具體條目? ?這種技巧就是延遲索引 |
8.9 巧用變量
| 1.? 用變量排名 |
| 例: 以 ecshop 中的商品表為例,計(jì)算每個欄目下的商品數(shù),并按商品數(shù)排名 select cat_id,count(*) as cnt from goods group by cat_id order by cnt desc; 并按商品數(shù)計(jì)算這些欄目的名次 set @curr_cnt := 0,@prev_cnt := 0, @rank := 0; select cat_id, (@curr_cnt := cnt) as cnt, (@rank := if(@curr_cnt <> @prev_cnt,@rank+1,@rank)) as rank, @prev_cnt := @curr_cnt from ( select cat_id,count(*) as cnt from shop. goods group by shop. goods.cat_id order by cnt desc) as tmp; |
| 2.? 用變量計(jì)算真正影響的行數(shù) |
| 當(dāng)插入多條,當(dāng)主鍵重復(fù)時,則自動更新,這種效果,可以用 insert on duplication for update 要統(tǒng)計(jì)真正 ”新增” 的條目,如下圖,我們想得到的值是 ”1”,即被更新的行數(shù) |
| ? |
| insert into user (uid,uname) values (4,?ds?),(5,'wanu'),(6,?safdsaf?) on duplicate key update uid=values(uid)+(0*(@x:=@x+1)) , uname=values(uname); mysql> set @x:=0; Query OK, 0 rows affected (0.00 sec) |
| ? |
| 總影響行數(shù)-2*實(shí)際 update 數(shù),即新增的行數(shù) |
| 3.? 簡化 union |
| 比如有新聞表,news,news_hot new_hot 是一張內(nèi)存表,非常快,用來今天的熱門新聞 首頁取新聞時,邏輯是這樣的:先取 hot,沒有 再取 news,為了省事,用一個 union 來完成 select nid,title from news_hot where nid=xxx union select nid,title from news where nid=xxx; |
| ? |
| 如何利用變量讓后半句 select 不執(zhí)行 select id,content,(@find := 1) from news where id=1 union select id,content,(@find :=1) from news2 where id=1 and (@find union 1,1,1 where (@find :=null) is not null; |
| 4.? 小心變量的順序 |
| 如下圖:變量先在 where 中發(fā)揮作用,然后再是 select 操作 如果 where 不成立,select 操作再不發(fā)生 |
| ? |
| Eg; |
| ? |
| 在這個例子中,1、2 兩行,先排好序,在內(nèi)存中,就是這樣的順序 [2] [1] 再逐行 where 條件判斷,取值 |
| ? |
| ? |
| ? |
| 對比這 2 張圖,分析: 1.? where 先發(fā)揮作用,把需要的行都給找出 2.? 然后再逐行 select 因此,前者,最終 select 時,select@num 變量,都是一個值 后者,不斷 select,不斷修改@num 的值,值不斷變化 同時:使用變量,將會使 sql 語句的結(jié)果不緩存 |
8.10 子查詢優(yōu)化
| MySQL 從 4.1 版本開始支持子查詢,使用子查詢進(jìn)行 SELECT 語句嵌套查詢,可以一次完成很多邏輯上需要多個步驟才能完成的 SQL 操作 |
| 子查詢雖然很靈活,但是執(zhí)行效率并不高 執(zhí)行子查詢時,MYSQL 需要創(chuàng)建臨時表,查詢完畢后再刪除這些臨時表,所以,子查詢的速度會受到一定的影響 |
| 優(yōu)化: 可以使用連接查詢(JOIN)代替子查詢,連接查詢時不需要建立臨時表,其速度比子查詢快 Rbac-----兩種 SQL 語句 |
9 索引及優(yōu)化
| 數(shù)據(jù)庫索引的本質(zhì)是數(shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)能夠幫助我們快速的獲取數(shù)據(jù)庫中的數(shù)據(jù) |
9.1 索引的作用
| 當(dāng)表中的數(shù)據(jù)量越來越大時,索引對于性能的影響愈發(fā)重要 索引優(yōu)化應(yīng)該是對查詢性能優(yōu)化最有效的手段 索引能夠輕易將查詢性能提高好幾個數(shù)量級 有了索引相當(dāng)于我們給數(shù)據(jù)庫的數(shù)據(jù)加了目錄一樣,可以快速的找到數(shù)據(jù),如果不適用索引則需要一點(diǎn)一點(diǎn)去查找數(shù)據(jù) 簡單來說提高數(shù)據(jù)查詢的效率 |
9.2 索引的分類
| 普通索引:index:加速查找 主鍵索引:primary key :加速查找+約束(不為空且唯一) 唯一索引:unique:加速查找+約束 (唯一) 聯(lián)合索引(組合索引) 全文索引 |
| -primary key(id,name):聯(lián)合主鍵索引 -unique(id,name):聯(lián)合唯一索引 -index(id,name):聯(lián)合普通索引 全文索引 fulltext:用于搜索很長一篇文章的時候,效果最好 空間索引 spatial:了解就好,幾乎不用 |
9.3 索引的優(yōu)點(diǎn)
| 1.? 可以通過建立唯一索引或者主鍵索引,保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性 |
| 2.? 建立索引可以大大提高檢索的速度,以及減少表的檢索行數(shù) |
| 3.? 在表連接的連接條件可以加速表與表直接的相連 |
| 4.? 在分組和排序字句進(jìn)行數(shù)據(jù)檢索,可以減少查詢時間中分組和排序時所消耗的時間(數(shù)據(jù)庫的記錄會重新排序) |
| 5.? 建立索引,在查詢中使用索引可以提高性能 |
9.4 索引的缺點(diǎn)
| 1.? 在創(chuàng)建索引和維護(hù)索引會耗費(fèi)時間,隨著數(shù)據(jù)量的增加而增加 |
| 2.? 索引文件會占用物理空間,除了數(shù)據(jù)表需要占用物理空間之外,每一個索引還會占用一定的物理空間 |
| 3.? 當(dāng)對表的數(shù)據(jù)進(jìn)行 INSERT,UPDATE,DELETE 的時候,索引也要動態(tài)的維護(hù),這樣就會降低數(shù)據(jù)的維護(hù)速度(建立索引會占用磁盤空間的索引文件;一般情況這個問題不太嚴(yán)重,但如果你在一個大表上創(chuàng)建了多種組合索引,索引文件的會膨脹很快) |
9.5 哪些情況或字段適合加索引
| 1.? 在經(jīng)常需要搜索的列上,可以加快索引的速度 |
| 2.? 主鍵列上可以確保列的唯一性 |
| 3.? 在表與表的而連接條件上加上索引,可以加快連接查詢的速度 |
| 4.? 在經(jīng)常需要排序(order by),分組(group by)和的 distinct 列上加索引可以加快排序查詢的時間 |
9.6 哪些情況不適合創(chuàng)建索引
| 1.? 查詢中很少使用到的列不應(yīng)該創(chuàng)建索引,如果建立了索引然而還會降低 mysql 的性能和增大了空間需求 |
| 2.? 很少數(shù)據(jù)的列也不應(yīng)該建立索引,比如 一個性別字段 0 或者 1,在查詢中,結(jié)果集的數(shù)據(jù)占了表中數(shù)據(jù)行的比例比較大,mysql 需要掃描的行數(shù)很多,增加索引,并不能提高效率 |
| 3.? 定義為 text 和 image 和 bit 數(shù)據(jù)類型的列不應(yīng)該增加索引 |
| 4.? 當(dāng)表的修改(UPDATE,INSERT,DELETE)操作遠(yuǎn)遠(yuǎn)大于檢索(SELECT)操作時不應(yīng)該創(chuàng)建索引,這兩個操作是互斥的關(guān)系 |
9.7 哪些情況會造成索引失效
| 1.? 如果條件中有 or,即使其中有條件帶索引也不會使用(這也是為什么盡量少用 or 的原因) |
| 2.? 索引字段的值不能有 null 值,有 null 值會使該列索引失效 |
| 3.? 對于多列索引,不是使用的第一部分,則不會使用索引(最左原則) |
| 4.? like 查詢以 % 開頭 |
| 5.? 如果列類型是字符串,那一定要在條件中將數(shù)據(jù)使用單引號引用起來,否則不使用索引 |
| 6.? 在索引的列上使用表達(dá)式或者函數(shù)會使索引失效 例如:select * from users where YEAR(adddate) < 2007,將在每個行上進(jìn)行運(yùn)算,這將導(dǎo)致索引失效而進(jìn)行全表掃描,因此我們可以改成:select * from users where adddate < '2007-01-01′ |
9.8 索引相關(guān) sql
9.8.1 創(chuàng)建索引
| mysql>ALTER TABLE 表名 ADD INDEX 索引名 列名; |
| mysql>ALTER TABLE 表名 ADD UNIQUE 索引名 列名; |
| mysql>ALTER TABLE 表名 ADD PRIMARY KEY 索引名 列名; |
| mysql>CREATE INDEX 索引名 ON 表名 列名; |
| mysql>CREATE UNIQUE INDEX 索引名 ON 表名 列名; |
9.8.2 刪除索引
| DORP INDEX IndexName ON TableName |
9.8.3 查看索引
| show index from tableName; |
9.9 索引原理
| MySQL 的基本存儲結(jié)構(gòu)是頁(記錄都存在頁里邊): |
| 各個數(shù)據(jù)頁可以組成一個雙向鏈表 |
| 每個數(shù)據(jù)頁中的記錄又可以組成一個單向鏈表 每個數(shù)據(jù)頁都會為存儲在它里邊兒的記錄生成一個頁目錄,在通過主鍵查找某條記錄的時候可以在頁目錄中使用二分法快速定位到對應(yīng)的槽,然后再遍歷該槽對應(yīng)分組中的記錄即可快速找到指定的記錄 以其他列(非主鍵)作為搜索條件:只能從最小記錄開始依次遍歷單鏈表中的每條記錄 所以說,如果我們寫 select * from user where indexname = 'xxx' 這樣沒有進(jìn)行任何優(yōu)化的 sql 語句,默認(rèn)會這樣做: 1. 定位到記錄所在的頁:需要遍歷雙向鏈表,找到所在的頁 2. 從所在的頁內(nèi)中查找相應(yīng)的記錄:由于不是根據(jù)主鍵查詢,只能遍歷所在頁的單鏈表了 很明顯,在數(shù)據(jù)量很大的情況下這樣查找會很慢!這樣的時間復(fù)雜度為 O(n) |
| 使用索引之后 索引做了些什么可以讓我們查詢加快速度呢?其實(shí)就是將無序的數(shù)據(jù)變成有序(相對): 很明顯的是:沒有用索引我們是需要遍歷雙向鏈表來定位對應(yīng)的頁,現(xiàn)在通過 "目錄"?就可以很快地定位到對應(yīng)的頁上了!(二分查找,時間復(fù)雜度近似為 O(logn)) 其實(shí)底層結(jié)構(gòu)就是 B+ 樹,B+ 樹作為樹的一種實(shí)現(xiàn),能夠讓我們很快地查找出對應(yīng)的記錄 |
| 索引被用來快速找出在一個列上用一特定值的行。沒有索引,MySQL 不得不首先以第一條記錄開始,然后讀完整個表直到它找出相關(guān)的行;表越大,花費(fèi)時間越多;對于一個有序字段,可以運(yùn)用二分查找(Binary Search),這就是為什么性能能得到本質(zhì)上的提高 MYISAM 和 INNODB 都是用 B+Tree 作為索引結(jié)構(gòu) (主鍵,unique 都會默認(rèn)的添加索引) |
| 索引的實(shí)現(xiàn)本質(zhì)上是為了讓數(shù)據(jù)庫能夠快速查找數(shù)據(jù),而單獨(dú)維護(hù)的數(shù)據(jù)結(jié)構(gòu);mysql 實(shí)現(xiàn)索引主要使用的兩種數(shù)據(jù)結(jié)構(gòu):hash 和 B+ 樹: 我們比較常用的 MyIsam 和 innorDB 引擎都是基于 B+ 樹的 hash:(hash 索引在 mysql 比較少用)他以把數(shù)據(jù)的索引以 hash 形式組織起來,因此當(dāng)查找某一條記錄的時候,速度非常快;當(dāng)時因?yàn)槭?hash 結(jié)構(gòu),每個鍵只對應(yīng)一個值,而且是散列的方式分布;所以他并不支持范圍查找和排序等功能 B+樹:b+tree 是(mysql 使用最頻繁的一個索引數(shù)據(jù)結(jié)構(gòu))數(shù)據(jù)結(jié)構(gòu)以平衡樹的形式來組織,因?yàn)槭菢湫徒Y(jié)構(gòu),所以更適合用來處理排序,范圍查找等功能;相對 hash 索引,B+ 樹在查找單條記錄的速度雖然比不上 hash 索引,但是因?yàn)楦m合排序等操作,所以他更受用戶的歡迎;畢竟不可能只對數(shù)據(jù)庫進(jìn)行單條記錄的操作 |
9.9.1 BTree 索引
| 大的方面看,都用的平衡樹,但具體的實(shí)現(xiàn)上,各引擎稍有不同 比如,嚴(yán)格的說,NDB 引擎使用的是 T-tree Myisam,innodb 中,默認(rèn)用 B-tree 索引 但抽象一下---B-tree 系統(tǒng),可理解為 "排好序的快速查找結(jié)構(gòu)" |
| BTree 是平衡搜索多叉樹,設(shè)樹的度為 2d(d>1),高度為 h,那么 BTree 要滿足以一下條件: 每個葉子結(jié)點(diǎn)的高度一樣,等于 h; 每個非葉子結(jié)點(diǎn)由 n-1 個 key 和 n 個指針 point 組成,其中 d 葉子結(jié)點(diǎn)指針都為 null; 非葉子結(jié)點(diǎn)的 key 都是 [key,data] 二元組,其中 key 表示作為索引的鍵,data 為鍵值所在行的數(shù)據(jù) |
9.9.2 B+Tree 索引
| B+Tree 是 BTree 的一個變種,設(shè) d 為樹的度數(shù),h 為樹的高度,B+Tree 和 BTree 的不同主要在于: |
| B+Tree 中的非葉子結(jié)點(diǎn)不存儲數(shù)據(jù),只存儲鍵值; |
| B+Tree 的葉子結(jié)點(diǎn)沒有指針,所有鍵值都會出現(xiàn)在葉子結(jié)點(diǎn)上,且 key 存儲的鍵值對應(yīng) data 數(shù)據(jù)的物理地址; |
| B+Tree 的每個非葉子節(jié)點(diǎn)由 n 個鍵值 key 和 n 個指針 point 組成; |
9.9.3 hash 索引
| 在 memory 表里,默認(rèn)是 hash 索引,hash 的理論查詢時間復(fù)雜度為 O(1) 疑問 既然 hash 的查找如此高效,為什么不都用 hash 索引? |
| 答: 1.? ?hash 函數(shù)計(jì)算后的結(jié)果,是隨機(jī)的,如果是在磁盤上放置數(shù)據(jù),比主鍵為 id 為例,那么隨著 id 的增長, id 對應(yīng)的行,在磁盤上隨機(jī)放置 2.??無法對范圍查詢進(jìn)行優(yōu)化 3: 無法利用前綴索引;比如 在 btree 中,field 列的值 "hellopworld",并加索引 查詢 xx=helloword,自然可以利用索引, xx=hello,也可以利用索引(左前綴索引) 因?yàn)?hash("helloword"),和 hash("hello"),兩者的關(guān)系仍為隨機(jī) 4.? 排序也無法優(yōu)化 5.? 必須回行;就是說通過索引拿到數(shù)據(jù)位置,必須回到表中取數(shù)據(jù) |
9.9.4 btree 索引的常見誤區(qū)
9.9.4.1 在 where 條件常用的列上都加上索引
| 例:where cat_id=3 and price>100;? //查詢第 3 個欄目,100 元以上的商品 誤:cat_id 上和 price 上都加上索引 錯:只能用上 cat_id 或 Price 索引,因?yàn)槭仟?dú)立的索引,同時只能用上 1 個 |
9.9.4.2 在多列上建立索引后,查詢哪個列,索引都將發(fā)揮作用
| 誤:多列索引上,索引發(fā)揮作用,需要滿足左前綴要求;以 index(a,b,c) 為例 |
| ? |
| 多列索引經(jīng)典題目: http://www.zixue.it/thread-9218-1-4.html |
9.9.4.3 面試題
| 有商品表,有主鍵,goods_id,欄目列 cat_id,價格 price |
| 說:在價格列上已經(jīng)加了索引,但按價格查詢還是很慢,問可能是什么原因,怎么解決? |
| 答:在實(shí)際場景中,一個電商網(wǎng)站的商品分類很多,直接在所有商品中,按價格查商品,是極少的,一般客戶都來到分類下,然后再查 |
| 改正:去掉單獨(dú)的 Price 列的索引,加 (cat_id,price) 復(fù)合索引再查詢 |
9.10 索引優(yōu)化策略
| 1.? 選擇唯一性索引 |
| 2.? 唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄 |
| 3.? 為經(jīng)常需要排序、分組和聯(lián)合操作的字段建立索引 |
| 4.? 為常作為查詢條件的字段建立索引 |
| 5.? 限制索引的數(shù)目: 越多的索引,會使更新表變得很浪費(fèi)時間 |
| 6.? 盡量使用數(shù)據(jù)量少的索引 |
| 7.? 如果索引的值很長,那么查詢的速度會受到影響 |
| 8.? 盡量使用前綴來索引 |
| 9.? 如果索引字段的值很長,最好使用值的前綴來索引 |
| 10.? 刪除不再使用或者很少使用的索引 |
| 11.? 最左前綴匹配原則,非常重要的原則 |
| 12.? 盡量選擇區(qū)分度高的列作為索引 |
| 13.? 區(qū)分度的公式是表示字段不重復(fù)的比例 |
| 14.? 索引列不能參與計(jì)算,保持列 "干凈":帶函數(shù)的查詢不參與索引 |
| 15.? 盡量的擴(kuò)展索引,不要新建索引 |
9.10.1 最左前綴原則
| MySQL 中的索引可以以一定順序引用多列,這種索引叫作聯(lián)合索引;如 User 表的 name 和 city 加聯(lián)合索引就是(name,city),而最左前綴原則指的是,如果查詢的時候查詢條件精確匹配索引的左邊連續(xù)一列或幾列,則此列就可以被用到;如下: |
| select * from user where name=xx and city=xx ;//可以命中索引select * from user where name=xx ; // 可以命中索引select * from user where city=xx ; // 無法命中索引 |
| 這里需要注意的是,查詢的時候如果兩個條件都用上了,但是順序不同,如 city= xx and name =xx,那么現(xiàn)在的查詢引擎會自動優(yōu)化為匹配聯(lián)合索引的順序,這樣是能夠命中索引的 |
| 由于最左前綴原則,在創(chuàng)建聯(lián)合索引時,索引字段的順序需要考慮字段值去重之后的個數(shù),較多的放前面ORDER BY 子句也遵循此規(guī)則 |
9.10.2 注意避免冗余索引
| 冗余索引指的是索引的功能相同,能夠命中就肯定能命中 ,那么就是冗余索引如(name,city )和(name )這兩個索引就是冗余索引,能夠命中后者的查詢肯定是能夠命中前者的,在大多數(shù)情況下,都應(yīng)該盡量擴(kuò)展已有的索引而不是創(chuàng)建新索引 |
| MySQLS.7 版本后,可以通過查詢 sys 庫的 schema_redundant_indexes 表來查看冗余索引 |
9.10.3 使用索引查詢需要注意
| 索引可以提供查詢的速度,但并不是使用了帶有索引的字段查詢都會生效,有些情況下是不生效的,需要注意 |
9.10.3.1 使用 LIKE 關(guān)鍵字的查詢
| 在使用 LIKE 關(guān)鍵字進(jìn)行查詢的查詢語句中,如果匹配字符串的第一個字符為 "%",索引不起作用 只有 "%" 不在第一個位置,索引才會生效 |
| ? |
| ? |
9.10.3.2 使用聯(lián)合索引的查詢
| MySQL 可以為多個字段創(chuàng)建索引,一個索引可以包括 16 個字段 對于聯(lián)合索引,只有查詢條件中使用了這些字段中第一個字段時,索引才會生效 |
| ? |
| ? |
| ? |
9.10.3.3 使用 OR 關(guān)鍵字的查詢
| 查詢語句的查詢條件中只有 OR 關(guān)鍵字,且 OR 前后的兩個條件中的列都是索引時,索引才會生效,否則,索引不生效 |
| ? |
| ? |
| ? |
9.10.4 聚簇索引與非聚簇索引
| Myisam 與 innodb 引擎,索引文件的異同 innodb 的主索引文件上,直接存放該行數(shù)據(jù),稱為聚簇索引,次索引指向?qū)χ麈I的引用 myisam 中,主索引和次索引,都指向物理行(磁盤位置) |
| 注意:innodb 來說 1.? 主鍵索引既存儲索引值,又在葉子中存儲行的數(shù)據(jù) 2.? 如果沒有主鍵,則會 Unique key 做主鍵 3.? 如果沒有 unique,則系統(tǒng)生成一個內(nèi)部的 rowid 做主鍵 4.? 像 innodb 中,主鍵的索引結(jié)構(gòu)中,既存儲了主鍵值,又存儲了行數(shù)據(jù),這種結(jié)構(gòu)稱為 "聚簇索引" |
| 聚簇索引 優(yōu)勢:根據(jù)主鍵查詢條目比較少時,不用回行(數(shù)據(jù)就在主鍵節(jié)點(diǎn)下) 劣勢:如果碰到不規(guī)則數(shù)據(jù)插入時,造成頻繁的頁分裂 |
9.10.4.1 高性能索引策略
| 對于 innodb 而言,因?yàn)楣?jié)點(diǎn)下有數(shù)據(jù)文件,因此節(jié)點(diǎn)的分裂將會比較慢 |
| 對于 innodb 的主鍵,盡量用整型,而且是遞增的整型 |
| 如果是無規(guī)律的數(shù)據(jù),將會產(chǎn)生的頁的分裂,影響速度 |
9.10.4.2 索引覆蓋
| 索引覆蓋是指:如果查詢的列恰好是索引的一部分,那么查詢只需要在索引文件上進(jìn)行,不需要回行到磁盤再找數(shù)據(jù) |
| 這種查詢速度非常快,稱為 "索引覆蓋" |
9.10.4.3 理想的索引
| 1.? 查詢頻繁 2.? 區(qū)分度高 3.? 長度小 4.? 盡量能覆蓋常用查詢字段 |
| 1.? 索引長度直接影響索引文件的大小,影響增刪改的速度,并間接影響查詢速度(占用內(nèi)存多) 針對列中的值,從左往右截取部分,來建索引 1: 截的越短,重復(fù)度越高,區(qū)分度越小,索引效果越不好 2: 截的越長,重復(fù)度越低,區(qū)分度越高,索引效果越好,但帶來的影響也越大--增刪改變慢,并間影響查詢速度 所以,我們要在 區(qū)分度 + 長度 兩者上,取得一個平衡 對于一般的系統(tǒng)應(yīng)用:區(qū)別度能達(dá)到 0.1,索引的性能就可以接受 |
9.10.5 對于左前綴不易區(qū)分的列,建立索引的技巧
| 如 url 列 http://www.baidu.comhttp://www.zixue.it列的前 11 個字符都是一樣的,不易區(qū)分,可以用如下 2 個辦法來解決 |
| 1.? 把列內(nèi)容倒過來存儲,并建立索引 Moc.udiab.www//:ptthTi.euxiz.www//://ptth這樣左前綴區(qū)分度大 |
| 2.? 偽 hash 索引效果 同時存 url_hash 列 |
9.10.6 多列索引
| 多列索引的考慮因素 --- 列的查詢頻率,列的區(qū)分度 |
9.10.6.1 索引與排序
| 排序可能發(fā)生 2 種情況: |
| 1.? 對于覆蓋索引,直接在索引上查詢時,就是有順序的,using index |
| 2.? 先取出數(shù)據(jù),形成臨時表做 filesort(文件排序,但文件可能在磁盤上,也可能在內(nèi)存中) |
| 我們的爭取目標(biāo)-----取出來的數(shù)據(jù)本身就是有序的;利用索引來排序 |
9.10.6.2 重復(fù)索引與冗余索引
| 重復(fù)索引: 是指在同 1 個列(如 age),或者順序相同的幾個列(age,school),建立了多個索引,稱為重復(fù)索引,重復(fù)索引沒有任何幫助,只會增大索引文件,拖慢更新速度,需要去掉 |
| 冗余索引: 冗余索引是指 2 個索引所覆蓋的列有重疊,稱為冗余索引 比如 x,m列,加索引 index x(x),index xm(x,m) x,xm 索引,兩者的 x 列重疊了,這種情況,稱為冗余索引 |
| 甚至可以把 index mx(m,x) 索引也建立,mx,xm 也不是重復(fù)的,因?yàn)榱械捻樞虿灰粯?/p> |
9.10.6.3 索引碎片與維護(hù)
| 在長期的數(shù)據(jù)更改過程中,索引文件和數(shù)據(jù)文件,都將產(chǎn)生空洞,形成碎片 我們可以通過一個 nop 操作(不產(chǎn)生對數(shù)據(jù)實(shí)質(zhì)影響的操作),來修改表 比如:表的引擎為 innodb,可以 alter table xxx engine innodb |
| optimize table 表名,也可以修復(fù) |
| 注意:修復(fù)表的數(shù)據(jù)及索引碎片,就會把所有的數(shù)據(jù)文件重新整理一遍,使之對齊 這個過程,如果表的行數(shù)比較大,也是非常耗費(fèi)資源的操作 所以,不能頻繁的修復(fù) |
| 如果表的 Update 操作很頻率,可以按周/月,來修復(fù) 如果不頻繁,可以更長的周期來做修復(fù) |
9.10.7 限制每張表上的索引數(shù)量(5 個)
| 建議單張表索引不超過 5 個 索引并不是越多越好 索引可以提高效率同樣可以降低效率 |
| 索引可以增加查詢效率,但同樣也會降低插入和更新的效率,甚至有些情況下會降低查詢效率 |
| 因?yàn)?MySQL 優(yōu)化器在選擇如何優(yōu)化查詢時,會根據(jù)統(tǒng)一信息,對每一個可以用到的索引來進(jìn)行評估,以生成出一個最好的執(zhí)行計(jì)劃,如果同時有很多個索引都可以用于查詢,就會增加 MySQL 優(yōu)化器生成執(zhí)行計(jì)劃的時間,同樣會降低查詢性能 |
9.10.8 禁止給表中的每一列都建立單獨(dú)的索引
| 5.6 版本之前,一個 sql 只能使用到一個表中的一個索引,5.6 以后,雖然有了合并索引的優(yōu)化方式,但是還是遠(yuǎn)遠(yuǎn)沒有使用一個聯(lián)合索引的查詢方式好 |
9.10.9 每個 Innodb 表必須有個主鍵
| Innodb 是一種索引組織表:數(shù)據(jù)的存儲的邏輯順序和索引的順序是相同的 每個表都可以有多個索引,但是表的存儲順序只能有一種 |
| Innodb 是按照主鍵索引的順序來組織表的 1.? 不要使用更新頻繁的列作為主鍵,不適用多列主鍵(相當(dāng)于聯(lián)合索引) 2.? ? 不要使用 UUID,MD5,HASH,字符串列作為主鍵(無法保證數(shù)據(jù)的順序增長) 3.? ?主鍵建議使用自增 ID 值 |
9.10.10 常見索引列建議
| 1. 出現(xiàn)在 SELECT、UPDATE、DELETE 語句的 WHERE 從句中的列 |
| 2. 包含在 ORDER BY、GROUP BY、DISTINCT 中的字段 |
| 3. 并不要將符合 1 和 2 中的字段的列都建立一個索引, 通常將 1、2 中的字段建立聯(lián)合索引效果更好 |
| 4. 多表 join 的關(guān)聯(lián)列 |
9.10.11 如何選擇索引列的順序
| 建立索引的目的是:希望通過索引進(jìn)行數(shù)據(jù)查找,減少隨機(jī) IO,增加查詢性能 ,索引能過濾出越少的數(shù)據(jù),則從磁盤中讀入的數(shù)據(jù)也就越少 |
| 區(qū)分度最高的放在聯(lián)合索引的最左側(cè)(區(qū)分度=列中不同值的數(shù)量/列的總行數(shù)) |
| 盡量把字段長度小的列放在聯(lián)合索引的最左側(cè)(因?yàn)樽侄伍L度越小,一頁能存儲的數(shù)據(jù)量越大,IO 性能也就越好) |
| 使用最頻繁的列放到聯(lián)合索引的左側(cè)(這樣可以比較少的建立一些索引) |
9.10.12 避免建立冗余索引和重復(fù)索引(增加了查詢優(yōu)化器生成執(zhí)行計(jì)劃的時間)
| 重復(fù)索引示例:primary key(id)、index(id)、unique index(id) |
| 冗余索引示例:index(a,b,c)、index(a,b)、index(a) |
9.10.13 對于頻繁的查詢優(yōu)先考慮使用覆蓋索引
| 覆蓋索引:就是包含了所有查詢字段 (where,select,ordery by,group by 包含的字段) 的索引覆蓋索引的好處: |
| 避免 Innodb 表進(jìn)行索引的二次查詢: Innodb 是以聚集索引的順序來存儲的,對于 Innodb 來說,二級索引在葉子節(jié)點(diǎn)中所保存的是行的主鍵信息,如果是用二級索引查詢數(shù)據(jù)的話,在查找到相應(yīng)的鍵值后,還要通過主鍵進(jìn)行二次查詢才能獲取我們真實(shí)所需要的數(shù)據(jù)。而在覆蓋索引中,二級索引的鍵值中可以獲取所有的數(shù)據(jù),避免了對主鍵的二次查詢 ,減少了 IO 操作,提升了查詢效率 |
| 可以把隨機(jī) IO 變成順序 IO 加快查詢效率: 由于覆蓋索引是按鍵值的順序存儲的,對于 IO 密集型的范圍查找來說,對比隨機(jī)從磁盤讀取每一行的數(shù)據(jù) IO 要少的多,因此利用覆蓋索引在訪問時也可以把磁盤的隨機(jī)讀取的 IO 轉(zhuǎn)變成索引查找的順序 IO |
9.10.14 索引 SET 規(guī)范
| 盡量避免使用外鍵約束 不建議使用外鍵約束(foreign key),但一定要在表與表之間的關(guān)聯(lián)鍵上建立索引 外鍵可用于保證數(shù)據(jù)的參照完整性,但建議在業(yè)務(wù)端實(shí)現(xiàn) 外鍵會影響父表和子表的寫操作從而降低性能 |
9.11 主鍵、外鍵和索引的區(qū)別
| 主鍵 | 外鍵 | 索引 | |
| 定義 | 唯一標(biāo)識一條記錄,不能有重復(fù)的,不允許為空 | 表的外鍵是另一表的主鍵,外鍵可以有重復(fù)的,可以是空值 | 該字段沒有重復(fù)值,但可以有一個空值 |
| 作用 | 用來保證數(shù)據(jù)完整性 | 用來和其他表建立聯(lián)系用的 | 是提高查詢排序的速度 |
| 個數(shù) | 主鍵只能有一個 | 一個表可以有多個外鍵 | 一個表可以有多個唯一索引 |
9.12 mysql:刪除表數(shù)據(jù)drop、truncate 和 delete 的用法
| 程度從強(qiáng)到弱 1. drop table tb drop將表格直接刪除,沒有辦法找回 2. truncate (table) tb 刪除表中的所有數(shù)據(jù),不能與where一起使用 3. delete from tb (where) 刪除表中的數(shù)據(jù)(可制定某一行) |
| 區(qū)別:truncate 和 delete 的區(qū)別 1. 事務(wù):truncate 是不可以 rollback 的,但是 delete 是可以 rollback 的 原因:truncate 刪除整表數(shù)據(jù)(ddl語句,隱式提交),delete 是一行一行的刪除,可以 rollback 2. 效果:truncate 刪除后將重新水平線和索引(id從零開始),delete 不會刪除索引 3. truncate 不能觸發(fā)任何? Delete 觸發(fā)器 4. delete 刪除可以返回行數(shù) |
10 插入數(shù)據(jù)的優(yōu)化? ?
| 插入數(shù)據(jù)時,影響插入速度的主要是索引、唯一性校驗(yàn)、一次插入的數(shù)據(jù)條數(shù)等 |
| 插入數(shù)據(jù)的優(yōu)化,不同的存儲引擎優(yōu)化手段不一樣,在 MySQL 中常用的存儲引擎有:MyISAM 和 InnoDB,兩者的區(qū)別: |
| http://www.cnblogs.com/panfeng412/archive/2011/08/16/2140364.html |
| ? |
10.1 MyISAM
10.1.1 禁用索引
| 對于非空表,插入記錄時,MySQL 會根據(jù)表的索引對插入的記錄建立索引 如果插入大量數(shù)據(jù),建立索引會降低插入數(shù)據(jù)速度 |
| 為了解決這個問題,可以在批量插入數(shù)據(jù)之前禁用索引,數(shù)據(jù)插入完成后再開啟索引 |
| 禁用索引的語句: ALTER TABLE table_name DISABLE KEYS |
| 開啟索引語句: ALTER TABLE table_name ENABLE KEYS |
| 對于空表批量插入數(shù)據(jù),則不需要進(jìn)行操作,因?yàn)?MyISAM 引擎的表是在導(dǎo)入數(shù)據(jù)后才建立索引 |
10.1.2 禁用唯一性檢查
| 唯一性校驗(yàn)會降低插入記錄的速度,可以在插入記錄之前禁用唯一性檢查,插入數(shù)據(jù)完成后再開啟 |
| 禁用唯一性檢查的語句:SET UNIQUE_CHECKS = 0; |
| 開啟唯一性檢查的語句:SET UNIQUE_CHECKS = 1; |
10.1.3 批量插入數(shù)據(jù)
| 插入數(shù)據(jù)時,可以使用一條 INSERT 語句插入一條數(shù)據(jù),也可以插入多條數(shù)據(jù) |
| ? |
| ? |
| 第二種方式的插入速度比第一種方式快 |
10.1.4 使用 LOAD DATA INFILE
| 當(dāng)需要批量導(dǎo)入數(shù)據(jù)時,使用 LOAD DATA INFILE 語句比 INSERT 語句插入速度快很多 |
10.2 InnoDB
10.2.1 禁用唯一性檢查
| 用法和 MyISAM 一樣 |
10.2.2 禁用外鍵檢查
| 插入數(shù)據(jù)之前執(zhí)行禁止對外鍵的檢查,數(shù)據(jù)插入完成后再恢復(fù),可以提供插入速度 |
| 禁用:SET foreign_key_checks = 0; |
| 開啟:SET foreign_key_checks = 1; |
10.2.3 禁止自動提交
| 插入數(shù)據(jù)之前執(zhí)行禁止事務(wù)的自動提交,數(shù)據(jù)插入完成后再恢復(fù),可以提高插入速度 |
| 禁用:SET autocommit = 0; |
| 開啟:SET autocommit = 1; |
11 服務(wù)器優(yōu)化
11.1 優(yōu)化服務(wù)器硬件
| 服務(wù)器的硬件性能直接決定著 MySQL 數(shù)據(jù)庫的性能,硬件的性能瓶頸,直接決定 MySQL 數(shù)據(jù)庫的運(yùn)行速度和效率 |
| 需要從以下幾個方面考慮: |
| 1. 配置較大的內(nèi)存 足夠大的內(nèi)存,是提高 MySQL 數(shù)據(jù)庫性能的方法之一;內(nèi)存的 IO 比硬盤快的多,可以增加系統(tǒng)的緩沖區(qū)容量,使數(shù)據(jù)在內(nèi)存停留的時間更長,以減少磁盤的 IO |
| 2. 配置高速磁盤 比如 SSD |
| 3. 合理分配磁盤 IO 把磁盤 IO 分散到多個設(shè)備上,以減少資源的競爭,提高并行操作能力 |
| 4. 配置多核處理器 MySQL 是多線程的數(shù)據(jù)庫,多處理器可以提高同時執(zhí)行多個線程的能力 |
11.2 優(yōu)化 MySQL 的參數(shù)
| 通過優(yōu)化 MySQL 的參數(shù)可以提高資源利用率,從而達(dá)到提高 MySQL 服務(wù)器性能的目的 |
| MySQL 的配置參數(shù)都在 my.conf 或者 my.ini 文件的[mysqld]組中,常用的參數(shù)如下: |
| ? |
| ? |
| ? |
| 要求:必須記憶至少 3 個 |
12 測試定位
| 高性能不是指 "絕對性能" 強(qiáng)悍,而是指業(yè)務(wù)能發(fā)揮出硬件的最大水平。性能強(qiáng)的服務(wù)器并非 "設(shè)計(jì)" 而來,而是不斷改進(jìn),提升短板,測試,就是量化找出短板的過程 |
12.1 測試指標(biāo)
| 只有會測試,能把數(shù)據(jù)量化,才能進(jìn)一步改進(jìn)優(yōu)化 |
| 1:吞吐量:單位時間內(nèi)的事務(wù)處理數(shù),單位 tps(每秒事務(wù)數(shù)) |
| 2:響應(yīng)時間:語句平均響應(yīng)時間,一般截取某段時間內(nèi),95% 范圍內(nèi)的平均時間 |
| 3:并發(fā)性:線程同時執(zhí)行 |
| 4:可擴(kuò)展性:資源增加,性能也能正比增加 |
12.1.1 發(fā)現(xiàn)系統(tǒng)運(yùn)行緩慢,如何定位和分析查詢慢的 sql 語句
| 1.開啟 mysql 慢日志查詢 定位查詢較慢的 sql 語句 (200ms 500ms ) |
| 2.使用 EXPLAIN 關(guān)鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結(jié)構(gòu)的性能瓶頸。EXPLAIN 的查詢結(jié)果還會告訴你你的索引主鍵被如何利用的,你的數(shù)據(jù)表是如何被搜索和排序的……等等,等等 |
| 3.代碼中 可以使用 AOP 的操作 對每個持久層的 service 方法打印執(zhí)行時間,將所有執(zhí)行時間較長的 sql 語句進(jìn)行預(yù)警 |
12.2 測試工具
12.2.1 mysqlslap
| ? |
12.2.2 sysbench
| 測試 CPU 性能 測試 IO 性能 測試事務(wù)性能 |
12.3 查看 mysql 的進(jìn)程狀態(tài)
| mysql -h 192.168.177.128 -u root -e 'show processlist\G'|grep State:|sort|uniq -c|sort -rn 5 State: Sending data 2 State: statistics 2 State: NULL 1 State: Updating 1 State: update 以下幾種狀態(tài)要注意: converting HEAP to MyISAM 查詢結(jié)果太大時,把結(jié)果放在磁盤 create tmp table 創(chuàng)建臨時表(如 group 時儲存中間結(jié)果) Copying to tmp table on disk 把內(nèi)存臨時表復(fù)制到磁盤 locked 被其他查詢鎖住 logging slow query 記錄慢查詢 |
總結(jié)
以上是生活随笔為你收集整理的面试-03-数据库和事务专题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jenkins 利用 Gitlab +
- 下一篇: MySQL的基本学习(三)——DD/M/