《高性能 MySQL》讀書筆記

結構和歷史mysql

1.  隔離級別有四種:
READ UNCOMMITTED(未提交讀),同事務中某個語句的修改,即便沒有提交,對其餘事務也是可見的。這個也叫髒讀。
READ COMMITTED(提交讀),另外一個事務只能讀到該事務已經提交的修改,是大多數據庫默認的隔離級別。可是有下列問題,一個事務中兩次讀取同一個數據,因爲這個數據可能被另外一個事務提交了兩次,因此會出現兩次不一樣的結果,因此這個級別又叫作不可重複讀。這裏的不同的數據包括虛讀(兩次結果不一樣)和幻讀(出現新的或者缺乏了某數據)。
REPEATABLE READ(可重複讀),這個級別不容許髒讀和不可重複讀,好比MYSQL中經過MVCC來實現解決幻讀問題。
SERIALIABLE(可串行化),這兒實現了讀鎖,級別最高。ios

2.  顯示和隱式鎖定:事務執行中,隨時能夠執行鎖定,鎖只有在COMMIT或ROLLBACK的時候才釋放,並且全部的鎖是同時釋放的。這些鎖定都是隱式鎖定。也能夠經過特定語句顯式鎖定,好比SELECT … LOCK IN SHARE MODE等。sql

3.  MVCC(多版本併發控制):經過保存數據在某個時間點的快照來實現。在INNODB中經過每行記錄後保存兩個隱藏的列,一個保存行的建立時間,一個保存行的過時(刪除)時間,這兒的保存不是時間而是系統版本號,隨着事務的數量增長而增長版本號。
SELECT:只找版本號早於當前事務版本的數據,刪除版本要大於當前版本號。
INSERT:插入時保存當前版本號爲行版本號。
DELETE:爲刪除的每行保存當前版本號爲行的刪除標示。
UPDATE:先爲插入的行保存版本號,同時保存當前版本號爲行刪除標示。數據庫

4.  INNODB經過MVCC來支持高併發,經過間隙鎖來防止幻讀。緩存

5.  MYISAM支持讀取的時候插入(併發插入),支持延遲更新索引鍵(Delayed Key Write),先寫內容最後才更新索引,須要指定DELAY_KEY_WRITE。性能優化

SCHEMA與數據類型優化服務器

1.  避免使用NULL。併發

2.  整數類型中,TINYINT使用8位存儲空間,BIGINT爲64位,通常作SIMHASH選擇64位作特徵值應該是基於這個,轉成16進制有16位。其中指定的寬度只在命令行中展現時起做用。異步

3.  實數類型中,DECIMAL用於存儲精確的小數,好比貨幣。函數

4.  VARCHAR比定長CHAR更省空間,由於它只須要使用必要的空間,可是其須要使用1或者2個額外字節用來記錄字符串的長度。可是在update的時候,容易形成碎片。
CHAR是定長的,MYSQL根據定義字符串的長度分配空間,並且其會刪除全部末尾空格。好比存」STRING 「的時候,末尾的空格會被刪除。
VARCHAR(5)和VARCHAR(100)存同一個字符雖然空間開銷相同,可是在存的時候會消耗更多內存,還有在使用臨時表的時候也會比較糟糕。

5.  BLOB和TEXT是爲存儲很大數據而設計的,分別以二進制和字符方式存儲。TEXT是SMALLTEXT的同義詞,BLOB也是。

6.  ENUM類型存儲是很是緊湊,其實際存儲爲整數。

7.  BIT能夠在一列中存儲一個或多個0/1值,最大長度爲64。問題是存進去是二進制,可是展現出來倒是十進制的。

8.  計數器表的優化,對於單表的a+1操做可能受到鎖的影響,能夠經過建立100行數據,而後隨機選取一行寫,取的時候使用SUM(a)進行查詢。

9.  高效ALTER TABLE,修改表結構涉及到不須要改變數據只要改frm文件的時候,可使用語句ALTER COLUMN來操做。
還有替換frm的高效方法,首先create table like來創建新表,修改新表結構,對舊錶數據執行鎖定」FLUSH TABLES WITH READ LOCK;」
執行系統命令,mv new.frm a.frm之類,記得備份。
UNLOCK TABLES;

