1. 程式人生 > >MySQL小技巧

MySQL小技巧

簡介篇

儲存引擎

MyISAM是MySQL5.1及之前的版本的預設儲存引擎。MyISAM提供了大量的特性,包括全文索引、壓縮、空間函式(GIS)等,但是MyISAM不支援事務和行級鎖,而且有一個毫無疑問的缺陷就是崩潰後無法安全回覆。

MyISAM會將表儲存在兩個檔案中:資料檔案和索引檔案,分別以.MYD和.MYI為副檔名。

InnoDB表是基於聚簇索引建立的。

推薦InnoDB儲存引擎

資料型別

MySQL支援的資料型別非常多,總體上分為:數字、字元、日期、JSON。選擇正確的資料型別對於獲得高效能至關重要。不管儲存那種型別的資料,下面幾個簡單的原則都有助於作出更好的選擇。

1)更小的通常更好

一般情況下,應該儘量使用可以正確儲存資料的最小資料型別。更小的資料型別通常更快,因為他們佔用更少的磁碟、記憶體和CPU快取,並且處理時需要的CPU週期也更少。

2)簡單就好

簡單的資料型別的操作通常需要更少的CPU週期。例如,整型比字元操作代價更低,因為字符集和校對規則(排序規則)使字元比較比整型比較更復雜。

3)儘量避免NULL

很多表都包含可為NULL的列,即使應用程式並不需要儲存NULL也是如此,這是因為可為NULL是列的預設屬性。通常情況下最好指定列為NOT NULL,除非真的需要儲存NULL值。

用NULL會浪費儲存空間,因為InnoDB需要一個額外的位元組儲存。

NULL欄位的複合索引無效。

如果查詢中包含可為NULL的列,對MySQL來說更難優化,因為可為NULL的列使得索引、索引統計和值比較都更負責。當可為NULL的列被索引時,每個索引記錄需要一個額外的位元組,在MyISAM裡甚至還可能導致固定大小的索引(例如只有一個整數列的索引)變成可變大小的索引。

日期型別

DATE型別表示日期的年月日部分,範圍在1000-01-01到9999-12-31之間。

DATETIME型別表示日期的年月日時分秒部分,範圍在1000-01-01 00:00:00到9999-12-31 23:59:59之間。

TIMESTAMP型別表示日期的年月日時分秒部分,範圍在1970-01-01 00:00:01UTC到2038-01-19 03:14:07UTC之間。

可以讓DATETIME和TIMESTAMP自動初始化並且隨其他欄位的修改而自動修改。

例如:

