MySQL 使用自增ID主鍵和UUID 作為主鍵的優劣比較詳細過程(從百萬到千萬表記錄測試)


Reference: https://blog.csdn.net/mchdba/article/details/52336203

一個開發同事做了一個框架,里面主鍵是uuid,我跟他建議說mysql不要用uuid用自增主鍵,自增主鍵效率高,他說不一定高,我說innodb的索引特性導致了自增id做主鍵是效率最好的,為了拿實際的案例來說服他,所以准備做一個詳細的測試。

 

作為互聯網公司,一定有用戶表,而且用戶表UC_USER基本會有百萬記錄,所以在這個表基礎上准測試數據來進行測試。

 

         測試過程是目前我想到的多方位的常用的幾種類型的sql進行測試,當然可能不太完善,歡迎大家留言提出更加完善的測試方案或者測試sql語句。

 

 

 

1、准備表以及數據

UC_USER,自增ID為主鍵,表結構類似如下:

CREATE TABLE `UC_USER` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `USER_NAME` varchar(100) DEFAULT NULL COMMENT '用戶名',
  `USER_PWD` varchar(200) DEFAULT NULL COMMENT '密碼',
  `BIRTHDAY` datetime DEFAULT NULL COMMENT '生日',
  `NAME` varchar(200) DEFAULT NULL COMMENT '姓名',
  `USER_ICON` varchar(500) DEFAULT NULL COMMENT '頭像圖片',
  `SEX` char(1) DEFAULT NULL COMMENT '性別, 1:男,2:女,3:保密',
  `NICKNAME` varchar(200) DEFAULT NULL COMMENT '昵稱',
  `STAT` varchar(10) DEFAULT NULL COMMENT '用戶狀態,01:正常,02:凍結',
  `USER_MALL` bigint(20) DEFAULT NULL COMMENT '當前所屬MALL',
  `LAST_LOGIN_DATE` datetime DEFAULT NULL COMMENT '最后登錄時間',
  `LAST_LOGIN_IP` varchar(100) DEFAULT NULL COMMENT '最后登錄IP',
  `SRC_OPEN_USER_ID` bigint(20) DEFAULT NULL COMMENT '來源的聯合登錄',
  `EMAIL` varchar(200) DEFAULT NULL COMMENT '郵箱',
  `MOBILE` varchar(50) DEFAULT NULL COMMENT '手機',
  `IS_DEL` char(1) DEFAULT '0' COMMENT '是否刪除',
  `IS_EMAIL_CONFIRMED` char(1) DEFAULT '0' COMMENT '是否綁定郵箱',
  `IS_PHONE_CONFIRMED` char(1) DEFAULT '0' COMMENT '是否綁定手機',
  `CREATER` bigint(20) DEFAULT NULL COMMENT '創建人',
  `CREATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注冊時間',
  `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '修改日期',
  `PWD_INTENSITY` char(1) DEFAULT NULL COMMENT '密碼強度',
  `MOBILE_TGC` char(64) DEFAULT NULL COMMENT '手機登錄標識',
  `MAC` char(64) DEFAULT NULL COMMENT 'mac地址',
  `SOURCE` char(1) DEFAULT '0' COMMENT '1:WEB,2:IOS,3:ANDROID,4:WIFI,5:管理系統, 0:未知',
  `ACTIVATE` char(1) DEFAULT '1' COMMENT '激活,1:激活,0:未激活',
  `ACTIVATE_TYPE` char(1) DEFAULT '0' COMMENT '激活類型,0:自動,1:手動',
  PRIMARY KEY (`ID`),
  UNIQUE KEY `USER_NAME` (`USER_NAME`),
  KEY `MOBILE` (`MOBILE`),
  KEY `IDX_MOBILE_TGC` (`MOBILE_TGC`,`ID`),
  KEY `IDX_EMAIL` (`EMAIL`,`ID`),
  KEY `IDX_CREATE_DATE` (`CREATE_DATE`,`ID`),
  KEY `IDX_UPDATE_DATE` (`UPDATE_DATE`)
) ENGINE=InnoDB AUTO_INCREMENT=7122681 DEFAULT CHARSET=utf8 COMMENT='用戶表'

 

 

 

UC_USER_PK_VARCHAR表,字符串ID為主鍵,采用uuid

