1.數據庫中的庫名,表名和字段名的命名規則
採用26個英文字母(區分大小寫)和0-9的自然數(經常不需要)加上下劃線'_'組成;
命名簡潔明確(長度不能超過30個字符);
例如:user, stat, log, 也可以wifi_user, wifi_stat, wifi_log給數據庫加個前綴;
每個表中必須有自增主鍵,create_time(系統時間)
表與表之間的相關聯字段名稱要求儘可能的相同;
2.數據庫表字段類型規範
用盡量少的存儲空間來存數一個字段的數據;
例如:能使用int就不要使用varchar、char,能用varchar(16)就不要使用varchar(256);
IP地址最好使用int類型;
固定長度的類型最好使用char,例如:郵編;
能使用tinyint就不要使用smallint,int;
最好給每個字段一個默認值,最好不能爲null;
- 數據庫字符集默認使用utf8,如果存儲emoji表情等四字節使用utf8mb4字符集
- 禁止在線上生產環境做數據庫壓力測試
- 禁止從測試、開發環境、本機直連線上生產數據庫
- 禁止在數據庫中存儲明文密碼
- 禁止在數據庫中存儲圖片、文件等大數據
- 禁止將業務日誌實時保存到數據庫,建議保存到日誌文件,對於統計後的結果再存放到mysql中
- 禁止線上核心業務使用mysql存儲過程、視圖、觸發器、Event、InnoDB外鍵約束等,這些容易將業務邏輯和db耦合在一起,而且在MySQL的這些特性中存在嚴重BUG
- 業務部門的推廣活動,請提前通知dba進行服務和訪問評估。
- 庫名、表名、字段名必須使小寫字母,並採用下劃線分割;對相關功能的表應當使用相同前綴,如job_xxx,前綴通常爲庫名或依賴主實體對象:
數據庫名稱約定:dbgm_xxx dbgj_xxx dbchr_xxx - 數據庫表的所有引擎默認都是InnoDB,新業務不支持MyISAM引擎
- 所有的表及字段都必須有備註,詳細說明表及字段的含義
- 涉及貨幣金額或其他精度敏感的數據必須使用定點數DECIMAL替代FLOAT和DOUBLE
- 庫名、表名、字段名禁止使用MySQL保留字,如date、like、desc、return等
- 控制表字段數,單表不超過50個純INT/20個VARCHAR(10)字段等同存儲體積的字段數,上限控制在20~50
- 字段長度只分配真正需要的空間
問題:使用VARCHAR(5) 和VARCHAR(200) 存儲’hello’的磁盤空間開銷是一樣的,使用更短的列有什麼優勢嗎?
更大的定義列會消耗更多的內存,因爲MySQL通常會分配固定大小的內存塊來保存內部值,尤其是使用內存臨時表進行排序或操作時會特別糟糕
- 索引名稱必須使用小寫,普通索引按照“idx_字段名_字段名[_字段名]”進行命名,唯一索引按照“uniq_字段名_字段名[_字段名]”進行命名”
- 表必須有主鍵,推薦使用獨立於業務的AUTO_INCREMENT列或全局ID生成器做主鍵,禁止使用多字段做聯合主鍵
- 不使用UUID/MD5/HASH等函數生成的無規則值做主鍵,效率極差
- 索引數量控制
- 單張表中索引數量不超過5個
- 單個索引中的字段數不超過5個
- 對字符串使用前綴索引,前綴索引長度不超過10個字符
- 索引字段的順序需要考慮每個字段去重之後的數量,區分度最大的【個數最多的】放在前面。
- 合理創建聯合索引(避免冗餘),符合最左前綴原則:(a,b,c) 相當於 (a) 、(a,b) 、(a,b,c)
- 可能需要添加索引的字段:
- ORDER BY,GROUP BY,DISTINCT的字段需要添加在索引的後面
- UPDATE、DELETE語句需要根據WHERE條件添加索引
- 對於JOIN操作,需要在JOIN字段上建立索引
- 線上慎用FORCE INDEX,使用前需要和DBA溝通,並得到DBA的測試允許
- 線上OLTP系統中禁止使用外鍵,高併發時極易引起死鎖等問題
- 索引使用禁忌
- 不使用%前導的查詢,如like “%ab”
- 不使用負向查詢,如not in/not like/<>
- 不在低區分度的列上建立索引,例如“性別”
- 不在索引列進行數學運算和函數運算
示例:假設在表tab中id建立了索引- Select col_A,col_B from tab where id + 1 > 10001 不會使用索引
- Select col_A,col_B from tab where id > 10001 – 1 會使用索引
- 線上儘量少使用大SQL,可能一條大SQL就把整個數據庫堵死,將複雜SQL拆分爲多條簡單SQL,化繁爲簡
- 一條SQL只能在一個CPU上運算,如果SQL比較複雜執行效率會非常低
- 簡單SQL緩存命中率更高
- 減少鎖表時間
- 充分利用多核CPU,提高併發效率
- 減少MySQL端的數學運算和邏輯判斷,避免SQL語句出現md5()、order by rand()等
- 儘量少用SELECT * ,只取需要的數據列, 避免無謂的IO、CPU和網絡開銷
- WHERE條件中,同一字段改寫OR爲IN(),IN包含的值不應過多,默認不超過200個,IN裏禁止使用子查詢
- 過濾表記錄合併且不去重的情況,改寫UNION爲UNION ALL
- 減少使用拼接SQL,使用預編譯語句,降低SQL注入概率
- WHERE條件中的非等值條件(IN、BETWEEN、<、<=、>、>=)會導致使用不了聯合索引的後續字段,注意避免
- WHERE條件比較,字段類型和傳入值必須保證類型一致,避免隱式轉換
示例:
字段: code varchar(50) NOT NULL COMENT ‘編碼’ #code上建立了索引
SELECT id,name,addr from tab_name where code=10001; 不會使用索引
SELECT id,name,addr from tab_name where code='10001'; 會使用索引 - Limit分頁優化
- 假設有一個千萬量級的表,取1到10條數據;
select * from table limit 0,10; select * from table limit 1000,10;
這兩條語句查詢時間應該在毫秒級完成;
select * from table limit 3000000,10;
你可能沒想到,這條語句執行之間在5s左右;
爲什麼相差這麼大?
可能mysql並沒有你想的那麼智能,比如你要查詢 300w開始後面10條數據;mysql會讀取300w加10條這麼多的數據,只不過 過濾後返回最後10條而已!!!
那麼如果解決這個問題呢;這裏總結三種常用方法;
第一種簡單粗暴,就是不允許查看這麼靠後的數據,比如百度就是這樣的
最多翻到76頁就不讓你翻了,這種方式就是從業務上解決;
第二種方法,在查詢下一頁時把上一頁的行id作爲參數傳遞給客戶端程序,然後sql就改成了
select * from table where id>3000000 limit 10;
這條語句執行也是在毫秒級完成的,id>300w其實就是讓mysql直接跳到這裏了,不用依次在掃描全面所有的行
如果你的table的主鍵id是自增的,並且中間沒有刪除和斷點,那麼還有一種方式,比如100頁的10條數據
select * from table where id>100*10 limit 10;
最後第三種方法:延遲關聯
我們在來分析一下這條語句爲什麼慢,慢在哪裏。
select * from table limit 3000000,10;
玄機就處在這個 * 裏面,這個表除了id主鍵肯定還有其他字段 比如 name age 之類的,因爲select * 所以mysql在沿着id主鍵走的時候要回行拿數據,走一下拿一下數據;
如果把語句改成
select id from table limit 3000000,10;
你會發現時間縮短了一半;然後我們在拿id分別去取10條數據就行了;
語句就改成這樣了:
select table.* from table inner join ( select id from table limit 3000000,10 ) as tmp on tmp.id=table.id;
這三種方法最先考慮第一種 其次第二種,第三種是別無選擇