CREATE TABLE aaa (

id bigint(11) NOT NULL AUTO_INCREMENT,

ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

dt datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

name varchar(20) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

主鍵

整數通常是主鍵的最好選擇,因為他們很快並且可以使用AUTO_INCREMENT。

如果可能,應該避免使用字串型別作為主鍵,因為他們很消耗空間,並且通常比數字型別慢。尤其在MyISAM表裡使用字串作為主鍵要特別小心。MyISAM預設對字串使用壓縮索引,這會導致查詢慢很多。

對於完全“隨機”的字串也許多加註意,例如MD5()、SHA1()、或者UUID()產生的字串。這些函式生成的新值會任意分佈在很大的空間內,這回導致INSERT以及一些SELECT語句變得很慢:

因為插入值會隨機地寫到索引的不通位置,所以是的INSERT語句更慢。這回導致頁分裂、磁碟隨機訪問,以及對於聚簇儲存引擎產生聚簇索引碎片。

SELECT語句會變得更慢,因為邏輯上相鄰的行會分佈在磁碟和記憶體的不同地方。

隨機值導致快取對所有型別的查詢語句效果都很差,因為會使得快取賴以工作的訪問區域性性原理失效。如果整個資料集都一樣的“熱”,那麼快取任何一部分特定資料到記憶體都沒有好處;如果工作集比記憶體大,快取將會有很多重新整理和不命中。

如果儲存UUID值,則應該移除“-”符號;或者更好的做法是,用UNHEX()函式轉換UUID值為16位元組的數字,並且儲存在一個BINARY(16)列中。檢索時可以通過HEX()函式來格式化為十六進位制格式。

UUID()生成的值與加密雜湊函式例如SHA1()生成的值有不同的特徵:UUID值雖然分佈也不均勻,但還是有一定順序的。儘管如此,還是不如遞增的整數好用。

索引

B-Tree索引

B-Tree索引能夠加快訪問資料的速度,因為儲存引擎不再需要進行全表掃描來獲取需要的資料,取而代之的是從索引的根節點開始進行搜尋。

B+樹索引並不能找到一個給定鍵值的具體行。B+樹索引能找到的只是被查詢資料行所在的頁。然後資料庫通過把頁讀入記憶體,再在記憶體中進行查詢,最終得到要查詢的資料。

限制:

如果不是按照索引的最左列開始查詢,則無法使用索引。

不能跳過索引中的列。

如果查詢中有某個列的範圍查詢,則其右邊所有列都無法使用索引優化查詢。

Hash索引

高效能索引策略:

分割槽表

分割槽功能並不是在儲存引擎層完成的。分割槽有水平分割槽和垂直分割槽兩種,但是MySQL不支援垂直分割槽。

大多數DBA會有這樣一個誤區:只要啟用了分割槽,資料庫就會變得更快。這個結論是存在很多問題的。其實,分割槽對於某些SQL語句效能可能會帶來提高,但是分割槽主要用語高可用性,利於資料庫的管理。在OLTP應用中,對於分割槽的使用應該非常小心。如果只是一味使用分割槽,而不理解分割槽是如何工作的,也不清楚你的應用如是使用分割槽,那麼分許極有可能只會對效能產生負面的影響。

MySQL資料庫支援以下幾種分割槽型別:

RANGE分割槽:

LIST分割槽:

HASH分割槽:

KEY分割槽:

不論建立哪種型別的分割槽,如果表中存在主鍵或者是唯一索引時,分割槽列必須是唯一索引的一個組成部分。

唯一索引可以是允許NULL值的,並且分割槽列只要是唯一索引的一個組成部分,不需要整個唯一索引列都是分割槽列。

當建表時沒有指定主鍵和唯一索引時,可以指定任何一列為分割槽列。

規範篇

規範存在意義

保證線上資料庫schema規範

減少出問題概率

方便自動化管理

規範需要長期堅持,對開發和DBA是一個雙贏的事情

核心規範

不在資料庫做運算:cpu計算務必移至業務層

控制單表資料量:單表記錄控制在1000w

控制列數量:欄位數控制在20以內

平衡正規化與冗餘:為提高效率犧牲正規化設計,冗餘資料

拒絕3B:拒絕大sql,大事物,大批量

基本命名和約束規範

表字符集選擇UTF8 ,如果需要儲存emoj表情,需要使用UTF8mb4(MySQL 5.5.3以後支援)

儲存引擎使用InnoDB

變長字串儘量使用varchar varbinary

不在資料庫中儲存圖片、檔案等

單表資料量控制在1000w以下

庫名、表名、欄位名不使用保留字

庫名、表名、欄位名、索引名使用小寫字母,以下劃線分割 ,需要見名知意

庫表名不要設計過長,儘可能用最少的字元表達出表的用途

索引規範

單個索引欄位數不超過5,單表索引數量不超過5,索引設計遵循B+ Tree索引最左字首匹配原則

選擇區分度高的列作為索引

建立的索引能覆蓋80%主要的查詢,不求全,解決問題的主要矛盾

DML和order by和group by欄位要建立合適的索引

不在索引做列運算

避免索引的隱式轉換

避免冗餘索引

不要用外來鍵

關於索引規範,一定要記住索引這個東西是一把雙刃劍,在加速讀的同時也引入了很多額外的寫入和鎖,降低寫入能力,這也是為什麼要控制索引數原因。之前看到過不少人給表裡每個欄位都建了索引,其實對查詢可能起不到什麼作用。

冗餘索引例子

idx_abc(a,b,c)

idx_a(a) 冗餘

idx_ab(a,b) 冗餘

隱式轉換例子

欄位:remark varchar(50) NOT Null

MySQL>SELECT id, gift_code FROM gift WHERE deal_id = 640 AND remark=115127; 1 row in set (0.14 sec)

MySQL>SELECT id, gift_code FROM pool_gift WHEREdeal_id = 640 AND remark=‘115127’; 1 row in set (0.005 sec)

欄位定義為varchar,但傳入的值是個int,就會導致全表掃描,要求程式端要做好型別檢查

欄位規範

所有欄位均定義為NOT NULL ,除非你真的想存Null

欄位型別在滿足需求條件下越小越好,使用UNSIGNED儲存非負整數 ,實際使用時候儲存負數場景不多

使用datetime儲存時間

使用varchar儲存變長字串 ,當然要注意varchar(M)裡的M指的是字元數不是位元組數;使用UNSIGNED INT儲存IPv4 地址而不是CHAR(15) ,這種方式只能儲存IPv4,儲存不了IPv6

使用DECIMAL儲存精確浮點數,用float有的時候會有問題

少用blob text

SQL規範

儘量不使用儲存過程、觸發器、函式等

避免使用大表的JOIN,MySQL優化器對join優化策略過於簡單

避免在資料庫中進行數學運算和其他大量計算任務

SQL合併,主要是指的DML時候多個value合併,減少和資料庫互動

UPDATE、DELETE語句不使用LIMIT,容易造成主從不一致

不用select *

慎用count(*)

避免負向%

請使用同類型比較

sql語句儘可能簡單

一條sql只能在一個cpu運算

大語句拆小語句以減少鎖時間

一條大sql可以堵死整個庫

簡單的事務

事務時間儘可能短

bad case:

上傳圖片事務

OR改寫為IN()

or的效率是n級別

in的訊息時log(n)級別

in的個數建議控制在200以內

select id from t where phone=’159′ or phone=’136′;

=>

select id from t where phone in (’159′, ’136′);

OR改寫為UNION

mysql的索引合併很弱智

select id from t where phone = ’159′ or name = ‘john’;

=>

select id from t where phone=’159′

union

select id from t where name=’jonh’

limit高效分頁

limit越大,效率越低

select id from t limit 10000, 10;

=>

select id from t where id > 10000 limit 10;

使用union all替代union

union有去重開銷

使用load data導資料

load data比insert快約20倍;