測試的目的和原因,公司有很多程序員,每個程序員對數據庫和表結構都有自己的理解。而且每個程序員的理解往往是以效率考慮。既然都是為了效率考慮,那么我就來測試一下究竟哪種使用方式效率最高。
基礎原則:程序員,不是DB ,所以能用程序邏輯解決的問題,就不要使用DB來解決問題,除非程序邏輯做不到,必須使用數據庫來解決的問題。比如分庫分表,比如數據唯一性,這些能在程序里面完成處理的,就不要使用數據庫來做,這樣萬一我們程序出問題了,數據庫還可以兜底,如果直接使用數據庫來做,數據庫出問題了,那用什么兜底呢???
測試環境:4核心8G內存 普通機械硬盤,內網環境(不考慮網速問題)
操作系統:centos linux release 7.9.2009 (Core) 64位
軟件:MySQL Ver 14.14 Distrib 5.7.28, for Linux (x86_64) using EditLine wrApper
注意:表的字符設置為 utf8mb4 ,引擎為:InnoDB
如果不是以上情況,可能不合適本文測試的內容。
晚點將會把生成數據的代碼發出來,分享給想要測試的小伙伴,如果感覺有用,請關注并轉發,謝謝!
先說結論:
各方面效率最高的是varchar ,居然是 varchar 這 。。。 。。。難道是因為使用utf8mb4 數據集的原因,目前尚未對此進行測試。
那么表的使用結構就非常明了了。
主鍵自增使用 int 就可以了。其他大部分字段均可以使用 varchar ,單個表格數據量盡量控制在 500萬的數據以內,500萬的數據備份可能就有4G 左右了。所以不適用bigint,考慮到數據量大的,可以在程序邏輯分表,因為是程序員不是DB,所以盡量少DB操作,程序業務邏輯處理。
電話號碼切記不要使用 bigint 原因是因為會有 010 7532 1456 這樣的格式,那么前面的 0 就不會被記錄在數據庫,那么邏輯需要判斷,會增加代碼量。比價大小也可以用 varchar 需要在邏輯增加一步判斷即可。
不要使用tinyint 做屬性,比如 1代表正常,2代表待審核,3代表審核通過,4代表刪除
直接使用 varchar 記錄 正常,待審核,審核通過,刪除
這樣,當以后增加屬性的時候可以直接增加,而且當緊急維護的時候可以直接知道屬性含義,可以直接操作庫。
如果數據庫記錄的都是 0 1 2 3 4 5 6 7 8 9 這樣的數字,那么現在看到的是 15 請問代表什么含義,那13呢?這樣要查代碼,非常麻煩。盡量不要給自己找麻煩。
以下三個字段必須要有,時間相關的記錄數據變化時間,這個由mysql自己控制,避免程序邏輯問題。最后一個是標志位刪除。
CREATE TABLE `XXXXX` (
`XXXXX_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'XXXXX的ID',
`XXXXX ` varchar(XXX) DEFAULT NULL COMMENT '任意數據',
`XXXXX` varchar(XXX) DEFAULT NULL COMMENT '任意數據',
… …
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
`tb_status` varchar(10) DEFAULT '正常' COMMENT '狀態:正常,正常;刪除,刪除;',
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
另外,不建議做數據庫基本的唯一屬性。可以在程序邏輯處理。原則盡量簡化,能省就省。
以下為測試內容,用來證明上面的結論的,僅作參考。數據量 400萬到1500萬+ 。
首先,制作表格:
CREATE TABLE `user_test` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用戶ID',
`char_email` char(200) DEFAULT NULL COMMENT 'char格式的郵箱',
`varchar_email` varchar(200) DEFAULT NULL COMMENT 'varchar格式的郵箱',
`char_name` char(200) DEFAULT NULL COMMENT 'char格式的名字',
`varchar_name` varchar(200) DEFAULT NULL COMMENT 'varchar格式的名字',
`varchar_content` varchar(800) DEFAULT NULL COMMENT 'varchar格式的文字內容',
`tinyint_type` tinyint(4) DEFAULT NULL COMMENT '用tinyint表示的屬性',
`char_type` char(20) DEFAULT NULL COMMENT '用char格式表示的屬性',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
`tb_status` char(50) DEFAULT '正常' COMMENT '狀態:正常,正常;刪除,刪除;',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意只有主鍵,沒有索引的情況下。
然后用程序插入數據,先測試看插入 400多萬數據的時候,數據庫的操作情況
查詢
因為只有主鍵,并沒有做索引,根據主鍵count耗時 3.53秒
然后 取出10條數據
select * from user_test limit 10;
速度非常快,0.00秒 (400萬+的數據量)
400萬數據查詢
select * from user_test where char_email='[email protected]' limit 10;
char 格式的數據查詢,耗時 7.39秒 (400萬+的數據量)
select * from user_test where varchar_email='[email protected]' limit 10;
varchar 格式的數據查詢,耗時 5.52 秒 (400萬+的數據量)
select * from user_test where user_id > 78921 limit 10;
這個查詢可以用于分頁操作,數據量大的時候可以用
耗時 0.07 秒,速度也很快。使用主鍵進行判斷查詢很快。(400萬+的數據量)
select * from user_test where user_id > 79021 limit 2;
查詢速度非常快
查詢
select * from user_test where varchar_name='行文可抒懷暢志,揚己志于天下,左登峰也喜此道,但他心性陰暗,不喜成群為伍,喧嘩宣講。' limit 12;
varchar 查詢 耗時24.34 秒(400萬+的數據量)
select * from user_test where char_name='行文可抒懷暢志,揚己志于天下,左登峰也喜此道,但他心性陰暗,不喜成群為伍,喧嘩宣講。' limit 12;
char 查詢 耗時 24.09 秒 (400萬+的數據量)
將表的數據量升級到 1513萬,繼續測試。
select count(user_id) from user_test;
耗時 50.32 秒,雖然是主鍵但是因為沒加索引,速度很慢。(1500萬+的數據量)
select count(char_type) from user_test where char_type='del';
char type 查詢 耗時 57.27 秒 ,并非主鍵,并且沒有索引。(1500萬+的數據量)
沒有索引
增加索引之后,耗時:1秒,和tinyint差別不大。
select count(tinyint_type) from user_test where tinyint_type='7';
tinyint type 查詢 耗時 56.68秒 并非主鍵,并且沒有索引。(1500萬+的數據量)
增加索引之后
增加索引之后,耗時:0.69秒。(1500萬+的數據量)
select * from user_test where tinyint_type=7 limit 3;
耗時:0.07秒,帶索引,(1500萬+的數據量)
select * from user_test where char_type='del' limit 3;
耗時:0.1秒,帶索引,(1500萬+的數據量)
查詢
這個測試證明,使用tinyint 或者 char 用來表示 屬性,在沒有索引的情況下,差別非常小。
查詢
增加索引前查詢count
select count(user_id) from user_test;
耗時 56.99秒
增加索引之后查詢 count
查詢
select count(user_id) from user_test;
耗時 3.24秒 ,速度提升非常多啊。
但是依然有3.24秒,速度依然不理想啊,查詢了下解決方案,不用主鍵做索引,
使用 count(*) 居然比使用 count(主鍵快) 這個你敢信???經過反復測試,發現都差不多的。
那最終的count的效率的解決方案是什么呢?答案是如果數據量比較大,可以使用一個計數列,通過邏輯處理,不過如果增刪改查比較多就不要用這樣的方式,可以通過邏輯處理的方式進行處理,比如入庫隊列,redis 緩存處理等等。
但是如果業務邏輯OK的情況下,單表數據量控制在 500萬以內更加合適。
以此表為例子,當數據在300萬的時候,備份的sql文件是2G左右大小。500萬+ 的數據庫備份文件會更大一些,這樣的話,還要考慮備份的時間和效率問題。切記一定要做讀寫分離,并且在從庫上做數據備份操作。
結論:tinyint 和 char 的效率非常接近,但是在維護和邏輯理解上,char更加直觀,建議使用char 又因為 varchar 的查詢效率高于 char 所以,都用 varchar吧。
使用bigint ,tinyint 還是使用char,varchar ?
測試表結構:
CREATE TABLE `test_big_char` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '測試的ID',
`bigint_num` bigint(20) DEFAULT NULL COMMENT 'bigint數據',
`char_num` char(20) DEFAULT NULL COMMENT 'char數據',
`varchar_num` varchar(20) DEFAULT NULL COMMENT 'varchar數據',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
`tb_status` char(50) DEFAULT '正常' COMMENT '狀態:正常,正常;刪除,刪除;',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
select count(*) from test_big_char;
耗時1.3秒,400萬數據量級
查詢
根據主鍵進行簡單查詢,速度非常快。400萬數據量級
查詢
select * from test_big_char where bigint_num = 6616351006811;
耗時:2.4秒 ,400萬數據量級
增加索引之后,耗時:0.01秒
切換其他值查詢,耗時:0.00秒,帶索引,400萬數據量級
select * from test_big_char where char_num = '6616351006811';
耗時:2.87秒 ,400萬數據量級
增加索引之后,耗時:0.00秒
select * from test_big_char where varchar_num = '6616351006811';
耗時:2.28秒 ,400萬數據量級
增加索引之后,耗時:0.00秒
切換其他值進行查詢,耗時:0.00秒,帶索引,400萬數據量級
查詢
第二次查詢情況
查詢
第三次查詢,因為 char 查詢最慢,所以之做 bigint 和 varchar 的比較:
查詢
在沒有索引的情況下,varchar和bigint 的查詢效率幾乎相同 ,400萬數據量級。
增加索引之后:
增加索引
那么接下來測試 大于,小于 的各種情況,實際測試 char和varchar 得到的結果不準確,需要加其他限定條件,但就數據庫查詢而言,varchar的查詢效率最高。
比較
為了避免執行先后順序問題,再執行一次順序不一樣的。
結果
還測試了許多其他的內容,就不再一一記錄了,感興趣可以自己操作一遍。
晚點將會把生成數據的代碼發出來,分享給想要測試的小伙伴,如果感覺有用,請關注并轉發,謝謝!






