注:本篇是《高性能Mysql》第三版的讀書筆記
Scheme && 數據類型優化
反範式的設計可以加快某些類型的查詢,但同時可能使另一些類型的查詢變慢。比如添加計數表和彙總表是一種很好的優化方式。
選擇優化的數據類型
- 存儲更小的數據類型通常更好,更小的數據類型佔用更少的內存和更少的磁盤空間。
- 簡單更好,一般能用整型表達的就不要用字符串,字符串對校驗排序規則等有更多的消耗。
- 儘量避免NULL,在設計列字段的時候儘量設計成not null ,存儲引擎對於非null的字段創建索引性能更好。
datetime和timesamp列都可以存儲相同類型的數據,時間和日期精確到秒。然而timesamp只使用datetime一半的存儲空間,並且timesamp會根據時區變化。
Integer bool numeric這種是別名。
整數類型
在設計整型存儲時儘量選擇小存儲空間的類型,可以節省很多磁盤和不必要的內存開銷。無符號和有符號使用相同的存儲空間,但是無符號存儲的正數範圍更多。
實數類型
帶有小數部分的數字,DECIMAL 是存儲精確數字的,float 和 double 是浮點數。在數據量比較大的時候,可以使用bigint存儲,並在使用的時候除以一定的基數。
字符串類型
varchar和char是常見的存儲字符串的類型,其中varchar在持久化到磁盤的時候會根據具體的值來。
varchar需要使用1個或2個額外字符存儲長度信息,在256個字節之內的需要1個額外字符。由於行是變長的,在UPDATE可能使行變得比原來更長,char 適合存儲md5這種值,定長的char不容易產生碎片,對於短的列char更節約存儲空間,因爲他不會使用額外字符存儲。char存儲字符時後面的空格會被去掉。這種對於空格的處理在不同的存儲引擎是一樣的,因爲這個是服務器的底層處理
varchar雖然是變長的,但是varchar(5)要比varchar(100)少用很多的內存。
BLOB&&TEXT
blob和text都是爲了存儲大類型設計的數據方式,分別採用二進制和字符方式存儲。
innodb會用專門的方式存儲,在行內僅需1~4字節存儲一個指針,然後在外部存儲其值。
兩者的區別是blob存儲的是二進制,沒有字符集和排序規則。
對於blob和text 在memory存儲引擎上是不支持的,所以需要使用myisam的磁盤臨時表,這樣性能就非常差,可以將blob轉成字符串,這樣就可以使用內存臨時表了,相對於磁盤臨時表要快很多,
對於排序而言,使用varchar(1000)卻只有三個字節的數據,這樣就會很佔內存,需要創建非常大的臨時表。
枚舉類型
目前工作沒遇到關係型數據庫中使用枚舉類型的。要變動需要alter table 比較麻煩。
日期和時間類型
timestamp要比datetime的效率更高,而且timestamp是帶有時區的。mysql可以根據當前時間戳更新,這個在作爲行記錄的update_time特別好用。不要用字符串或者整數保存時間。
整數標識列是最好的選擇,可以很快並且使用AUTO_INCREMENT
字符串標識列是比數字類型要慢的,uuid md5 sha1 這種也是會導致插入變慢的。
ip地址經常被使用varchar(15)來存儲,但實際上IP是32位無符號整數,不是字符串。
mysql在設計schema時需要注意的⚠️
1、不要在一個表中有太多的列,myisam和innodb的行結構總是需要轉換,所以列多是很消耗性能的。
2、不要使用太多的關聯,看阿里巴巴的開發手冊也提到不要使用超過3個表的連接查詢。
3、枚舉這個沒見有項目中的表設計使用過。
4、儘量避免NULL。有一個隱蔽的常識,null和null是不一樣的。有一些字段是需要null的,而一些別的有默認值要比null更好,這個需要根據實際情況來設置。mysql會在索引中存儲null值,而oracle卻不會。
5、別用enum、set、bit、 哈哈哈哈。
範式
對於範式,冗餘範式對我們來說是更方便的,但是需要注意的是不要導致數據不一致。
範式話的設計通常的缺點是需要關聯,更新操作要比反範式的快,而且相對的內存佔用要小一些,操作速度更快。
彙總表
對於那種不需要實時給結果但是需要統計或者那種sql需要複雜的group by left 操作導致性能不好的可以採用彙總表。這樣只需要接口查詢彙總表就能提高很多的性能,而那個彙總表的數據後臺用定時任務定時的去計算。
計數器表
計數器在web網站常常被用到,比如點擊數量等等,這個如果我們數據庫存一條記錄需要記錄這個時間的話,那麼需要每次更新的時候對行記錄進行互斥鎖,這樣就非常慢,可以採用優先在這個表中存儲100條記錄,然後更新時候隨機更新一行進行+1,這樣就大概減少了100倍的併發,提高了很高的性能。 在獲取計數器的時候計算 sum(cnt) 就可以了。
加快alter table 的操作速度
alter table test modify column test tinyint(3) not null default 5; -----慢語法
show status 展示上面這個語句做了1000次讀取和1000次插入,很慢,下面語句只涉及修改.frm文件快很多。
alter table test alter column test set default 5; -----快語法
創建高性能索引
大家都知道索引在數據量大的時候可以很快的查找到數據,起到一個高速查找,降低響應時間的作用。
如果在某個列上建立了索引,例如age 會去索引文件中按值10查找所有包含該值的數據行,然後返回數據行。
select * from habby where age = 10;
如果是多列索引,那麼列出現的順序很關鍵,mysql使用最左前綴。如果創建的是 a,b 兩列 b出現在a的前面,那麼將導致索引失效。
在mysql中索引是在存儲引擎層實現的,而不是服務器層。
Innodb存儲引擎使用的是B+tree索引, 存儲引擎以不同的方式使用B-tree例如myisam使用前綴壓縮技術使得索引更小,innodb使用按照元數據格式進行存儲。myisam索引通過數據的物理位置引用被索引的行,而innodb則根據主鍵引用被索引的行。
B-tree通常意味着所有的值都是按順序存儲的。如下圖所示。
B-tree能夠加快訪問數據的速度,因爲存儲引擎不再需要進行全表掃描來獲取需要的數據。
B-tree是對數據列進行順序存儲的,因此很適合查找範圍數據。(索引中是存在索引列的值的)
注⚠️:索引中對多個值進行排序的適合是根據創建索引時索引列的順序決定的,a,b,c 則先排a,a相同排b這樣。
使索引生效的查詢方法
索引列 a,b,c
1、全值匹配。和索引中的所有列都匹配 a='a' and b='b' and c= 'c'
2、最左前綴法。查找所有 a列 這種使用索引第一列的前綴的。 a= 'a'
3、匹配範圍值。查找第一列的範圍 where a > 'a' and a < 'z'
4、精確匹配第一列,並範圍匹配第二列。 where a= 'a' and b> 'a' and b < 'z'
5、只訪問索引的查詢。
因爲B-tree索引的節點是有順序的,所以索引還可以用於排序。
注⚠️:聯合索引a,b,c中b,c如果沒有a列在左邊是使用不了索引的。也不能a,c使用而跳過b
如果使用like 則聯合索引中後面的列索引都用不了了。
哈希索引
哈希索引基於哈希表實現,只有精確匹配索引所有列的查詢纔有效,例如url
如下是innodb存儲引擎下的索引。mysql5.7 navicat15
1、哈希索引只包含哈希值和行指針。
2、哈希索引數據不是按照索引列順序存儲的,所以也無法用於排序。
3、哈希索引也不支持部分索引列匹配查找。
4、哈希索引只支持等值比較查詢。 = in <==> 不支持範圍查詢
最常見的索引是Btree索引,order by group by 也會提高很多效率。 對於varchar text等列的索引不會全部索引,會有一定長度的值被索引。
前綴索引
alter table test add key (city(7)); 前綴索引制定索引的長度,這樣索引會小,但是不能order by group by
索引有如下優點
1、大大減少了服務器需要掃描的數據量
2、索引可以幫助服務器避免排序和臨時表
3、可以將隨機io 變成順序io (這能提高百倍的效率)
注⚠️:不要建立太多單列索引, where a= '1' and b = '2' where a= '1' or b= '2' 這種會對兩個單列索引進行合併,從而執行了全表掃描,並沒有真用上兩個單列索引。 但是當多列索引a,b的時候 and是可以用的。 or不行。
聯合索引的創建順序非常關鍵,要儘量保證 前面列會篩選掉比較多的結果集,不然這個索引用的意義就不這麼大了。
聚簇索引
聚簇索引不是一種單獨的索引類型,而是一種數據存儲方式。但innodb的聚簇索引實際上在同一個結構中保存了B-Tree索引和數據行。當表有聚簇索引時,它的數據行實際上存放在索引的葉子頁中,術語聚簇表示數據行和相鄰的鍵值緊湊的存儲在一起。
innodb通過主鍵聚集索引。聚簇索引就是表,每一個葉子節點存儲了主鍵值,事務ID,事務和MVCC的回滾指針,以及所有剩餘的列。myisam則會按插入順序存儲行號指針。不會存真正的數據。
聚簇索引最大限度的提高了IO密集型應用的性能。插入順序嚴重依賴於主鍵的順序,因此主鍵遞增插入很關鍵。存在二級索引。
使用UUID這種當主鍵存儲就很有問題了。
不順序的缺點
寫入目標頁的可能已經刷到磁盤並從緩存中刪除或者沒被加載到緩存,在插入前會產生大量的隨機IO。
寫入是亂序的,innodb需要頻繁的做頁分裂操作。
頻繁的頁分裂,最終數據會有碎片。
如果一個索引包含查詢所需要的字段的值,我們稱爲覆蓋索引。極大的提高性能。直接返回。可以避免對主鍵索引的二次查詢。
mysql中只能用BTree做覆蓋索引。
只有當索引的列順序和order by的順序一致時才使用索引對結果進行排序。
myisam使用壓縮前綴索引,先完全保存索引塊中的第一個值,然後將其他值和第一個值進行比較得到相同前綴的字節數和剩餘的不同後綴部分,例如:第一個索引塊存儲perform 第二個值是performance 存儲成7,ance 使用更少的存儲空間。
注⚠️:不要在相同的列以相同的順序建立相同類型的索引。避免多個範圍條件查詢,比較慢。
索引可以讓查詢鎖定更少的行,如果查詢從不訪問那些不需要的行,那麼就會鎖定更少的行。
一個狡猾的方法是 sex這種只有兩個選項的列使用 sex in (1,2) 這樣就能使用到索引了。
大數據下,重新創建索引能夠減少碎片,從而提高索引查詢效率。