10. 高效載入數據到MyISAM表,能夠暫時禁用索引。
ALTER TABLE tab DISABLE KEYS;
ALTER TABLE tab ENABLE KEYS;
可是DISABLE KEYS只對非惟一索引有效。

建立高性能的索引

1.  B-Tree索引,其意味着全部的值都是按照順序存儲的,而且每個葉子頁到根的距離都相等。
B-Tree對索引列是順序存儲的,因此很適合查找範圍數據。
缺點是必須按照索引從最左列開始查找,不然沒法使用索引。

2.  R-Tree(空間數據索引),MyISAM表支持空間索引,能夠用做地理數據存儲。

3.  獨立的列沒法使用索引,獨立的列是指索引列爲表達式的一部分或者函數的參數。

4.  前綴索引,索引很長的字符列會讓索引變大變慢,因此選擇一個合適的長度來索引是頗有效率的。
首先須要找出合適長度的前綴,用語句:
select count(*) as cnt,LEFT(city,3) as pref from group by pref order by cnt;
調整其中LEFT函數的值選擇最合適的長度。建索引時以下:
ALTER TABLE a ADD KEY(city(7));
還能夠考慮後綴索引,好比查找某個域名的全部電子郵件地址,須要把字符串翻轉後存儲。

5.  多列索引的順序很是重要,要選擇最有效率的列放到最左邊。

6.  聚族索引並非一種單獨的索引類型,而是一種數據存儲的方式。
當表有聚簇索引時,它的數據行實際上存放在索引的葉子頁(LEAF PAGE)中,聚簇表示數據行和相鄰的鍵值緊湊地存儲在一塊兒。

7.  當存在OR條件的時候,會看到此時使用了index_merge類型索引,這個說明表上的索引很糟糕,這個是因爲在OR左右兩個條件都創建了索引,應該修改索引,或者使用IGNORE INDEX來會略某些索引。

8.  在選擇多列索引的時候,一般把選擇性更大的放到前面(該條件下統計數量更小的)。

9.  在INNODB中最好使用自增做爲主鍵,而使用UUID等隨機的聚簇索引會對I/O密集型應用形成很壞性能,它使得聚簇索引的插入變得徹底隨機。

10. 當要查詢的字段的值在索引中,就稱該索引爲覆蓋索引。在explain的時候extra顯示using index。爲了能用到覆蓋索引,可使用延遲關聯(deferred join)。書上有很巧妙的例子:)。注意的是,INNODB中二級索引的葉子節點都包含了主鍵的值,因此查詢的值包含主鍵id時,主鍵id能夠不在所建的聯合索引中。關於延遲關聯還有個經典例子,大偏移翻頁的時候。

11. 當索引類型爲index時,說明MYSQL使用了索引掃描來作排序。

12. 在5.1或更新版本中,INNODB在服務器端過濾掉行後就釋放鎖,而早期版本中則須要在事務提交後才釋放鎖。

13. EXPLAIN中出現Using where表示在存儲引擎返回行後再使用where過濾條件。

14. 一個訣竅,一個符合查詢條件的多列索引中,有時候條件裏沒有包含存在的索引列,這時候使用IN來知足最左前綴。好比多列索引中有sex列,可是用戶查詢時沒有選擇sex,則使用IN(‘M’,’F’)來知足使用索引的條件。
某一些條件好比age,通常是範圍查詢,而根據最左前綴碰到範圍查詢後會終止,因此這類通常放在多列索引的最後面。
而使用開始的IN語句知足最左前綴也不能濫用,3個IN條件,每 個有N個枚舉值,則會產生N*N*N中組合,下降效率。

15. 按順序訪問範圍數據很快,由於順序I/O不須要屢次磁盤尋道,不須要額外排序操做。

16. 聚簇索引(Clustered Index),一個索引項直接對應實際數據記錄存儲頁。
索引項和實際數據行的排序徹底同樣。
一個表只能有一個聚簇索引。可是該列能包含多個列,就像電話簿使用姓氏和名字同時進行排序。

