大佬都在用的數據庫設計規範!你不點進來看看嘛?

這是我參與更文挑戰的第29天,活動詳情查看:更文挑戰sql

建表規約

  • 表達是與否概念的字段,必須使用is_xxx命名,數據類型是unsigned tinyint(1-是,0-否)
    • 任何字段若是是非負數,必須是unsigned
    • POJO類中的任何布爾型變量,都不要加is前綴
    • 須要在< resultMap >設置從is_xxx到Xxx的映射關係
    • 數據庫表示是與否的值,使用tinyint類型
    • 堅持is_ xxx的命名方式是爲了明確取值含義和取值範圍
  • 表名,字段名必須使用小寫字母(或數字),禁止出現數字開頭,禁止兩個下劃線中間只出現數字.數據庫字段名的修改代價很大,由於沒法進行預發佈,因此字段名稱須要慎重考慮
    • MySQL在windows下不區分大小寫,但在Linux下默認是區分大小寫的
    • 所以,數據庫名,表名,字段名,都不容許出現任何大寫字母
  • 表名不使用複數名詞
    • 表名應該僅僅表示表裏面的實體內容,不該該表示實體數量
    • 對於DAO類名也是單數形式,符合表達習慣
  • 禁止使用MySQL的官方保留字命名:
    • desc
    • range
    • match
    • delayed
  • 索引命名:
    • pk_字段名: 主鍵primary key索引
    • uk_字段名: 惟一unique key索引名
    • idx_字段名: 普通index索引名
  • 小數類型爲decimal, 禁止使用float,double
    • float和double在存儲的時候,存在精度損失的問題,極可能在值比較時,獲得不正確的結果
    • 若是存儲的數據範圍超過decimal的範圍,建議將數據拆分紅整數和小數分開存儲
  • 若是存儲的字符串長度幾乎相等,使用char定長字符串類型
  • varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000
    • 若是長度大於此值,定義字符串類型爲text, 獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率
  • 表必備的三個字段:
    • id: 主鍵,類型爲bigint,unsigned,單表時自增,步長爲1
    • gmt_create: 類型爲datetime,如今時表示主動建立
    • gmt_modified 類型爲datetime,過去分詞表示被動更新
  • 表的命名最好加上[業務名稱_表的做用]
  • 庫名與應用名稱儘可能一致
  • 若是修改字段含義或者對字段的表示狀態追加時,須要及時更新字段註釋
  • 字段容許適當冗餘以提升查詢性能,但必須考慮數據一致.冗餘的字段應遵循:
    • 不是頻繁修改的字段
    • 不是varchar超長字段,更不能是text字段
      • 商品類目名稱使用頻率高,字段長度短,名稱基本一成不變,可在相關聯的表中冗餘存儲類目名稱,避免關聯查詢
  • 單錶行數超過500萬行或者單表容量超過2GB, 才推薦進行分庫分表
    • 若是預計三年後的數據量根本達不到這個級別,不要在建立表時就分庫分表
  • 合適的字符存儲長度,不但節約數據庫表空間,節約索引存儲,更重要的是提高檢索速度

索引規約

  • 業務上具備惟一特性的字段,即便是多個字段的組合,也必須建成惟一索引
    • 索引不會影響insert的速度,這個速度能夠忽略,但提升查找速度是明顯的
    • 即便在應用層作了很是完善的校驗控制,只要沒有惟一索引,必然有髒數據產生
  • 超過三個表禁止join, 須要join的字段 ,數據類型必須絕對一致. 多表關聯查詢時,保證被關聯的字段須要有索引
  • 在varchar字段上創建索引時,必須指定索引長度,不必對全字段創建索引,根據實際文本區分度決定索引長度便可
    • 索引長度與區分度是一對矛盾體
      • 通常對字符串類型數據,長度爲20的索引,區分度會高達90%以上
      • 可使用count(distinct left(列名, 索引長度)) / count(*) 的區分度來肯定
  • 頁面搜索嚴禁左模糊或者全模糊,若是須要要使用搜索引擎來解決
    • 索引文件具備B-Tree的最左前綴匹配特性,若是左邊的值未肯定,沒法使用此索引
  • 若是有order by的場景,要注意利用索引的有序性 .order by最後的字段是組合索引的一部分,而且放在索引組合順序的最後,避免出現file_sort的狀況,影響查詢性能
where a=? and b=? order by c;
索引: a_b_c
複製代碼

要是在索引中有範圍查找,那麼索引有序性就沒法利用(WHERE a>10 ORDER BY b; 索引:a_b沒法排序)數據庫

  • 利用覆蓋索引來進行查詢操做,避免回表
    • 好比一本書須要知道第11章是什麼標題,只須要目錄瀏覽一下就更好,這個目錄就起到覆蓋索引的做用
    • 可以創建索引的種類分爲主鍵索引,惟一索引,普通索引三種,而覆蓋索引只是一種查詢的效果
    • explain的結果,extra列會出現: using index
  • 利用延遲關聯或者子查詢優化超多分頁場景:
    • MySQL不是跳過offset行,而是取offset+N行,而後返回放棄前offset行,返回N行
    • 當offset特別大的時候,效率就很是低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行SQL改寫
    • 先快速定位須要獲取的id字段,而後再關聯:
