程序員必會的SQL調優
首先了解數據庫存儲兩大常用引擎 Innodeb和MyISAM
數據庫引擎 | 存儲方式 | 底層結構 | 事務 | 鎖 | 索引 | 主外鍵 |
---|---|---|---|---|---|---|
MyISAM | 存儲三個文件frm表結構和myd數據和myi索引 | B樹 | No | 行級鎖 | 非聚集 | No |
Innodeb | 存儲兩個文件 frm表結構和myd數據 | B+樹 | Yes | 行級鎖和表級鎖(默認) | 聚集索引 | Yes |
B樹是每一個節點都存在key和訪問磁盤value的索引,B+樹是葉子節點才存數據值,其它都是索引
索引類型
單值索引where name=‘xxx’ 複合索引where name=‘xx’ and email=‘xxx’ 唯一索引:值得是唯一值的
create index idx_user_name on user(name) //單值
create index idx_user_nameEmail on user(name,email) //複合
create unique index idx_user_id on user(id) //
Sql執行順序
什麼是索引
索引本質是數據結構,目的是提高查找效率,排好序的快速查找數據結構就是索引
數據庫系統還維護者滿足特定查找算法的數據結構,這些數據結構以某種方式指向數據,比如B樹和B+樹
B樹
葉子節點存儲的是磁盤數據的地址
查詢快,但是增刪慢,因爲每次修改,還要修改指向數據的索引
優勢: 提高檢索效率,降低數據排序成本,避免全盤掃描
索引不是越多越好
索引也是一張表,保存了主鍵與索引字段,所以也佔內存,
而且雖然提高查詢速度,但是降低更新表的速度,因爲mysql不僅要保存數據,還要保存一下索引文件每次更新添加的索引列字段,都會調整更新後帶來的鍵值變化的索引
如果一個表沒有建索引,會自動以主鍵爲索引,如果沒有主鍵,會自動選取一個不爲空的當主鍵
索引場景
- 主鍵自動唯一索引
- 頻繁查詢的字段
- 查詢中與其它表關聯的字段,外鍵關係建立索引
- where裏用不到的字段不創建索引
- 高併發組合索引比較好
- 頻繁更新的字段不要建立索引
- 太多重複內容
EXPLAIN使用
explain+sql
explain select * form user
id能判斷Sql執行順序
- id表示執行加載順序 id越大越先執行,id全相同由上至下,id混合
- select_type:primary代表最外層查詢,最後被執行,從最裏層開始執行到最外層
- table :dervide 衍生表
select_type 表示sql操作的類型
- simple,簡單的select查詢
- primary,外層雞蛋殼
- subquery,在Select或where包含了子查詢
- derived,子查詢起了別名表,會暫時放在臨時緩存裏
- union, 聯合查詢
table表名
就是說明該行數據關聯了哪個表執行
type 訪問類型
syetem>const>eq_ref>ref>range>index>All
- all 全盤掃描
- syetem,表只有一行記錄,系統表,平時不會出現,忽略不計
- const 表示通過一次索引就找到了,如where 主鍵,只匹配一行數據
- eq_ref 唯一性索引掃描,主鍵兩個情況
- ref 非唯一,可能找出多個符合條件
- range 範圍查詢
possible_keys 顯示可能應用的索引
key實際使用的索引 null爲沒有使用
key_len 索引使用的字節數
ref 顯示索引哪一行被使用
rows 大致估算讀取的行數才能出結果
Extra 額外重要信息顯示
- useing filesort 文件內排序
- useing temporey 使用臨時表
- useing index 使用索引找到數據
- useing where 範圍找
- covering index 剛好索引對於查詢的
索引注意點
-
單表連接 如果是直接索引改的話,可以把type從all改爲ref,因爲一旦遇到了範圍查詢索引就會失效 type就會是range
-
雙表連接 相反鍵
如果是左連接,那索引建在左邊,沒效果,因爲左連接就是含左表所有記錄,建在右邊,會減少很多rows
-
三表連接 都是左連接設置給後兩行,rows會變少,然後type是ref
-
優先優化最裏層的,然後就是儘可能減少join循環次數,然後用小表來連接大表
-
當無法得知join的字段是否有索引的時候,且內存充足的話可以調大joinBuffer的大小,來緩存
索引失效
-
全值匹配,然後如果是建立了三個索引的複合索引,但是where沒有從第一個開始(火車頭,最左匹配原則)
-
全值匹配我最愛,中間兄弟不能斷,
-
函數自動轉換或者計算會失效索引,範圍之後全失效
-
使用不等於會使索引失效
-
is null ,is not null 也會使索引失效
-
儘量使用覆蓋索引,索引列和查詢列一致,之訪問索引的查詢,減少select *
-
like以%開頭會使索引失效,百分like加右邊
如何解決like%寫右邊索引失效
比如 給name 和age建立索引,兩個%不會失效索引
用覆蓋索引來避免全盤掃碼
- varchar類型絕對不能失去單引號,數字失去的話會自動轉換,但是會失去索引
- 少用or,用它連接索引會失效
- 最佳左匹配原則
【優化總結口訣】
全值匹配我最愛,最左前綴要遵守;
帶頭大哥不能死,中間兄弟不能斷;
索引列上少計算,範圍之後全失效;
Like百分寫最右,覆蓋索引不寫星;
不等空值還有or,索引失效要少用;
VAR引號不可丟,SQL高級也不難!
局部性原理
每次取數據都是按頁取,Innodb是16kb,MYyISAM是4kb
慢Sql的分析,排除
- 觀察,先跑一天,看看生產的慢SQL情況
- 開啓慢查詢日誌,設置超過一定時間就算是慢sql,然後提取出來
- explain對sql進行分析
- show profile sql執行細節和生命週期情況
- 運維對sql服務器的參數調優
慢查詢日誌
set global slow_query_log=1;
show variables like ‘%slow_query_log%’;
會出現slow_query_log_file地址
set global long_query_time=3; //修改慢查詢閾值
打開log文件
show global status like ‘Slow_queries%’; //打印慢sql條數
mysqldumpslow 日誌分析
ShowProfile
是mysql可以用來分析當前會話中語句執行的資源消耗情況,
默認保存最近15次運行結果,默認關閉set profiling=on
然後會顯示sql ,id,執行時間,
再show profile cpu,block io for query id查看sql執行生命週期
數據庫鎖
按操作來分有讀鎖和寫鎖
可以想想讀寫鎖,讀鎖可以共享同時,但是寫鎖是獨佔的不能和讀鎖一起,也不能和寫鎖一起
lock table 表名 read,表名 write;
unlock tables;釋放表鎖
MYIASAM是寫鎖優先,不適合作爲查詢的數據庫引擎,適合寫的場景,比如店家上架
按照類別分有表鎖和行鎖
表鎖粒度大,效率低,容易死鎖,衝突高
行鎖粒度小,效率高,頁鎖,發生鎖的衝突低,併發級別高
數據庫事務
Innodeb支持
什麼是事務,事務是保證業務要麼成功要麼失敗的方案,具有ACID屬性
A 原子性 要麼雙方一起成功要麼一起失敗
C 一致性 成功後的總合和之前的總合應該相等,質量沒有變化
I 隔離性 每個事務的執行不會影響其它的事務,且事物內部對外部不可見
D 持久性 事務完成,數據永久保存在數據庫中
更新丟失
兩個事務都同時更新一行數據,一個事務對數據的更新把另一個事務對數據的更新覆蓋了。這是因爲系統沒有執行任何的鎖操作,因此併發並沒有被隔離開來。
髒讀
一個事務讀取到了另一事務未提交的數據操作結果。這是相當危險的,因爲很可能所有的操作都被回滾。
A修改數據之前,有200快,B查到了未提交的200,A這時候扣了100塊,提交完是100,B卻是查到未提交的之前的200
不可重複讀(修改)
一個事務對同一行數據重複讀取兩次,但是卻得到了不同的結果
- 虛讀:事務T1讀取某一數據後,事務T2對其做了修改,當事務T1再次讀取該數據時得到與前一次不同的值。
Mysql默認是可重複讀
幻讀(新增和刪除)
- 幻讀:事務在操作過程中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺少了第一次查詢中出現的數據。這是因爲在兩次查詢過程中有另外一個事務插入數據造成的。
事務隔離級別
未提交讀: 可以處理更新丟失,如果一個事務已經開始寫數據,則不允許其他事務同時進行寫操作,但允許其他事務讀此行數據。可通過“排他寫鎖”實現。
提交讀: 處理更新丟失、髒讀。讀取數據的事務允許其他事務繼續訪問改行數據,但是未提交的寫事務將會禁止其他事務訪問改行。可通過“瞬間共享讀鎖”和“排他寫鎖”實現。
可重複讀取(Repeatable Read): 處理更新丟失、髒讀和不可重複讀取。讀取數據的事務將會禁止寫事務,但允許讀事務,寫事務則禁止任何其他事務。可通過“共享讀鎖”和“排他寫鎖”實現,別人在讀的時候不允許其它人寫
序列化(Serializable): 提供嚴格的事務隔離。要求失去序列化執行,事務只能一個接一個地執行,不能併發執行
隔離級別越高,越能保證數據的完整性和統一性,但是對併發性能的影響也越大。對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設爲Read Committed。它能夠避免髒讀,而且具有較好的併發性能。儘管它會導致不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,可以由應用程序採用悲觀鎖或樂觀鎖來控制。