1. 程式人生 > >資料庫設計規範(MYSQL、WEB)

資料庫設計規範(MYSQL、WEB)

一、命名規範

1. 資料庫、表、欄位、別名規範

識別符 最大長度(位元組) 允許的字元
資料庫 64 [a-z_] (所有字元均小寫, 字之間用 _ 分割)
64 [a-z_] (所有字元均小寫, 字之間用 _ 分割)
64 [a-z_] (所有字元均小寫, 字之間用 _ 分割)
索引 64 [a-z_] (所有字元均小寫, 字之間用 _ 分割)
別名 255 [a-z_] (所有字元均小寫, 字之間用 _ 分割)

資料庫、表、列、索引、別名的命名應儘可能描述其真實的意思。

2. 統一命名

欄位

名稱空間為: 
資料庫::表(資料庫_表名):: 欄位(簡)

* 名稱或標題      name (char[])
 * 建立時間  create_time (datetime)
 * 更新時間  update_time (datetime)
 * 過期時間  expire_time (datetime)
 * 資料狀態  status (tinyint) ''0:正常 1:隱藏''
 * ID       id (int)
 * IP       ip (char[19])
 * 資原始檔/圖片Id   resource_id (int)
 * 標籤  tag  (char[])
 * 型別 type (tinyint)

索引 名稱空間為:

index_table_field
unique_table_field
key_table_field

3. 所有日誌表均以 log_ 開頭 如 :log_user_login

4. 各模組表以模組名開頭 如獎品:award award_exchange

5. 資料庫、表的備份,請使用資料庫、表加備份時間 如:

資料庫 '''camp camp_20091130'''
  表 '''award award_20091130'''

6. 對於與使用者表關聯的其它表,對於使用者表的關聯欄位,除非有特殊需要,請使用 username 關聯而不要使用 user_id 關聯。因為許多查詢中都使用 username,這樣可以避免不必要的查詢 (從 user_id 查得 username

二、設計規則

在設計過程中,應該從實際需求出發,以效能提升為根本目標來展開工作,很多時候為了儘可能提高效能,必須做反正規化設計。

1. 適度冗餘,讓查詢儘量減少 JOIN (MYSQL JOIN 效能不是很高)

2. 大欄位垂直分表

大欄位垂直分表簡單來說就是將自己身上的欄位拆分出去放到另外的表裡。
大欄位一般都是存放著一些較長的 Detail 資訊,如文章內容、帖子內容、產品的介紹等。
其次是和表中的其它欄位相比訪問頻率明顯要少很多。

3. 合適的資料型別

  1. 通過選用更 "小" 的資料型別減少儲存空間, 使查詢相同資料需要的IO資源降低.
  2. 通過合適的資料型別加速資料的比較.
  3. 選擇欄位時儘量不要選用 SET, EMNUM 型別, 不便於擴充套件.
  4. 除了像 TEXT, BLOB, AUTO_INCREMENT 等這些列不能指定預設值的列型別之外, 應儘量為每個欄位指定預設值. 這樣可以增強資料的移植性和減少嚴格模式下出錯的機會.
  5. 關聯欄位儘可能地建成相同列型別, 這樣可以加快表關聯搜尋.
  6. 儘可以為每列指定 NOT NULL, 除了在確實需要 NULL 值的情況下. 這樣可以減少儲存空間和索引優化.
  7. 如果一個表沒有像 text 這類欄位,一個表儘可能用 char 替代 varchar,因為固定長度的表有更高的查詢和恢復效能.
  8. 所有表、欄位均應用 comment 列屬性來描述此表、欄位所代表的真正含義,除了意思明瞭的欄位如: id

4. 適度的空間換時間

比如一個查詢頻繁的表,如果大部分字都時靜態長度的;可以全部都換成靜態的長度的(靜態表),以提高查詢效率。

注意:

CHAR[M] 屬於靜態長度型別, 存放長度完全以字元數來計算, 所以最終的儲存長度是基於字符集的, 如 latin1 其最大儲存長度為 255 位元組, 但是如果使用 gbk 則最大儲存長度為 510 (255x2) 位元組. CHAR 型別的儲存特點是不管實際存放的資料多長, 在資料庫中都會存放 M 個字元, 不夠通過空格補上, M 預設為1. 雖然 CHAR 會通過空格補齊存放空間, 但是在訪問資料時, MYSQL會忽略最的的所有空格, 所以如果實資料在最後確實需要空格, 則不能使用 CHAR 型別來存放. 在MYSQL 5.03 之前的版本中, 如果定義 CHAR時 M值超過 255, MYSQL 會自動將 CHAR 型別轉換為可以存入對應資料量的 TEXT型別, 如 CHAR(1000) 會自動轉換為 TEXT, CHAR(10000) 則會轉為 MEDIUMTEXT. 而從 MYSQL 5.0.3 開始, 所有超過 255 的定義 MYSQL 都會直接拒絕並給出錯誤資訊, 不再自動轉換.

VARCHAR[M] 屬於動態儲存長度型別, 僅儲存佔用實際儲存資料的長度. 其存放的最大長度與 MYSQL 版本有關, 在 5.0.3 之前的版本 VARCHAR 以字元數控制儲存的最大長度, 最大隻能存放 255 個字元, 佔用儲存空間的實際大小與字符集有關. 但是從 5.0.3 開始, VARCHAR 的最大儲存限制已經更改為位元組數限制了, 擴充套件到可以存放 65535 位元組的資料, 不同的字符集可能存放的字元數並不一樣. 也就是說, 在 MYSQL 5.0.3 之前的版本, M 所代表的是字元數, 而從 5.0.3 版本開始, M 代表位元組數了. VARCHAR 的儲存特點是不管設定 M 為多大值, 真正佔用的儲存空間只有存入的實際資料的大小, 和 CHAR 不同的是 VARCAHR 會保留存入資料最後的空格, 也就是說我們存入什麼, MYSQL 返回的就是什麼. 在 VARCHAR 型別欄位的資料中, MYSQL 會在每個 VARCHAR 資料中使用 1 到 2 個位元組來存放 VARCHAR 資料的實際長度, 當實際資料在 255 位元組之內時, 會使用 1 位元組來存放實際長度, 而大於 255 位元組時, 則需要使用 2 位元組來存放.

TINYTEXT, TEXT, MEDIUMTEXT 和 LONGTEXT 這 4 種類型同屬於一種儲存方式, 即動態儲存長度型別, 不同的僅是最大長度的限制. 4 種類型的定義都是通過最大字元數來限制, 但它們的字元數限制實際上是可以理解為位元組數限制, 因為當使用多位元組字符集時, 實際能存放的字元數並沒最大字元數那麼多, 而是以單位元組字元來計算的字元數. 此外, 由於是動態儲存長度型別, 所以和 VARCHAR 一樣, 每個欄位資料之前都需要一個存放實際長度的空間. TINYTEXT 需要 1 個位元組來存放, TEXT 需要 2 個位元組, MEDIUMTEXT 和 LONGTEXT 則分別需要 3 個和 4 個位元組來存放實際資料長度. 實際上, 除了 MYSQL 內嵌的最大長度限制之外, 它們還受到客戶端與伺服器端的網路通訊緩衝區最大值 (max_allowed_packet 預設為 1M, 也就是說, MEDIUMTEXT 和 LONGTEXT 在預設情況可能存不進去值) 的限制.

這 4 種 TEXT 型別和 CHAR 及 VARCHAR 在實際使用中存在幾個不一樣的地方:

1. 不能設定預設值.
2. 只有 TEXT 可以使用 TEXT[M] 這樣的方式通過 M 設定大小.
3. 基於這 4 種類型的索引必須指定字首長度.

TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分別佔用的位元組為 1, 2, 3, 4, 8. INT 型別的值就上億了.
對於 INT[M] 中 M 值的解釋: 以前我遇到很多人他們認為 INT(4), INT(10) 其取值範圍分別是 (-9999 到 9999), (-9999999999 到 9999999999). 這種理解是錯誤的. 其實對整型中的 M 值, 與 ZEROFILL 屬性結合使用時, 可以實現列值等寬. 不管 INT[M] 中 M 值是多少, 其取值範圍還是 (-2147483648 到 2147483647 有符號時), (0 到 4294967295 無符號時). 官方文件說明: 顯示寬度並不限制可以在列內儲存的值的範圍, 也不限制超過列的指定寬度的值的顯示. 當結合可選擴充套件屬性ZEROFILL使用時, 預設補充的空格用零代替. 例如: 對於宣告為INT(5) ZEROFILL的列, 值4檢索為00004. 請注意如果在整數列儲存超過顯示寬度的一個值, 當MySQL為複雜聯接生成臨時表時會遇到問題, 因為在這些情況下MySQL相信資料適合原列寬度.如果為一個數值列指定ZEROFILL, MySQL自動為該列新增UNSIGNED屬性.

三、建立索引

a. 較頻繁的作為查詢條件的欄位應該建立索引.

b. 唯一性太差的欄位不適合單獨建立索引, 即使頻繁作為查詢條件.

唯一性太差的欄位主要是指哪些呢? 如狀態欄位, 型別欄位等這些欄位中存放的資料可能總共就那麼幾個或幾十個值重複使用, 每個值都會存在於成千上萬或更多的記錄中. 對於這類欄位, 完全沒有必要建立單獨的索引. 因為即使建立了索引, MYSQL QUERY OPTIMIZER 大多數時候也不會去選擇使用, 如果什麼時候 MYSQL QUERY OPTIMIZER 選擇了這各索引, 那麼非常遺憾地告訴你, 這可能會帶來極大的效能問題. 由於索引欄位中每個值都會含有大量的記錄, 那麼儲存引擎在根據索引訪問資料的時候會帶來大量的隨機 IO, 甚至有些時候還會出現大量的重複 IO.

c. 更新頻繁的欄位不適合建立索引

索引中的欄位被更新的時候, 不僅要更新表中的資料, 還要更新索引資料, 以確保索引資訊是準確. 這個問題致使 IO 訪問量較大增加, 不僅僅影響了更新 Query 的響應時間, 還影響了整個儲存系統資源消耗, 加大了整個儲存系統負載.

d. 不會出現在 WHERE 子句中的欄位不該建立索引.

注意:
MYSQL 中索引的限制
1. MYISAM 儲存引擎索引長度的總和不能超過 1000 位元組.
2. BLOB 和 TEXT 型別的列只能建立字首索引.
3. MYSQL 目前不支援函式索引.
4. 使用不等於 (!= 或者 <>) 的時候, MYSQL 無法使用索引.
5. 過濾欄位使用函式運算 (如 abs (column)) 後, MYSQL無法使用索引.
6. Jion 語句 中 Jion 條件欄位型別不一致的時候, MYSQL無法使用索引.
7. 使用 LIKE 操作的時候如果條件以萬用字元開始 (如 '%abc...')時, MYSQL無法使用索引.
8. 使用非等值查詢的時候, MYSQL 無法使用 Hash 索引.

建表 SQL 語句示例:

CREATE TABLE `admin` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `role_id` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '所屬組ID',
  `username` char(16) NOT NULL COMMENT '使用者名稱',
  `password` char(33) NOT NULL COMMENT '密碼',
  `acl` text NOT NULL COMMENT '資源控制',
  `ctime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '建立時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='使用者表';