MYSQL數據庫設計規範與原則

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,化繁爲簡
    • 一條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;

這三種方法最先考慮第一種 其次第二種,第三種是別無選擇

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章