17. INNODB支持聚簇索引,其中聚簇索引就是表,必需要像MYISAM那樣的行存儲。聚簇索引的每一個葉子節點都包含了主鍵值、事務ID、用於事務和MVCC的回滾指針以及全部的剩餘列。
InnoDB的二級索引和聚簇索引很不相同。InnoDB二級索引的葉子節點中存儲的不是「行指針」,而是主鍵值,並以此做爲指向行的「指針」。
在INNODB主鍵中插入UUID,因爲主鍵會保持有序,會嚴重影響性能。

查詢性能優化

1.  檢查響應時間,掃描的行和返回的行,掃描的行數和訪問類型(Explain的時候)是三個簡單衡量查詢的指標。

2.  在進行大查詢的時候使用分而治之,好比delete大數據的時候使用limit,使用do while分解操做,避免大語句鎖住過多數據,佔滿事務日誌,耗盡系統資源,阻塞不少重要查詢。

3.  關聯查詢拆成簡單查詢而後在應用層聚合數據,可讓緩存效率更高,單個查詢能夠減小鎖競爭,自己查詢效率也更高,在數據庫中作關聯查詢還可能致使須要重複地訪問一部分數據。

4.  mysql客戶端和服務器之間的通訊協議是半雙工,任何一個時刻只能單向發送數據而不能兩邊同時進行,像是拋繡球。因此mysql一般須要等全部數據都已經發送給客戶端後才能釋放這條查詢鎖佔用的資源,這時max_allowed_packet很重要。

5.  一個完整查詢包含以下過程包括客戶端/服務器端通訊->查詢緩存->語法解析器和預處理->查詢優化器->數據和索引的統計信息->查詢執行引擎->返回結果給客戶端。下面會一次說說每一個步驟。

6.  查詢狀態,一個鏈接或者線程,在任什麼時候刻都有一個狀態。
sleep,線程正在等待客戶端發來新請求。
query,線程正在執行查詢或者將結果發送給客戶端。
locked,該線程正在等待表鎖。而存儲引擎級別的鎖好比innodb的行鎖並不會體如今線程狀態。
copying to tmp table[on disk],線程正在執行查詢而且將結果集都複製到一張臨時表,通常是group by或者文件排序等操做。on disk表示正在將一個內存臨時表放到磁盤上。
sorting result,線程正在對結果集進行排序。
sending data,線程可能在多個狀態間傳送數據,或者正在生成結果集或者正在向客戶端返回數據。
瞭解這些狀態能夠很快了解誰正在擲球。

7.  在查詢緩存後,先進行語法解析器和預處理,mysql經過關鍵字將SQL語句進行解析並生成一顆對應的解析樹,進行語法規則驗證。當語法樹被認爲合法了,則由優化器將其轉化爲執行計劃,一條語句可能有不少執行方式並返回相同結果,優化器的做用就是找到這其中最好的執行計劃。優化器是基於成原本預測。

8.  在不少數據庫中IN等同OR,可是在mysql中,會把IN中的數據先進行排序,而後經過二分查找的方式來肯定列表中的值是否知足條件,這是一個O(log n)的操做。當IN中有大量數據的時候效率會更快。

9.  關聯查詢,MySQL認爲任何一次查詢都是一次關聯,不只僅是UNION,子查詢等均可能是。對於UNION,MYSQL現將一系列查詢的單個查詢結果放到一個臨時表中,再從新讀出臨時表的數據來完成UNION查詢。
MYSQL對任何關聯都執行嵌套循環關聯操做,即先在一個表中循環取出單條數據,而後嵌套循環到下一個表中尋找匹配的行,依次下去,直到找到全部表中匹配的行爲爲止。
當在FROM子句中遇到子查詢時,先執行子查詢並將其結果放到一個臨時表中,而後將這個臨時表當作一個普通表對待(派生表)。

10. 執行計劃,MYSQL生成查詢的一顆指令數。可使用EXPLAIN EXTENDED後再使用WARNINGS。
任何多表查詢均可以用一棵樹來表示,好比四表查詢:
而事實上MYSQL老是從一個表開始一直嵌套循環,是一顆左側深度優先的樹。

