MySQL使用自增ID主键和UUID作为主键的优劣

目录

      • 一、准备表&数据
      • 二、500w级数据测试
        • 2.1 录入500W数据,自增ID节省一半磁盘空间
        • 2.2 单个数据走索引查询,自增id和uuid相差不大
        • 2.3 范围like查询,自增ID性能优于UUID
        • 2.4 写入测试,自增ID是UUID的4倍
        • 2.5、备份和恢复,自增ID性能优于UUID
        • 500W总结
        • 1000W总结
        • 自增ID主键+步长,适合中等规模的分布式场景
        • UUID,适合小规模的分布式环境

一、准备表&数据

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='用户表';

二、500w级数据测试

# 自增id为主键的表

mysql> select count(1) from UC_USER;

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

| count(1) |

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

|  5720112 |

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

1 row in set (0.00 sec)

 

# uuid为主键的表

mysql> select count(1) from UC_USER_PK_VARCHAR_1;

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

| count(1) |

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

|  5720112 |

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

1 row in set (1.91 sec)

2.1 录入500W数据,自增ID节省一半磁盘空间

占据的空间容量来看,自增ID比UUID小一半左右。

主键类型 数据文件大小 占据容量
自增ID -rw-rw---- 1 mysql mysql 2.5G Aug 11 18:29 UC_USER.ibd 2.5G
UUID -rw-rw---- 1 mysql mysql 5.4G Aug 15 15:11 UC_USER_PK_VARCHAR_1.ibd 5.4G

2.2 单个数据走索引查询,自增id和uuid相差不大

主键类型 SQL 执行时间(s)
自增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 执行时间(s)
(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
(4)范围查询总数量,自增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

2.4 写入测试,自增ID是UUID的4倍

主键类型 SQL 执行时间(s)
自增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

2.5、备份和恢复,自增ID性能优于UUID

主键类型 SQL 执行时间(s)
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
UUID time mysql -utim -ptimgood -h192.168.121.63 test < UC_USER_PK_VARCHAR_500.sql 9m42

500W总结

在500W记录表的测试下:

(1) 普通单条或者20条左右的记录检索,uuid为主键的相差不大几乎效率相同;

(2) 但是范围查询特别是上百成千条的记录查询,自增id的效率要大于uuid;

(3) 在范围查询做统计汇总的时候,自增id的效率要大于uuid;

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

(5) 在备份恢复上,自增ID主键稍微优于UUID。

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。

自增ID主键+步长,适合中等规模的分布式场景

在每个集群节点组的master上面,设置(auto_increment_increment),让目前每个集群的起始点错开 1,步长选择大于将来基本不可能达到的切分集群数,达到将 ID 相对分段的效果来满足全局唯一的效果。

优点是:实现简单,后期维护简单,对应用透明。

缺点是:第一次设置相对较为复杂,因为要针对未来业务的发展而计算好足够的步长;


UUID,适合小规模的分布式环境

对于InnoDB这种聚集主键类型的引擎来说,数据会按照主键进行排序,由于UUID的无序性,InnoDB会产生巨大的IO压力,而且由于索引和数据存储在一起,字符串做主键会造成存储空间增大一倍。

在存储和检索的时候,innodb会对主键进行物理排序,这对auto_increment_int是个好消息,因为后一次插入的主键位置总是在最后。但是对uuid来说,这却是个坏消息,因为uuid是杂乱无章的,每次插入的主键位置是不确定的,可能在开头,也可能在中间,在进行主键物理排序的时候,势必会造成大量的 IO操作影响效率,在数据量不停增长的时候,特别是数据量上了千万记录的时候,读写性能下降的非常厉害。

优点:搭建比较简单,不需要为主键唯一性的处理。

缺点:占用两倍的存储空间(在云上光存储一块就要多花2倍的钱),后期读写性能下降厉害。

你可能感兴趣的:(mysql,分布式,数据库,索引)