Mysql 數據庫開發規範
參考原文
詳細內容看上文,提幾點需要記住的要點。
庫表基礎規範
1.註釋
每個表要添加註釋,對 status 型需指明主要值的含義,如”0-離線,1-在線”
2.表的字段數量
- 單表字段數上限30左右,再多的話考慮垂直分表,一是冷熱數據分離,二是大字段分離,三是常在一起做條件和返回列的不分離。
- 表字段控制少而精,可以提高IO效率,內存緩存更多有效數據,從而提高響應速度和並發能力,後續 alter table 也更快。
3.表的數據量控制在5000w以內
表字段數量不要超過20個,如果有需要建立主副表,主鍵一一關聯,避免單行數據過多以及修改記錄binlog ROW模式導致文件過大。
特別對於有一個text/blob或很大長度的varchar字段時,更應考慮單獨存儲。但也要註意查詢條件盡量放在一個表上。
字段規範
具體字段類型請參考
1.數字類型定義
- 對於整數的存儲,建議區分開 TINYINT / INT / BIGINT 的選擇,因為三者所占用的存儲空間也有很大的差別,能確定不會使用負數的字段,建議添加unsigned定義。
- 對於整型數值,mysql支持在類型名稱後面的小括號內指定顯示寬度,例如int(5)表示當數值寬度小於5位時候在數值前面填滿寬度,一般配合zerofill屬性使用。如果一個列指定為zerofill,則MySQL自動為該列添加unsigned屬性。bigint(20), int(11),一般不要隨便改動這個顯示寬度,c++裏面需要這個長度去截取字段
- 使用tinyint來代替 enum和boolean,tinyint使用1個字節,一般用於status,type,flag的列。ENUM類型在需要修改或增加枚舉值時,需要在線DDL,成本較高;ENUM列值如果含有數字類型,可能會引起默認值混淆
- 使用Decimal 代替float/double存儲精確浮點數。對於貨幣、金額這樣的類型,使用decimal,如 decimal(9,2)。
- 如果是固定精度的小數,也不建議使用DECIMAL,建議乘以固定倍數轉換成整數(如 BIG INT)存儲,可以大大節省存儲空間,且不會帶來任何附加維護成本
2.timestamp 初始值
- datetime 和 timestamp類型所占的存儲空間不同,前者5個字節(5.5是8字節),後者4個字節,這樣造成的後果是兩者能表示的時間範圍不同。優先使用timestamp,datetime也沒問題
- timestamp顯示與時區有關,內部總是以 UTC 毫秒 來存的。還受到嚴格模式的限制
- timestamp可以在insert/update行時,自動更新時間字段(如 f_set_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP),但一個表只能有一個這樣的定義。
- 默認時間,要麽current_timestamp,要麽’1970-01-02 01:01:01’,不要設置為 ‘‘ 或0
- 如果一定要使用int的型存儲時間戳,約定統一使用 int unsigned default 0
3. varchar 類型
- char定長,它會刪除字符串尾部的空格,varchar不會,varchar向前補1-2字節;。InnoDB建議使用varchar類型,不區分固定長度和可變長度。
- 把 BLOB或TEXT列分離到單獨的表中,它還使你在主數據表上運行 SELECT *查詢的時候不會通過網絡傳輸大量的BLOB或TEXT值
4.字段都使用 NOT NULL
- 如果是索引字段,一定要定義為not null 。因為null值會影響cordinate統計,影響優化器對索引的選擇
- 如果不能保證insert時一定有值過來,定義時使用default ‘‘,或 0
索引規範
1.任何新的select,update,delete上線,都要先explain,看索引使用情況
盡量避免extra列出現:Using File Sort,Using Temporary,rows超過1000的要謹慎上線。
explain解讀
type
:ALL, index, range, ref, eq_ref, const, system, NULL(從左到右,性能從差到好)possible_keys
:指出MySQL能使用哪個索引在表中找到記錄,查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用key
:表示MySQL實際決定使用的鍵(索引)
如果沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX
ref
:表示選擇key
列上的索引,哪些列或常量被用於查找索引列上的值rows
:根據表統計信息及索引選用情況,估算的找到所需的記錄所需要讀取的行數Extra
Using temporary
:表示MySQL需要使用臨時表來存儲結果集,常見於排序和分組查詢Using filesort
:MySQL中無法利用索引完成的排序操作稱為“文件排序”
2.避免冗余索引
InnoDB表是一棵索引組織表,主鍵是和數據放在一起的聚集索引,普通索引最終指向的是主鍵地址。不要在頻繁更新的列上創建索引。
3.前綴索引
對超過30個字符長度的列創建索引時,考慮使用,如 idx_cs_guid2 (f_cs_guid(26))表示截取前26個字符做索引,既可以提高查找效率,也可以節省空間
4.聯合索引
mysql使用聯合索引時,從左向右匹配,遇到斷開或者範圍查詢時,無法用到後續的索引列
比如索引idx_c1_c2_c3 (c1,c2,c3),相當於創建了(c1)、(c1,c2)、(c1,c2,c3)三個索引,where條件包含上面三種情況的字段比較則可以用到索引,但像 where c1=a and c3=c 只能用到c1列的索引,像 c2=b and c3=c等情況就完全用不到這個索引。遇到範圍查詢(>、<、between、like)也會停止索引匹配,比如 c1=a and c2 > 2 and c3=c,只有c1,c2列上的比較能用到索引
5.覆蓋索引
INNODB存儲引擎中,secondary index(非主鍵索引,又稱為輔助索引、二級索引)沒有直接存儲行地址,而是存儲主鍵值。
例如SELECT email,uid FROM user_email WHERE uid=xx,如果uid不是主鍵,適當時候可以將索引添加為index(uid,email),以獲得性能提升。如果用戶需要查詢secondary index中所不包含的數據列,則需要先通過secondary index查找到主鍵值,然後再通過主鍵查詢到其他數據列,因此需要查詢兩次。
SQL 設計
1.能確定返回結果只有一條時,使用 limit 1
在保證數據不會有誤的前提下,能確定結果集數量時,多使用limit,盡快的返回結果。
2.使用like模糊匹配,%不要放首位
會導致索引失效,有這種搜索需求是,考慮其它方案,如sphinx全文搜索
3.使用join時,where條件盡量使用充分利用同一表上的索引
- 如 select t1.a,t2.b * from t1,t2 and t1.a=t2.a and t1.b=123 and t2.c= 4 ,如果t1.c與t2.c字段相同,那麽t1上的索引(b,c)就只用到b了。此時如果把where條件中的t2.c=4改成t1.c=4,那麽可以用到完整的索引
- 這種情況可能會在字段冗余設計(反範式)時出現
- 正確選取inner join和left join。不允許濫用left join
4.考慮使用union all,少使用union,註意考慮去重
union all不去重,而少了排序操作,速度相對比union要快,如果沒有去重的需求,優先使用union all
5.分頁優化
大頁情況下不使用跳躍式分頁
假如有類似下面分頁語句:
SELECT FROM table1 ORDER BY ftime DESC LIMIT 10000,10; 這種分頁方式會導致大量的io,因為MySQL使用的是提前讀取策略。
推薦分頁方式:
SELECT FROM table1 WHERE ftime < last_time ORDER BY ftime DESC LIMIT 10 使用條件關系確定分頁頁數
6. count 計數
- 首先count()、count(1)、count(col1)是有區別的,count()表示整個結果集有多少條記錄,count(1)表示結果集裏以primary key統計數量,絕大多數情況下count()與count(1)效果一樣的,但count(col1)表示的是結果集裏 col1 列 NOT null 的記錄數。優先采用count()
- 大數據量count是消耗資源的操作,甚至會拖慢整個庫,查詢性能問題無法解決的,應從產品設計上進行重構。例如當頻繁需要count的查詢,考慮使用匯總表
- 遇到distinct的情況,group by方式可能效率更高。
7.減少與數據庫交互的次數,盡量采用批量SQL語句
INSERT ... ON DUPLICATE KEY UPDATE ...
,插入行後會導致在一個UNIQUE索引或PRIMARY KEY中出現重復值,則執行舊行UPDATE,如果不重復則直接插入,影響1行。REPLACE INTO
類似,但它是沖突時刪除舊行。INSERT IGNORE
相反,保留舊行,丟棄要插入的新行。- INSERT INTO VALUES(),(),(),合並插入。
8.杜絕危險SQL
- 去掉where 1=1 這樣無意義或恒真的條件,如果遇到update/delete或遭到sql註入就恐怖了
- SQL中不允許出現DDL語句。一般也不給予create/alter這類權限,但阿裏雲RDS只區分讀寫用戶
9.是否應該 order by 主鍵
許多排序的場景,如果主鍵id是增長的,如果 order by f_create_time 查詢慢,有可能使用了filesort,此時最簡單的辦法是看能否換成 order by id,因為id作為主鍵是遞增的,並且附帶在了每個二級索引後面。
但是也要謹慎使用 order by id,特別是在explain結果看到filesort的情況下,優化器極有可能放棄這個filesort,而選擇了它所認為更高效的掃描方式,實則更慢。
end
Mysql 數據庫開發規範