11. 關聯查詢優化器,用來決定多個表關聯時的順序。
因爲其第二個是從第一個表的結果嵌套循環的,因此第一個表尤其重要,優化器會選擇掃描不多的行。
若是有超過N各表的關聯,那麼須要檢查N的階乘種關聯順序,這是很糟糕的。

12. 排序優化,若是order by子句中全部列來自第一個表,那麼在處理第一個表是就會進行文件排序,會顯示Using filesort
其他狀況,都須要先把關聯結果放到一個臨時表中,最後再進行文件排序。會顯示Using filesort,Using temporary;

13. 關聯子查詢是一個爛查詢。select from IN(select id from where)
由於優化器會改寫成select from where exists(select * from where AND a.id=b.id)
因此子句須要主句的id才能查詢,這樣MYSQL會首先對主句進行全表掃描來找出全部id,而後使用id對子句進行逐個執行子查詢。
因此方法一是:select * from a
inner join b USING(id)
where c=1;
還有個方法,GROUP_CONCAT。

14. MYSQL目前不支持鬆散索引掃描,就是最左綴的一個原則,聯合索引無法越過左邊的字段。雖然其聯合索引所有有序。

15. 在同一個表中進行查詢和更新,不能用update set a=(select from),要使用生成表的方式來繞過限制,update tb inner join(select) as b using(id) set tb.a=b.a,這樣會關聯到一個臨時表。

16. HINT查詢優化器的提示,
HIGH_PRIORITY和LOW_PRIORITY,優先級設置。HIGH通常用於select語句,會將其放到隊列的最前面。LOW會讓語句一直處於等待狀態,只要隊列中還有須要訪問同一個表的語句。這兩個提示只在表鎖的存儲引擎中有效。
DELAYED,對insert和replace有效,會將提示當即返回給客戶端,並將插入的行數放到緩衝區,而後在空閒時批量寫入。會致使LAST_INSERT_ID()失效。
FOR UPDATE和LOCK IN SHARE MODE,主要控制SELECT語句的鎖機制,只對實現了行級鎖的存儲引擎有效。

17. count(*)中的*不是指全部的列,而是會忽略全部的列而直接統計行數。
統計同一個列裏不一樣顏色的商品語句,select sum(if(color=’blue’, 1, 0)) as blue,sum(if(color=’red’, 1, 0)) as red from items;
其中也能夠寫成sum(color=’blue’),sum(color=’red’)
或者使用以下語句
select count(color=’blue’ OR NULL) as blue, count(color=’red’ OR NULL) as red from items;

18. 關於大數據偏移的一個經常使用方法是延遲關聯,
select from a inner join(select id from limit 100,10) as b using(id);
或者計算出具體位置使用between,
OFFSET會致使MYSQL掃描大量不須要的行並拋棄,避免使用OFFSET,好比計算出主鍵的id,使用
select from a where id<2131231 order by id desc limit 20;

19. MYSQL老是經過建立並填充臨時表的方式來執行UNION查詢。

MySQL高級特性

1.  分區表:是一個獨立的邏輯表,底層是由多個物理子表組成。索引也是按照分區的字表定義的,沒有全局索引。
在建立的時候使用PARTITION BY子句定義每一個分區存放的數據。在執行查詢的時候優化器會根據分區定義過濾那些沒有咱們須要數據的分區,這樣查詢就無需掃描全部分區。
CREATE TABLE sales(date DATETIME NOT NULL,…) ENGINE=InnoDB PARTITION BY RANGE(YEAR(date))(
PARTITION p_2010 VALUES LESS THAN (2010),
PARTITION p_2011 VALUES LESS THAN (2011),
PARTITION p_catchall VALUES LESS THAN MAXVALUE);