CREATE TABLE `UC_USER_PK_VARCHAR_1` (
  `ID` varchar(36) CHARACTER SET utf8mb4 NOT NULL DEFAULT '0' COMMENT '主鍵',
  `USER_NAME` varchar(100) DEFAULT NULL COMMENT '用戶名',
  `USER_PWD` varchar(200) DEFAULT NULL COMMENT '密碼',
  `BIRTHDAY` datetime DEFAULT NULL COMMENT '生日',
  `NAME` varchar(200) DEFAULT NULL COMMENT '姓名',
  `USER_ICON` varchar(500) DEFAULT NULL COMMENT '頭像圖片',
  `SEX` char(1) DEFAULT NULL COMMENT '性別, 1:男,2:女,3:保密',
  `NICKNAME` varchar(200) DEFAULT NULL COMMENT '昵稱',
  `STAT` varchar(10) DEFAULT NULL COMMENT '用戶狀態,01:正常,02:凍結',
  `USER_MALL` bigint(20) DEFAULT NULL COMMENT '當前所屬MALL',
  `LAST_LOGIN_DATE` datetime DEFAULT NULL COMMENT '最后登錄時間',
  `LAST_LOGIN_IP` varchar(100) DEFAULT NULL COMMENT '最后登錄IP',
  `SRC_OPEN_USER_ID` bigint(20) DEFAULT NULL COMMENT '來源的聯合登錄',
  `EMAIL` varchar(200) DEFAULT NULL COMMENT '郵箱',
  `MOBILE` varchar(50) DEFAULT NULL COMMENT '手機',
  `IS_DEL` char(1) DEFAULT '0' COMMENT '是否刪除',
  `IS_EMAIL_CONFIRMED` char(1) DEFAULT '0' COMMENT '是否綁定郵箱',
  `IS_PHONE_CONFIRMED` char(1) DEFAULT '0' COMMENT '是否綁定手機',
  `CREATER` bigint(20) DEFAULT NULL COMMENT '創建人',
  `CREATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注冊時間',
  `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '修改日期',
  `PWD_INTENSITY` char(1) DEFAULT NULL COMMENT '密碼強度',
  `MOBILE_TGC` char(64) DEFAULT NULL COMMENT '手機登錄標識',
  `MAC` char(64) DEFAULT NULL COMMENT 'mac地址',
  `SOURCE` char(1) DEFAULT '0' COMMENT '1:WEB,2:IOS,3:ANDROID,4:WIFI,5:管理系統, 0:未知',
  `ACTIVATE` char(1) DEFAULT '1' COMMENT '激活,1:激活,0:未激活',
  `ACTIVATE_TYPE` char(1) DEFAULT '0' COMMENT '激活類型,0:自動,1:手動',
  PRIMARY KEY (`ID`),
  UNIQUE KEY `USER_NAME` (`USER_NAME`),
  KEY `MOBILE` (`MOBILE`),
  KEY `IDX_MOBILE_TGC` (`MOBILE_TGC`,`ID`),
  KEY `IDX_EMAIL` (`EMAIL`,`ID`),
  KEY `IDX_CREATE_DATE` (`CREATE_DATE`,`ID`),
  KEY `IDX_UPDATE_DATE` (`UPDATE_DATE`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶表';

 

 

 

 

2、500W數據測試

2.1 錄入500W數據,自增ID節省一半磁盤空間

確定兩個表數據量

# 自增id為主鍵的表

mysql> select count(1) from UC_USER;

+----------+

| count(1) |

+----------+

|  5720112 |

+----------+

1 row in set (0.00 sec)

 

mysql>

 

# uuid為主鍵的表

mysql> select count(1) from UC_USER_PK_VARCHAR_1;

+----------+

| count(1) |

+----------+

|  5720112 |

+----------+

1 row in set (1.91 sec)

 

占據的空間容量來看,自增ID比UUID小一半左右。

主鍵類型

數據文件大小

占據容量

自增ID

-rw-rw---- 1 mysql mysql 2.5G Aug 11 18:29 UC_USER.ibd

2.5 G

UUID

-rw-rw---- 1 mysql mysql 5.4G Aug 15 15:11 UC_USER_PK_VARCHAR_1.ibd

5.4 G

 

 

 

2.2 單個數據走索引查詢,自增id和uuid相差不大

主鍵類型

SQL語句

執行時間 (秒)

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER` t WHERE t.`MOBILE` ='14782121512';

0.118

 

 

 

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`MOBILE` ='14782121512';

0.117

 

 

 

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER` t WHERE t.`MOBILE` IN( '14782121512','13761460105');

0.049

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`MOBILE` IN('14782121512','13761460105');

0.040

 

 

 

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER` t WHERE t.`CREATE_DATE`='2013-11-24 10:26:36' ;

0.139

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE`='2013-11-24 10:26:43' ;

0.126

 

 

 

2.3 范圍like查詢,自增ID性能優於UUID

主鍵類型

SQL語句

執行時間 (秒)

 

(1)模糊范圍查詢1000條數據,自增ID性能要好於UUID

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER` t WHERE t.`MOBILE` LIKE '147%' LIMIT 1000;

1.784

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`MOBILE` LIKE '147%' LIMIT 1000;

3.196

 

(2)日期范圍查詢20條數據,自增ID稍微弱於UUID

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER` t WHERE t.`CREATE_DATE` > '2016-08-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 20;

0.601

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE` > '2016-08-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 20;

0.543

 

(3)范圍查詢200條數據,自增ID性能要好於UUID

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 200;

2.314

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 200;

3.229

 

范圍查詢總數量,自增ID要好於UUID

自增ID

SELECT SQL_NO_CACHE COUNT(1) FROM test.`UC_USER` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36'  ;

0.514

UUID

SELECT SQL_NO_CACHE COUNT(1) FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36'  ;

1.092

 

 

 

PS:在有緩存的情況下,兩者執行效率沒有相差很小。

 

 

 

2.4 寫入測試,自增ID是UUID的4倍

主鍵類型

SQL語句

執行時間 (秒)

 

 

 

 

 

自增ID

UPDATE test.`UC_USER` t SET t.`MOBILE_TGC`='T2' WHERE t.`CREATE_DATE` > '2016-05-03 10:26:36' AND t.`CREATE_DATE` <'2016-05-04 00:00:00'  ;

1.419  

UUID

UPDATE test.`UC_USER_PK_VARCHAR_1` t SET t.`MOBILE_TGC`='T2' WHERE t.`CREATE_DATE` > '2016-05-03 10:26:36' AND t.`CREATE_DATE` <'2016-05-04 00:00:00'  ;

5.639

 

 

 

自增ID

INSERT INTO test.`UC_USER`(   ID,   `USER_NAME`,   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`,   `MOBILE`,   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` ) SELECT       NULL,    CONCAT('110',`USER_NAME`,8),   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`, CONCAT('110',TRIM(`MOBILE`)),   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` FROM `test`.`UC_USER_1` LIMIT 100;

0.105

UUID

INSERT INTO test.`UC_USER_PK_VARCHAR_1`(    ID,   `USER_NAME`,   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`,   `MOBILE`,   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` ) SELECT         UUID(),   CONCAT('110',`USER_NAME`,8),   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`, CONCAT('110',TRIM(`MOBILE`)),   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` FROM `test`.`UC_USER_1` LIMIT 100;

0.424

 

 

 

2.5、備份和恢復,自增ID性能優於UUID

主鍵類型

SQL語句

執行時間 (秒)

 

Mysqldump備份

自增ID

time mysqldump -utim -ptimgood -h192.168.121.63 test UC_USER_500> UC_USER_500.sql

28.59秒

UUID

time mysqldump -utim -ptimgood -h192.168.121.63 test UC_USER_PK_VARCHAR_500> UC_USER_PK_VARCHAR_500.sql

31.08秒

 

MySQL恢復

自增ID

time mysql  -utim -ptimgood -h192.168.121.63  test < UC_USER_500.sql

7m36.601s

UUID

time mysql  -utim -ptimgood -h192.168.121.63  test < UC_USER_PK_VARCHAR_500.sql

9m42.472s

 

 

 

 

 

 

 

3、500W總結

在500W記錄表的測試下:

(1)      普通單條或者20條左右的記錄檢索,uuid為主鍵的相差不大幾乎效率相同;

(2)      但是范圍查詢特別是上百成千條的記錄查詢,自增id的效率要大於uuid;

(3)      在范圍查詢做統計匯總的時候,自增id的效率要大於uuid;

(4)      在存儲上面,自增id所占的存儲空間是uuid的1/2;

(5)      在備份恢復上,自增ID主鍵稍微優於UUID。

 

 

 

4、1000W數據測試

4.1 錄入1000W數據記錄,看存儲空間

# 自增id為主鍵的表

mysql> use test;

Database changed

mysql> select count(1) from UC_USER_1;

+----------+

| count(1) |

+----------+

| 10698102 |

+----------+

1 row in set (27.42 sec)

 

# uuid為主鍵的表

mysql> select count(1) from UC_USER_PK_VARCHAR_1;

+----------+

| count(1) |

+----------+

| 10698102 |

+----------+

1 row in set (0.00 sec)

 

mysql>

 

 

占據的空間容量來看,自增ID比UUID小一半左右:

主鍵類型

數據文件大小

占據容量 

自增ID

-rw-rw---- 1 mysql mysql 4.2G Aug 20 23:08 UC_USER_1.ibd

4.2 G

UUID

-rw-rw---- 1 mysql mysql 8.8G Aug 20 18:20 UC_USER_PK_VARCHAR_1.ibd

8.8 G

 

 

 

4.2 單個數據走索引查詢,自增id和 uuid效率比是:(2~3):1

主鍵類型

SQL語句

執行時間 (秒)

 

單條記錄查詢

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_1` t WHERE t.`MOBILE` ='14782121512';

0.069

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`MOBILE` ='14782121512';

0.274

 

小范圍查詢

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_1` t WHERE t.`MOBILE` IN( '14782121512','13761460105');

0.050

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`MOBILE` IN('14782121512','13761460105');

0.151

 

根據日期查詢

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_1` t WHERE t.`CREATE_DATE`='2013-11-24 10:26:36' ;

0.269

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE`='2013-11-24 10:26:43' ;

0.810

 

 

 

4.3 范圍like查詢,自增ID性能優於UUID,比值(1.5~2):1

主鍵類型

SQL語句

執行時間 (秒)

 

(1)模糊范圍查詢1000條數據,自增ID性能要好於UUID

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER` t WHERE t.`MOBILE` LIKE '147%' LIMIT 1000;

2.398

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`MOBILE` LIKE '147%' LIMIT 1000;

5.872

 

(2)日期范圍查詢20條數據,自增ID稍微弱於UUID

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_1` t WHERE t.`CREATE_DATE` > '2016-08-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 20;

0.765

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE` > '2016-08-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 20;

1.090

 

(3)范圍查詢200條數據,自增ID性能要好於UUID

自增ID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_1` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 200;

1.569

UUID

SELECT SQL_NO_CACHE t.* FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36' ORDER BY t.`UPDATE_DATE` DESC LIMIT 200;

2.597

 

范圍查詢總數量,自增ID要好於UUID

自增ID

SELECT SQL_NO_CACHE COUNT(1) FROM test.`UC_USER_1` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36'  ;

1.129

UUID

SELECT SQL_NO_CACHE COUNT(1) FROM test.`UC_USER_PK_VARCHAR_1` t WHERE t.`CREATE_DATE` > '2016-07-01 10:26:36'  ;

2.302

 

 

 

4.4 寫入測試,自增ID比UUID效率高,比值(3~10):1

主鍵類型

SQL語句

執行時間 (秒)

 

 

修改一天的記錄

自增ID

UPDATE test.`UC_USER_1` t SET t.`MOBILE_TGC`='T2' WHERE t.`CREATE_DATE` > '2016-05-03 10:26:36' AND t.`CREATE_DATE` <'2016-05-04 00:00:00'  ;

2.685

UUID

UPDATE test.`UC_USER_PK_VARCHAR_1` t SET t.`MOBILE_TGC`='T2' WHERE t.`CREATE_DATE` > '2016-05-03 10:26:36' AND t.`CREATE_DATE` <'2016-05-04 00:00:00'  ;

26.521

 

錄入數據

自增ID

INSERT INTO test.`UC_USER_1`(   ID,   `USER_NAME`,   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`,   `MOBILE`,   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` ) SELECT       NULL,    CONCAT('110',`USER_NAME`,8),   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`, CONCAT('110',TRIM(`MOBILE`)),   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` FROM `test`.`UC_USER_1` LIMIT 100;

0.534

UUID

INSERT INTO test.`UC_USER_PK_VARCHAR_1`(    ID,   `USER_NAME`,   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`,   `MOBILE`,   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` ) SELECT         UUID(),   CONCAT('110',`USER_NAME`,8),   `USER_PWD`,   `BIRTHDAY`,   `NAME`,   `USER_ICON`,   `SEX`,   `NICKNAME`,   `STAT`,   `USER_MALL`,   `LAST_LOGIN_DATE`,   `LAST_LOGIN_IP`,   `SRC_OPEN_USER_ID`,   `EMAIL`, CONCAT('110',TRIM(`MOBILE`)),   `IS_DEL`,   `IS_EMAIL_CONFIRMED`,   `IS_PHONE_CONFIRMED`,   `CREATER`,   `CREATE_DATE`,   `UPDATE_DATE`,   `PWD_INTENSITY`,   `MOBILE_TGC`,   `MAC`,   `SOURCE`,   `ACTIVATE`,   `ACTIVATE_TYPE` FROM `test`.`UC_USER_1` LIMIT 100;

1.716

 

 

 

4.5、備份和恢復,自增ID性能優於UUID

主鍵類型

SQL語句

執行時間 (秒)

 

Mysqldump備份

自增ID

time mysqldump -utim -ptimgood -h192.168.121.63 test UC_USER_1> UC_USER_1.sql

0m50.548s

UUID

time mysqldump -utim -ptimgood -h192.168.121.63 test UC_USER_PK_VARCHAR_1> UC_USER_PK_VARCHAR_1.sql

0m58.590s

 

MySQL恢復

自增ID

time mysql -utim -ptimgood -h192.168.121.63 test < UC_USER_1.sql

17m30.822s

UUID

time mysql -utim -ptimgood -h192.168.121.63 test < UC_USER_PK_VARCHAR_1.sql

23m6.360s

 

 

 

 

 

 

5、1000W總結

在1000W記錄表的測試下:

(1)普通單條或者20條左右的記錄檢索,自增主鍵效率是uuid主鍵的2到3倍;

(2)但是范圍查詢特別是上百成千條的記錄查詢,自增id的效率要大於uuid;

(3)在范圍查詢做統計匯總的時候,自增id主鍵的效率是uuid主鍵1.5到2倍;

(4)在存儲上面,自增id所占的存儲空間是uuid的1/2;

(5)在寫入上面,自增ID主鍵的效率是UUID主鍵的3到10倍,相差比較明顯,特別是update小范圍之內的數據上面。

(6)在備份恢復上,自增ID主鍵稍微優於UUID。

 

 

 

6、MySQL分布式架構的取舍

分布式架構,意味着需要多個實例中保持一個表的主鍵的唯一性。這個時候普通的單表自增ID主鍵就不太合適,因為多個mysql實例上會遇到主鍵全局唯一性問題。

 

 

 

6.1、自增ID主鍵+步長,適合中等規模的分布式場景

 

         在每個集群節點組的master上面,設置(auto_increment_increment),讓目前每個集群的起始點錯開 1,步長選擇大於將來基本不可能達到的切分集群數,達到將 ID 相對分段的效果來滿足全局唯一的效果。

 

優點是:實現簡單,后期維護簡單,對應用透明。

 

缺點是:第一次設置相對較為復雜,因為要針對未來業務的發展而計算好足夠的步長;

 

規划:

比如計划總共N個節點組,那么第i個節點組的my.cnf的配置為:

auto_increment_offset  i

auto_increment_increment  N

 

假如規划48個節點組,N為48,現在配置第8個節點組,這個i為8,第8個節點組的my.cnf里面的配置為:

auto_increment_offset  8

auto_increment_increment  48

 

 

 

6.2、UUID,適合小規模的分布式環境

         對於InnoDB這種聚集主鍵類型的引擎來說,數據會按照主鍵進行排序,由於UUID的無序性,InnoDB會產生巨大的IO壓力,而且由於索引和數據存儲在一起,字符串做主鍵會造成存儲空間增大一倍。

 

在存儲和檢索的時候,innodb會對主鍵進行物理排序,這對auto_increment_int是個好消息,因為后一次插入的主鍵位置總是在最后。但是對uuid來說,這卻是個壞消息,因為uuid是雜亂無章的,每次插入的主鍵位置是不確定的,可能在開頭,也可能在中間,在進行主鍵物理排序的時候,勢必會造成大量的 IO操作影響效率,在數據量不停增長的時候,特別是數據量上了千萬記錄的時候,讀寫性能下降的非常厲害。

 

優點:搭建比較簡單,不需要為主鍵唯一性的處理。

缺點:占用兩倍的存儲空間(在雲上光存儲一塊就要多花2倍的錢),后期讀寫性能下降厲害。

 

 

 

 

6.3、雪花算法自造全局自增ID,適合大數據環境的分布式場景

由twitter公布的開源的分布式id算法snowflake(Java版本)

 

IdWorker.java:

 

package com.demo.elk;

import org.slf4j.Logger; 

import org.slf4j.LoggerFactory;

 

public class IdWorker {

   

    protected static final Logger LOG = LoggerFactory.getLogger(IdWorker.class);

    

    private long workerId;

    private long datacenterId;

    private long sequence = 0L;

 

    private long twepoch = 1288834974657L;

 

    private long workerIdBits = 5L;

    private long datacenterIdBits = 5L;

    private long maxWorkerId = -1L ^ (-1L << workerIdBits);

    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    private long sequenceBits = 12L;

 

    private long workerIdShift = sequenceBits;

    private long datacenterIdShift = sequenceBits + workerIdBits;

    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    private long sequenceMask = -1L ^ (-1L << sequenceBits);

 

    private long lastTimestamp = -1L;

 

    public IdWorker(long workerId, long datacenterId) {

        // sanity check for workerId

        if (workerId > maxWorkerId || workerId < 0) {

            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));

        }

        if (datacenterId > maxDatacenterId || datacenterId < 0) {

            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));

        }

        this.workerId = workerId;

        this.datacenterId = datacenterId;

        LOG.info(String.format("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d", timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId));

    }

 

    public synchronized long nextId() {

        long timestamp = timeGen();

 

        if (timestamp < lastTimestamp) {

            LOG.error(String.format("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp));

            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));

        }

 

        if (lastTimestamp == timestamp) {

            sequence = (sequence + 1) & sequenceMask;

            if (sequence == 0) {

                timestamp = tilNextMillis(lastTimestamp);

            }

        } else {

            sequence = 0L;

        }

 

        lastTimestamp = timestamp;

 

        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;

    }

 

    protected long tilNextMillis(long lastTimestamp) {

        long timestamp = timeGen();

        while (timestamp <= lastTimestamp) {

            timestamp = timeGen();

        }

        return timestamp;

    }

 

    protected long timeGen() {

        return System.currentTimeMillis();

    }

}

 

 

 

測試生成ID的測試類,IdWorkerTest.java:

package com.demo.elk;

 

import java.util.HashSet;

import java.util.Set;

 

public class IdWorkerTest {

          

    static class IdWorkThread implements Runnable {

        private Set<Long> set;

        private IdWorker idWorker;

 

        public IdWorkThread(Set<Long> set, IdWorker idWorker) {

            this.set = set;

            this.idWorker = idWorker;

        }

 

        public void run() {

            while (true) {

                long id = idWorker.nextId();

                System.out.println("            real id:" + id);

                if (!set.add(id)) {

                    System.out.println("duplicate:" + id);

                }

            }

        }

    }

 

    public static void main(String[] args) {

        Set<Long> set = new HashSet<Long>();

        final IdWorker idWorker1 = new IdWorker(0, 0);

        final IdWorker idWorker2 = new IdWorker(1, 0);

        Thread t1 = new Thread(new IdWorkThread(set, idWorker1));

        Thread t2 = new Thread(new IdWorkThread(set, idWorker2));

        t1.setDaemon(true);

        t2.setDaemon(true);

        t1.start();

        t2.start();

        try {

            Thread.sleep(30000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

 

 

 

7,總結

(1)單實例或者單節點組:

經過500W、1000W的單機表測試,自增ID相對UUID來說,自增ID主鍵性能高於UUID,磁盤存儲費用比UUID節省一半的錢。所以在單實例上或者單節點組上,使用自增ID作為首選主鍵。

 

(2)分布式架構場景:

         20個節點組下的小型規模的分布式場景,為了快速實現部署,可以采用多花存儲費用、犧牲部分性能而使用UUID主鍵快速部署;

 

         20到200個節點組的中等規模的分布式場景,可以采用自增ID+步長的較快速方案。

 

         200以上節點組的大數據下的分布式場景,可以借鑒類似twitter雪花算法構造的全局自增ID作為主鍵。

原文地址:https://www.cnblogs.com/skying555/p/8647617.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM