MySQL5.7資料庫優化之表設計
一個數據庫、表設計的優劣會影響到資料庫的效能,所以合理的設計資料庫是非常重要的。
最近看了MySQL5.7手冊,手冊第八章就是關於優化的,第十一章詳細的介紹了各個欄位。如果你有興趣可以去看看,相信會收穫頗豐。下面根據手冊及結合平時開發經驗還有大學學的資料庫原理來談談一些自己的見解。由於水平有限,難免會有錯誤及疏漏的地方,歡迎指正。
一、資料庫的建立
建立資料庫非常簡單,只不過,建議給資料庫指定預設的字符集和排序規則。雖然表、欄位均可指定字符集和排序規則。
字符集一般選utf8或者utf8mb4,utf8mb4是utf8的超集,相容四個位元組。utf8是utf8mb3的別名,最大支援三個位元組,所以儲存四個位元組的字元時會出錯,例如單字元表情佔四個位元組,使用utf8儲存就會出錯。
指定欄位長度時,在絕大多數情況下,utf8與utf8mb4是一樣的,但有一個需要注意,那就是定長型別(char)。char型別會預先分配儲存空間,對於ut8是按照三個位元組計算的,對於utf8mb4是按照四個位元組分配的,所以utf8mb4會比ut8多佔儲存空間。根據業務特點選擇是的字元,這裡推薦如下:
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8mb4 COLLATE=utf8mb4_general_ci;
二、表的設計
1、儲存引擎的選擇
我們可以使用SHOW ENGINES
來檢視所支援的儲存引擎。
一般常見的有InnoDB、MyISAM、MEMORY、Archive。用的最多的是InnoDB、MyISAM。Archive 是存檔型儲存引擎,提供無阻塞插入和查詢,但是也只能做插入和查詢操作,MEMORY是記憶體型儲存引擎,資料儲存於記憶體中,常用於快取。
此版本對InnoDB做了大量的優化、增加了很多特性,比如開始支援全文索引(fulltext)等。只不過mysql自帶的全文索引不支援中文分詞,中文分詞可以使用coreseek。coreseek是在sphinx的基礎上,增加了中文分詞功能,所以可以用來做中文的全文索引。直接編譯進mysql中效能更強。
下面說說Innodb和MyISAM這兩個儲存引擎的幾個最重要的區別(只有瞭解了區別,才知道如何選擇合適的儲存引擎
):
1)儲存方式不同
MyISAM資料、索引、結構分別存在不同的檔案中(.MYD
,.MYI
,.frm
);
InnoDB預設資料和索引儲存在同一個表空間檔案中(.ibd
),結構儲存在另一個檔案中(.MYI
SET global innodb_file_per_table=1;
使資料和索引儲存在不同的表空間檔案中。如果一張大表需要很多索引,這能起到很不錯的優化效果。 對於小表就無所謂了。
2)鎖的粒度不同
MyISAM只支援表級鎖
InnoBD既支援表級鎖,還支援行級鎖
這也就是為什麼InnoBD併發寫(修改、刪除)操作效能更好
3)InnoDB支援事務、外來鍵約束等
4)索引實現方式不同
為了減少一次性磁碟IO開銷,可以讀取到更多的關鍵字數量,MyISAM和InnoDB均採用B+樹作為索引結構,但是實現的方式卻有不同。InnoDB採用的是聚簇索引,即主鍵的葉子節點儲存了完整的資料記錄。
下面舉例說明這兩者的差異:
假設我們要儲存以下資料:
id | name | age |
---|---|---|
1 | zhaoyi | 13 |
3 | sunsan | 14 |
2 | qianer | 15 |
4 | lisi | 13 |
5 | zhouwu | 11 |
6 | wuliu | 10 |
7 | zhengqi | 21 |
8 | wangba | 15 |
9 | fengjiu | 17 |
10 | chenshi | 16 |
11 | chushiyi | 13 |
12 | weishier | 14 |
假設用MyISAM儲存引擎儲存,那麼儲存方式如下圖
由上圖可以看出,主鍵索引的葉子節點儲存的是資料的地址,資料並不是按主鍵順序儲存的。 所以查詢資料時,如果不指定排序,則會按儲存的順序檢索出來,所以你會發現id=3卻在id=2後面。
由於主鍵儲存的時資料的地址,所以查詢非常快,這也就是MyISAM適合高速讀的原因
又由於插入時資料不需要排序,所以插入速度也非常快,這就是MyISAM適合高速併發插入的原因
假設用InnoDB儲存引擎儲存,那麼儲存方式如下圖
由上圖可以看出,主鍵索引是聚簇索引,葉子節點儲存的是資料。
每次插入資料都會根據主鍵id進行排序插入,這也就是為什麼InnoDB不適合高速併發插入的原因。 也是InnoDB表必須有一個主鍵的原因,而主鍵往往推薦使用自增整型,避免使用字元型別(char、varchar),因為整型排序更快,自增插入會加快插入的速度。
對於二級索引,MyISAM和Innodb實現也是不同的,雖然都是B+樹,但是MyISAM的葉子節點儲存的是資料的地址,與主鍵的實現方式是一致的。而InnoDB的二級索引的葉子節點儲存的是主鍵id。
假設我們在name欄位建立一個普通索引,那麼MyISAM和InnoDB的實現方式分別如下:
由上圖可以看出,InnoDB二級索引葉子節點儲存的是主鍵id,這也間接的表明對於InnoDB,每個二級索引都有一個主鍵欄位隱藏在索引中,當查詢索引+id時會走索引覆蓋,不需要去查表記錄。
即當我們需要查詢id和name時,使用SELECT id,name FROM student
時,只會走索引,不會去查表記錄,速度極快。
綜上所述:
當表只需要高速的查詢和插入時,可以選擇MyISAM儲存引擎,
當表需要併發的修改和刪除、事務外來鍵約束等保持資料完整性的要求時,可以選擇InnoDB儲存引擎。
需要注意的是,MyISAM雖然高併發的插入效能非常優越,但是這是建立在表沒有太多空行的情況下,如果表有過多的刪除動作,就會造成很多空行,使併發插入效能急劇下降。
另外主鍵建議使用整型自增。
2、分割槽分表
分割槽:
對於大型表,比如訂單表,建議使用分割槽來增強效能。
使用Partition by 分割槽演算法 分割槽選項
來對錶進行分割槽。
mysql提供了四種分割槽演算法,分別是Key,hash,List,range。各有優缺點,一般常用的是key演算法,即求模演算法。
分表:
對於大型表,儘量做到“動靜分離”,即把不常改動的欄位放一張表裡,把經常改動的欄位放另一種擴充套件表裡,以此來提高效能。
3、在BCNF的基礎上適當做一些冗餘。
適當的冗餘可以減少聯表操作,提高查詢效能。當然,並不是所有的欄位都適合做冗餘,要根據業務特點,選擇極少變更但又經常用到的欄位 來做冗餘。
4、欄位型別的選擇
關於欄位型別的選擇,有幾個原則:
- 欄位要儘可能的使用整型
- 欄位要儘可能的小
- Innodb引擎表字段要儘可能的使用變長(雖然定長檢索比變長要快,但會預先分配較多的儲存空間,MySQL手冊也推薦使用變長,因為MySQL優化了Innodb的varchar的儲存)
- 欄位要儘可能宣告為
NOT NULL
,儘量指定一個預設值,比如狀態欄位status TINYINT NOT NULL DEFAULT '0' COMMENT...
,因為NULL
是一個特殊型別,是佔用儲存空間的,並且會使索引查詢變得複雜,特別是聯表查詢,所以外來鍵(邏輯外來鍵,一般情況下不要使用MySQL宣告為外來鍵,減少資料庫的開銷)欄位一定要宣告為NOT NULL
,並且儘可能的為整型
MySQL支援多種資料型別,有數字型別、日期和時間型別 、字串型別、空間資料型別、JSON型別。下面一一介紹各型別及推薦使用:
1、數字型別:
數字型別的使用原則是:
- 儘可能的使用範圍小的型別 ,能使用TINYINT就絕不使用SMALLINT,比如狀態欄位,一般狀態不會太多,使用TINYINT就足夠了,但見過很多人使用INT(1)來作為狀態的欄位型別,但 INT(1) 和 INT 實質是沒有區別的,TINYINT 佔一個位元組,INT 佔四個位元組,所以會浪費大量儲存空間。
- 能指定為無符號(UNSIGNED)儘量指定為無符號,比如自增整型主鍵id,一般不會為負數,所以應該指定UNSIGNED來增加正整數的範圍,否則會浪費符號位。最典型的主鍵欄位設計
id INT UNSIGNED NOT NULL AUTO_INCREMENT
。
下面詳細介紹各個數字型別:
1)整數型別:TINYINT、SMALLINT、MEDIUMINT、INT (INTEGERd的同義詞)、BIGINT
下面給出這五種型別的有符號和無符號數的範圍,可以根據實際情況選擇使用。
型別 | 儲存(位元組) | 有符號最小值 | 有符號最大值 | 無符號最小值 | 無符號最大值 |
---|---|---|---|---|---|
TINYINT | 1 | -128 | 127 | 0 | 255 |
SMALLINT | 2 | -32768 | 32767 | 0 | 65535 |
TINYINT | 3 | -8388608 | 8388607 | 0 | 16777215 |
MEDIUMINT | 4 | -2147483648 | 2147483647 | 0 | 4294967295 |
BIGINT | 8 | -263 | 263 - 1 | 0 | 264 - 1 |
2)定點型別:DECIMAL、NUMERIC
當你需要儲存精確到小數點後幾位的數值時,就可以用它們來宣告。
DECIMAL的標準語法時DECIMAL(M,N),當你只宣告DECIMAL而省略M和N時。預設M為10,N為0。M表示數字個數(不包含負號和小數點),N表示小數點位數。比如你宣告DECIMAL(5,2),它表示的範圍是-999.99到999.99。
3)浮點型別:FLOAT、DOUBLE
FLOAT是單精度,佔四個位元組。DOUBLE是雙精度,佔八個位元組。由於精度問題,一般情況下極少被用到。
4)位元值型別:BIT
用來儲存二進位制數的,用到的頻率也比較低
小結:數字型別用的最多的是TINYINT、SMALLINT、MEDIUMINT、INT和DECIMAL,實際設計時,根據欄位的範圍來選擇合適的型別。
2、日期和時間型別:
日期和時間型別主要有DATE、 TIME、 DATETIME、 TIMESTAMP 和 YEAR這幾種。
DATE一般格式為:xxxx-xx-xx
TIME一般格式為:xx:xx:xx
DATETIME一般格式為:xxxx-xx-xx xx:xx:xx
TIMESTAMP 一般格式為:xxxx-xx-xx xx:xx:xx
YEAR一般格式為:xxxx
MySQL預設這些型別時不能儲存“零值”的。比如0000-00-00
,如果你想儲存這樣值,需要禁用NO_ZERO_DATE
模式。
對於DATE、TIME、YEAR這三種沒什麼特別強調的
下面說說DATETIME和TIMESTAMP的區別:
相同點:
1)預設的格式都是xxxx-xx-xx xx:xx:xx
2)都可以指定位數。
DATETIME和TIMESTAMP的完整寫法是DATETIME(M)和TIMESTAMP(M),
M的範圍是0~6的整數,預設值是0。比如你聲明瞭DATETIME(1),則日期格式為xxxx-xx-xx xx:xx:xx.x
。最高可精確到微秒。不過一般宣告DATETIME(即DATETIME(0)
)即能滿足日常需求。
注:TIME也可以指定位數,TIME(M)
不同點:
1)所佔儲存空間不同
DATETIME佔8個位元組,TIMESTAMP佔4個位元組。所以DATETIME比TIMESTAMP佔更多的儲存空間。
2)所表示的時間範圍不同
由於DATETIME佔更多的位元組,所以DATETIME比TIMESTAMP表示的時間範圍要大的多。
DATETIME能表示的時間範圍(NO_ZERO_DATE
模式下)為:'1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999
TIMESTAMP能表示的時間範圍(NO_ZERO_DATE
模式下)為:'1970-01-01 00:00:01.000000' 到 '2038-01-19 03:14:07.999999'
3)檢索速度不同
TIMESTAMP要比DATETIME檢索速度快
小結:根據需求以及考慮以後可能的業務擴充套件,選擇合適的型別。
另外,時間建議不要使用int存時間戳,雖然整型查詢速度很快,但是整型的時間戳不直觀,不利於排查問題
3、字串型別:
字串主要的型別有字串型別CHAR, VARCHAR, BINARY, VARBINARY, BLOB, TEXT, ENUM,和 SET。
1)CHAR和VARCHAR型別
char(M)表示儲存長度為M的字串,M表示字串的長度,最多可儲存255個字元。
如果一張表指定字符集是utf8(utf8mb3、三個位元組),宣告一個欄位為char(4)
,那麼MySQL就會預先分配 4 * 3個位元組,哪怕你只儲存了'abcd'
四個單位元組字元,也要佔用4 * 3個位元組 = 12個位元組的空間。如果你儲存的是你好ab
這幾個字元,所佔儲存空間也是 4 * 3個位元組,總共12個位元組的儲存空間。
如果你想儲存一個字元a
,MySQL也會在後面補充三個空格,組成四個字元存入,查詢出來時,會自動去除後面的空格,所以你如果想存入'a '
a和空格,查詢出來也只有a,空格被去除了。
所以char(M)所佔儲存空間是M*(字符集最大編碼長度)。
varchar(M)表示儲存最大長度為M的字串,M表示字串的長度。與char不同的是,M值只受最大行長度影響(MySQL規定一張表所有欄位長度加一起,最大長度不能超過65535位元組),所以理論上M的最大值是65533(對於支援多位元組字符集,此值有變動,如utf8,還需要除以3 = 21844),因為還需要兩個位元組儲存長度值。
當M小於等於255時,需要額外的一個位元組來儲存長度。
當M大於255時,需要額外的兩個位元組來儲存長度。
舉個例子:
如果一張表指定字符集是utf8(utf8mb3、三個位元組),宣告一個欄位為varchar(4)
,如果你儲存的是四個單位元組字元,如abcd
,那麼所佔空間是4 * 1個位元組 + 1個位元組(儲存長度值用的),總共佔五個位元組儲存空間。如果你儲存的是你好
這兩個中文,那麼所佔儲存空間就是2 * 3個位元組 + 1個位元組,總共佔7個位元組儲存空間。
對比char的例子就可以發現,對於支援多位元組的字符集,varchar比char更省儲存空間。
如果一張表指定字符集是utf8(utf8mb3、三個位元組),宣告一個欄位為varchar(256)
,超過了255,所以需要兩個額外的位元組儲存長度值。如果你儲存的是四個單位元組字元,如abcd
,那麼所佔空間是4 * 1個位元組 + 2個位元組(儲存長度值用的),總共佔6個位元組儲存空間。如果你儲存的是你好
這兩個中文,那麼所佔儲存空間就是2 * 3個位元組 + 2個位元組,總共佔8個位元組儲存空間。
同時,MySQL5.7對Innodb儲存引擎的varchar儲存格式做了優化,這也就是為什麼推薦優先使用varchar的原因。
還有,要遵從大的原則,長度值M儘可能的小,儘量宣告為NOT NULL DEFAULT ''
雖然varcahr型別儲存時是按實際字串大小儲存的,但是在使用到臨時表時,MySQL還是會按照設定的M值來分配空間,所以M值還是越小越好,可以節省空間。這裡的空間有可能是記憶體空間、也有可能是檔案空間。當臨時表比較小時(設定值),臨時表會在記憶體中生成,當臨時表比較大時,臨時表會在檔案中生成。所以M值越小,所分配的空間也就越小。
2)BINARY和VARBINARY型別
這兩個其實與char和varchar相似,只不過這兩個儲存的是字串的二進位制值
3)BLOB和TEXT型別
這兩個平時見的最多的就是TEXT了,BLOB是儲存二進位制值的,主要儲存二進位制的檔案等。
BLOB 型別有四種TINYBLOB,BLOB, MEDIUMBLOB,和LONGBLOB,儲存值的長度依次增大,由於平時基本不使用,這裡就不做過多介紹。
下面介紹TEXT的四種儲存型別: TINYTEXT,TEXT, MEDIUMTEXT,和LONGTEXT。
TINYTEXT最多可儲存28 = 256個字串。
TEXT最多可儲存216 = 65535個字串。
MEDIUMTEXT最多可儲存224 = 16777216個字串。
LONGTEXT最多可儲存232個字串。
平時使用最多的就是TEXT,不過還是需要根據實際情況來選擇。
注意:
在設計表的時候要把TEXT型別欄位拆出來單獨存在一張表中
4)ENUM型別
列舉型別用的較少,除非特殊情況,不然一般使用TINYINT代替。比如狀態、型別這些欄位,如果使用列舉,後期有新需求就需要改動欄位。但是你的列舉值如果是字串,那最好還是用列舉型別。如果是整型值,還是建議使用整型系列型別。
5)SET型別
集合型別,集合中成員使用,
(逗號)隔開。只不過對集合資料操作起來不方便,需要使用特定函式進行操作。
4、空間資料型別
儲存空間資料,例如地理位置等,一般業務上用到的較少,這裡不做討論。
5、JSON資料型別
這是MySQL5.7.7的labs版本開始支援的資料型別,可以使用MySQL提供的JSON函式來直接對資料進行操作。實際業務中有時候也需要儲存JSON文件,對JSON文件進行操作。
JSON型別會對JSON文件進行校驗,也不允許設定預設值。JSON列的儲存要求與 LONGBLOB或 LONGTEXT列的儲存要求大致相同,所佔儲存空間也大致相同。
JSON 列不能直接索引,要建立間接引用這樣一個列的索引,您可以定義一個生成的列,它提取應該被索引的資訊,然後在生成的列上建立一個索引,如下例所示(MySQL文件上的例子):
mysql> CREATE TABLE jemp (
-> c JSON,
-> g INT GENERATED ALWAYS AS (c->"$.id"),
-> INDEX i (g)
-> );
Query OK, 0 rows affected (0.28 sec)
mysql> INSERT INTO jemp (c) VALUES
> ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'),
> ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}');
Query OK, 4 rows affected (0.04 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT c->>"$.name" AS name
> FROM jemp WHERE g > 2;
+--------+
| name |
+--------+
| Barney |
| Betty |
+--------+
2 rows in set (0.00 sec)
mysql> EXPLAIN SELECT c->>"$.name" AS name
> FROM jemp WHERE g > 2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: jemp
partitions: NULL
type: range
possible_keys: i
key: i
key_len: 5
ref: NULL
rows: 2
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name'))
AS `name` from `test`.`jemp` where (`test`.`jemp`.`g` > 2)
1 row in set (0.00 sec)
通過上面的例子可以看出,JSON型別如果想使用索引,就必須將JSON文件中的屬性提取出來生成一列,然後在該列上建立索引。
一般實際應用時,儲存的JSON文件儘量避免使用文件的屬性值作為查詢條件。
避無可避時記得使用上面的方法建立索引來提高查詢效率。
5、索引的選擇
索引在查詢中起到至關重要的作用,直接影響查詢效率,好的索引的設計能極大的增強MySQL的效能。但,索引並不是越多越好。 因為每次的插入、刪除、修改操作都可能需要重新維護索引,影響這些操作的效能。所以需要在“查”和“增、刪、改”之間找一個平衡。
這個平衡點不好找,需要設計人員根據業務特點及自身設計經驗,還有經常性對SQL語句的分析得出的結論來不停的嘗試。因為同一個查詢語句,在不同的資料量(記錄數)下,預設使用的索引可能不同(MySQL優化器會根據統計量選擇合適的索引)。
想檢視一條SQL語句索引的使用情況,可以使用查詢執行計劃EXPLAIN
來分析。
如果MySQL沒有按照設想的使用索引,可以嘗試使用ANALYZE TABLE語法 來更新表統計資訊。
MySQL提供了PRIMARY KEY(主鍵索引)、 UNIQUE(唯一索引)、INDEX(普通索引)和 FULLTEXT(全文索引)等索引。
大多數MySQL索引(PRIMARY KEY, UNIQUE,INDEX和 FULLTEXT)是使用 B-Trees
儲存的。但也有例外,空間資料型別的索引使用R-Trees
; MEMORY 表也支援雜湊索引 ; InnoDB使用反向列表作為FULLTEXT索引。
我們常看到使用SQL語句建立索引時,有人使用KEY
,有人使用INDEX
,那麼這兩者有什麼區別呢?
INDEX
通常表述為普通索引,沒有約束條件,而KEY
呢,通常表述約束索引,比如PRIMARY KEY
、UNIQUE KEY
等。但在MySQL中,KEY
代替了INDEX
的宣告,就算你宣告INDEX index_name(index_col)
,MySQL也會改成KEY index_name(index_cols)
,但它只是普通索引,沒有任何約束條件。
下面來說說則幾個索引:
1、主鍵索引(PRIMARY KEY)
建議每張表都有一個主鍵(InnoDB表必須要有主鍵),主鍵通常使用無符號整型自增。自增的步長預設是1,如果你有多個寫庫,可以通過改變步長來解決主鍵衝突問題。
整型選擇也是越小越好,例如,你的表最多不超過255條記錄,你可以這麼設計:
CREATE TABLE `xxxx` (
`id` tinyint UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
) ENGINE=InnoDB ...;
如果你的表記錄絕對不會不超過65535,你可以這麼設計:
CREATE TABLE `xxxx` (
`id` smallint UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
) ENGINE=InnoDB ...;
關於主鍵為什麼建議使用整型,上面在介紹InnoDB和MyISAM時已經說了,這裡就不在贅述。
2、唯一索引(UNIQUE KEY)
這個就沒什麼好說的了,當表中一個或多個欄位需要做唯一限制時,就需要為這一個欄位或多個欄位建立唯一索引UNIQUE KEY key_name (key_col)
。
3、普通索引(INDEX)
普通索引使用的最多,通常出現在一些子句中。例如where子句、order by子句、group by子句等。
給普通索引指定長度就會變成字首索引,例如INDEX index_name( index_col(5) )
,MySQL就會利用該欄位前五個字元建立索引。
如果一個欄位過長,而前n個字元可以肯定不相同,就可以使用字首索引INDEX index_name( index_col(n) )
,來減小索引大小、提高查詢效率。
普通索引並不是每個欄位都適合建立,區分度較小的欄位通常不需要建索引,例如狀態欄位,一般狀態只有幾個,而記錄數非常多,所以區分度接近與零,沒有必要建索引。
外來鍵欄位也需要建立普通索引,在實際專案中,極少使用外來鍵約束,一般給外來鍵欄位建立一個普通索引,在業務程式碼中維持外來鍵約束。
where子句、order by子句、group by子句等子句中出現欄位通常需要建立索引,有的需要建立複合索引。要注意複合索引是使用最左字首來查詢行,所以複合索引左邊的前n欄位就不用建立複合索引了。
比如有a,b,c,d四列建立複合索引index a_b_c_d(a,b,c,d)
那麼就沒必要建立諸如index a(a)
、index a_b(a,b)
、index a_b_c(a,b,c)
這類複合a_b_c_d字首的索引了,因為無論是a還是a_b都滿足a_b_c_d的最左字首,所以會使用a_b_c_d索引。
例如以下語句就會使用a_b_c_d
索引:
select * from table_name where a=val1;
select * from table_name where a=val1 and b=val2;
select * from table_name where a=val1 and b=val2 and c=val3;
select * from table_name where a=val1 and b=val2 and c=val3 and d=val4;
但是以下語句就不會使用a_b_c_d
索引:
select * from table_name where a=val1 and c=val2;
select * from table_name where b=val1 and c=val2;
select * from table_name where b=val1 and c=val2 and d=val3;
...
因為這些語句的where子句不滿足a_b_c_d
索引的最左匹配,所以不會使用a_b_c_d
索引。
所以設計索引時,要避免設計這些浪費效能而無效的索引。
另外單個索引名稱建議使用欄位名,複合索引名稱建議使用欄位名加下劃線,按索引欄位的順序命名,這樣比較直觀的知道這條索引包含的列以及順序情況。
還有個要注意,主鍵索引和唯一索引都是索引,無需再為其建立一個普通索引了。
我之前見過一個這麼設計的,差點沒吐血:
create table table_name(
id varchar(20),
...
PRIMARY KEY (`id`),
index id(id)
)...;
要注意,上面時錯誤的設計
4、全文索引(FULLTEXT)
我們都知道只要要檢索的索引欄位左邊不使用’%’、’_'等萬用字元就會使用索引。
例如以下語句就會使用索引:
select * from table_name where col_name like 'world%';
select * from table_name where col_name like 'world_bb%';
...
但是以下語句就不會使用索引:
select * from table_name where col_name like '%world%';
...
那麼要想快速檢索出包含world
的列,就需要使用全文索引了。
但是MySQL提供的全文索引只支援英文分詞,不支援中文分詞。中文分詞可以使用coreseek。
coreseek是在sphinx的基礎上,增加了中文分詞功能,所以可以用來做中文的全文索引。直接編譯進mysql中效能更強。
也有很多使用ElasticSearch做全文搜尋服務,它是分散式的全文搜尋引擎,效能更強,速度更快。
索引也就介紹這兒多了,還是要強調一下:
索引並不是越多越好,不要建立無用的索引,維護索引的開銷是很大的
三、總結
1、如果你清楚要設計的表的用處即常用操作,可以根據各儲存引擎的特點來進行選擇,如果你不清楚,那就選InnoDB。
2、表字段設計儘可能的小
3、根據業務查詢特點,設計合理的索引
關於表設計基本說完了,以上是本人的一些總結,水平有限,難免有錯誤疏漏之處,歡迎指正。後續如果還想到別的再來補充。