2.  查詢緩存能夠更快得返回數據,可是高併發下可能成爲整個服務器的資源競爭點,多核服務器上還可能致使服務器僵死。因此不少時候咱們認爲應該默認關閉查詢緩存。
在判斷緩存是否命中的時候,MySQL不會解析格式化查詢語句,而是直接使用SQL語句和一些客戶端的信息,好比空格,註釋等任何的不一樣都會致使緩存的不命中。
當語句中存在不肯定數據好比NOW()等函數時都不會被緩存。
當打開查詢緩存時,每一個讀查詢都會先檢查是否命中緩存,寫入緩存的時候會帶來系統消耗,當某個表有變更,則對應表的全部緩存都將設置失效,當查詢緩存太大或者碎片太多,都會帶來很大系統消耗。
因此使用查詢緩存時要當心,不要設置太大內存,並且要在肯定有明確收益的時候才使用查詢緩存。

優化服務器設置

操做系統和硬件優化

1.  存儲引擎一般把數據和索引都保存在一個大文件中,這意味着RAID(磁盤冗餘陣列)存儲大量數據一般是最可行的方法。
RAID 0
這是成本最低和性能最高的RAID配置,由於其沒有冗餘,建議只在不擔憂數據丟失的狀況下使用。並且其的損壞機率比單塊磁盤還要高。
RAID 1
提供不錯的讀性能,並且在不一樣磁盤間冗餘數據,因此有很好的冗餘性。
RAID 5
經過分佈奇偶校驗把數據分佈到多個磁盤,這樣任何一個盤的數據失效,均可以從奇偶校驗中重建。可是若是有兩塊磁盤失效了,則整個卷數據都沒法恢復。就每一個存儲單元的成本而言,這是最經濟的,由於只額外消耗了一塊磁盤的存儲空間。
RAID 10
他由分片的鏡像組成,因此對讀和寫都有良好的擴展性。
RAID 15
由條帶化的RAID5組成。用於存放很是龐大的數據集。

2.  vmstat和iostat

複製

1.  複製解決的基本問題是讓一臺服務器的數據與其餘服務器保持同步。

2.  MYSQL支持兩種複製方式:基於行的複製和基於語句的複製。基於語句的複製也叫邏輯複製,很早就存在。基於行的複製5.1版才加進來。這兩種方式都是經過在主庫記錄二進制日誌,在備庫重放日誌的方式來實現異步的數據複製。這意味着同一時間點備庫上的數據可能和主庫存在不一致。

3.  複製一般不會增長主庫的開銷,主要是啓用二進制日誌帶來的開銷。

可擴展的MySQL

1.  向上擴展:購買更多性能強悍的硬件。

2.  向外擴展,分三部分:複製,拆分以及數據分片。

3.  複製可分爲按功能拆分,好比分爲論壇、新聞等。
數據分片,全局數據存儲在單點上,而對同功能下的數據增加過多的時候進行分片。好比對用戶表進行分片。

4.  生成全局惟一的ID
好比分片後,使用AUTO_INCREMENT來獲取惟一ID會有問題。
首先可使用auto_increment_increment和auto_increment_offset兩個變量來讓MYSQL以指望的值和偏移量增長自增列的值。好比兩臺服務器,能夠配置兩臺服務器的自增幅度爲2,其中一臺偏移爲1一臺爲2.因此其中一臺老是奇數一臺老是偶數。
還能夠在一個全局數據庫中建立自增來生成惟一數字。
還能從全局節點中請求一批數字,用完再申請。
還可使用分片號和自增的組合來做爲惟一ID。
可使用UUID()來生成全局惟一值。由於其值很大且不連續,所以不適合作innodb的主鍵。這時能夠考慮UUID_SHORT(),能生成連續的值,並使用64位代替128位。

5.  經過多實例擴展。

6.  經過集羣擴展。

7.  向內擴展,好比對不須要的數據進行歸檔和清理。

備份與恢復

1.  熱備份指備份不須要任何的停機服務。

2.  邏輯備份指導出,物理備份指複製原始文件。

3.  差別備份是對自上次全備份後全部改變的部分而作的備份,增量備份則是字從任何類型的上次備份後全部的修改作的備份。

4.  生成邏輯備份:
mysqldump test t1
select into outfile以符號分割文件格式建立數據的邏輯備份。

5.  文件系統快照。
快照會在/dev目錄下建立一個新的邏輯卷,能夠像掛載其餘設備同樣掛載它。

6.  備份腳本化

相關文章
相關標籤/搜索