SELECT a.* FROM table1 a,(select id from table1 where condition LIMIT 100000,20) b where a.id=b.id
複製代碼
  • SQL性能優化的目標: 至少要達到range級別,要求是ref級別,最好是consts級別
    • consts: 單表中最多隻有一個匹配行(主鍵或者惟一索引),在優化階段便可讀取到數據
    • ref: 指的是使用普通的索引(normal index)
    • range: 指對索引進行範圍檢索
      • explain表的結果,type=index,索引物理文件全掃描,速度很是慢
      • 這個index級別比range還低,但比全表掃描要好的多
  • 創建組合索引的時候,區分度最高的在最左邊
    • 若是 where a=? and b=?;若是a列幾乎接近於惟一值,只須要單建idx_a索引便可
    • 存在非等號和等號混合時,在創建索引時,等號條件列前置
      • 好比 where c>? and d=?; 即便c的區分度更高,也必需要將d放在索引的最前列,即索引idx_d_c
  • 要注意防止由於字段類型不一樣形成隱式轉換,致使索引失效
  • 建立索引有如下錯誤的觀點:
    • 認爲一個查詢就須要建一個索引
    • 認爲索引會消耗空間,嚴重拖慢更新和新增速度
    • 抵制惟一索引,認爲業務的惟一性須要在應用層經過"先查後插"的方式解決

SQL語句規約

  • 不要使用count(列名)count(常量) 來代替count(*), count(*) 是SQL92定義的標準統計行數的方法 ,跟數據庫無關,跟NULL和非NULL無關
    • count(*) 會統計只爲NULL的行
  • count(distinct col) 計算該列出NULL以外的不重複行數,注意 count(distinct col1, col2) 若是其中一列全爲NULL, 那麼即便另外一列有不一樣的值,也返回0
  • 當某一列的值全是NULL時, count(NULL)的返回結果爲0,但sum(col)返回結果爲NULL, 所以使用sum要注意NPE問題
    • 使用如下方式來規避sum的NPE問題:
SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM TABLE;
複製代碼
  • 使用ISNULL來判斷是否爲NULL
    • NULL與任何值的直接比較都爲NULL:
      • NULL<>NULL的返回結果是NULL,而不是false
      • NULL==NULL的返回結果是NULL,而不是true
      • NULL<>1的返回結果是NULL,而不是true
  • 在代碼中寫分頁邏輯時,若count爲0應直接返回,避免執行後面的分頁語句
  • 不得使用外鍵與級聯,一切外間的概念必須在應用層解決
    • 好比學生和成績的關係:
      • 學生表中的student_id是主鍵,那麼成績表中的student_id則爲外鍵
      • 若是更新學生表中的student_id,同時觸發成績表中的student_id更新,即爲級聯更新
    • 外鍵與級聯更新適用於單機低併發,不適合分佈式,高併發集羣
    • 級聯更新是強阻塞,存在數據庫更新風暴的風險
    • 外鍵影響數據庫的插入速度
  • 禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性
  • 數據訂正(數據刪除,修改記錄操做)時,要先select, 避免出現誤刪除,確認無誤才能執行更新語句
  • in操做能避免就避免,若實在避免不了,須要仔細評估in後面集合元素數量,控制在1000個以內
  • 若是有國際化須要,全部的字符存儲與表示,都要以UTF-8編碼
  • TRUNCATE TABLEDELETE速度快,且使用的系統和事務日誌資源少,但TRUNCATE無事務且不觸發trigger, 有可能形成事故,因此不要使用TRUNCATE語句

ORM映射規約

  • 在表查詢中,一概不要使用 * 做爲查詢字段列表,須要哪些字段必須明確寫明
    • 增長查詢分析器的解析成本
    • 增減字段容易與resultMap配置不一致
    • 無用字段增長網絡消耗,尤爲是text類型字段
  • POJO類的布爾屬性不能加is, 而數據庫字段必須加is_, 要求在resultMap中進行字段與屬性之間的映射
    • 定義POJO類以及數據庫字段定義規定,在中增長映射,是必須的
    • 在MyBatis Generator生成的代碼中,須要進行對於的修改
  • 不要使用resultClass當返回參數,即便全部類屬性名與數據庫字段一一對應,也須要定義,每個表必定有一個POJO類對應
    • 配置映射關係,使字段與DAO類解耦,方面維護
  • Sql.xml配置參數使用 #{ } 或者 #param#. 不容許使用 ${ }, 這種方式容易出現SQL注入
  • 不要使用iBATIS自帶的queryForList(String statementName, int start, int size)
    • 這個方法的實現方式是在數據庫取到statementName對應的SQL語句的全部記錄,再經過subList取start,size的子集合
  • 不容許直接使用HashMap與HashTable做爲查詢結果集的輸出
    • resultClass="HashTable",會置入字段名和屬性值,可是值的類型不可控
  • 更新數據表記錄時,必須同時更新記錄對應的gmt_modified字段值爲當前時間
  • 不要寫一個大而全的數據更新接口:
    • 不要傳入一個POJO類進行更新
    • 執行SQL時,不要更新無改動的字段.一是易出錯,二是效率低,三是增長binlog存儲
  • @Transactional事務不要濫用:
    • 事務會影響數據庫的QPS
    • 使用事務須要考慮各方面的回滾方案,包括緩存回滾,搜索引擎回滾,消息補償,統計修正
  • < isEqual > 中的compareValue是與屬性值對比的常量,通常是數字,表示相等時帶上此條件
  • < isNotEmpty > 表示不爲空且不爲null時執行
  • < isNotNull > 表示不爲null時執行
相關文章
相關標籤/搜索