- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
运维同事导入一批大约500万左右的数据,耗时较久。他使用的是纯SQL导入,主键使用的是UUID,因为业务原因没有使用自增ID。 因为是内网,不能远程访问。 通过沟通,大致觉得有两个原因,一是因为UUID作为主键,二是表字段繁多,单行加起来接近10000的长度引起行溢出.
因为是临时一次性任务,同事没有做深纠,我在这里简单做一个验证。 版本:5.7.19 引擎:innodb 。
mysql自带的UUID()函数简单方便,不重复。但是它缺点也是众所周知的.
UUID的返回值通常是随机的,而InnoDB的表实质是以主键组织存储的索引,插入新的记录不是顺序追加,而会往前插入,造成页分裂,表的再平衡。在数据量越大的情况,性能影响越严重.
主键包含在每个二级索引中,过长的主键会浪费磁盘和内存的空间.
页分裂 。
为什么主键ID的顺序这么重要?
mysql innodb引擎数据结构为B+tree,查找的时候二分查找,这就要求数据是按顺序存储的。 而UUID是无序的,它作为主键在写入的时候,就可能会频繁的进行中间插入和页分裂。页分裂会造成已有的数据移位,类似于arraylist进行插入数据。 因此表数据越多,插入的效率就会越低。也会使得磁盘空间利用率降低.
除了UUID,其它还有两种常见的ID生成法。 一是自增id,最简单效率也最高,但不适合分布式唯一主键,和在一些敏感业务如订单注册用户时,可能通过ID值会暴露一些商业秘密.
雪花算法 。
再一个是雪花算法,最常见的分布式ID生成算法.
可以看到它跟时间戳(毫秒级)和服务器相关,通过12bit序列号区分同毫秒内产生的不同id。 因此能保证在单台服务器上的大致趋势递增。 在并发高的情况下不能保证完全递增,因此需考虑到这一点.
它有时钟回拨的问题,这里不展开.
定量验证 。
以上只是定性的判断.
下面做一下定量的测试.
本文验证自增id,雪花算法ID,以及uuid作为主键3种情况,往3张空表插入500万条数据,每个批次10万,共50个批次.
StopWatch stopWatch = new StopWatch();
stopWatch.start("准备数据阶段");
int count = 500 * 10000 ;
List<TUser> list = new ArrayList<>(count);
ExecutorService executorService = Executors.newFixedThreadPool(100);
List<TUser> finalList = list;
Semaphore semaphore = new Semaphore(100);
for (int i = 0; i < count; i++) {
executorService.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
semaphore.acquire();
TUser user = new TUser();
// 雪花算法ID
user.setId(SnowflakeIdUtil.snowflakeId());
user.setName(testService.getRandomName());
// UUID
user.setId(UUID.randomUUID().toString());
finalList.add(user);
semaphore.release();
}
}).get();
}
stopWatch.stop();
System.out.println("开始写入");
List<Time> times = new ArrayList<>();
stopWatch.start("写入数据阶段");
List<TUser> subList = new ArrayList<>(100000);
for (int i = 1; i <= count; i++) {
TUser user = finalList.get(i-1);
subList.add(user);
if (i % 100000 == 0) {
// finalList.remove(user);
long start = System.currentTimeMillis();
tUserDao.insertBatch(subList);
long time = System.currentTimeMillis() - start;
times.add(new Time(time));
subList.clear();
log.info("写入一次" + i);
}
}
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
// 将每个批次耗时打印出来,放入excel生成图表
times.stream().map(e -> e.getTime()).forEach(System.out::println);
总耗时情况 。
UU ID:414321ms 雪花ID 167748ms 自增ID 75829ms 。
将最后每个批次耗时的集合打印出来,放到Excel中,生成图表.
纵坐标表示耗时,单位ms 横坐标表示写入批次 。
可以明显的观察到,使用UUID作为主键,在表数据量在350万以后,耗时上升极快,性能呈指数级下降。 同时自增ID效率高于雪花算法ID,都很稳定.
这是在行数据量极小(<50)的情况下.
在行大小> 8000,在页16KB至少两行数据的情况下,也必然会发生行溢出。这里再看看测试情况.
MySQL单行数据最大能存储65535字节的数据,InnoDB的页为16KB,即16384字节,当行的实际存储长度超过16384/2的长度时,就会行溢出。 为什么是/2,因为页至少需要存储两行数据。 为什么单页至少存储两行数据,如果为1,B+tree就退化为了链表.
当行溢出的时候,会将大字段数据存放在页类型为Uncompress BLOB页中,在当前页就会对大字段建立一个引用,类假于二级索引.
所以设置测试表name长度大于大约16384/2的时候,就会发生行溢出现象.
测试代码跟上面类似。 再准备3张空表。 字段类型varchar,长度10000,user.setName()设置实际长度大于8000.
这次只写入50万条数据,每批次1万条数据,共50个批次.
这次uuid在写入30万条记录后,耗时开始明显上升.
以上是行溢出的情况下,UUID,雪花算法,自增ID等 3种ID的写入性能情况.
下面来测试一下,使用UUID在行溢出和不溢出的两种情况下写入性能情况.
再新建两张表,t_user_uuid_long,此表name字段长度100000,最终写入字符长度9995 t_user_uuid_short,此表name字段长度8000,最终写入字符长度7995 。
写入耗时曲线大致相当,看不出明显差距.
但是读的差距非常明显! 。
因为t_user_uuid_long 中的name字段发生了行溢出,。。。。。select的时候需要跨页提取数据,类似于做了1次回表,而且回表还是跨页的.
select id,name from t_user_uuid_short order by id asc limit 1000 耗时0.145s 。
select id,name from t_user_uuid_long order by id asc limit 1000 耗时4.153s,性能相差接近30倍.
以上测试结果只是在测试机上的粗糙测试结果,不是基准测试,只做参考.
测试结果表明,运维同事的慢操作可能受UUID影响 ,但大字段对写入的影响并不明显。 不过大字段对读取的性能影响明显.
最后此篇关于MySQLuuid及其相关的一些简单性能测试的文章就讲到这里了,如果你想了解更多关于MySQLuuid及其相关的一些简单性能测试的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
第一段代码工作正常,并给出了我需要的结果。我现在想做的是让它在 'as num' 上返回 3 个数字值对于“as num”上的 3 个不同值,对于同一列上的 3 个不同位置 SELEC
我想分析一些数据以编写定价算法。以下日期可用: 我需要三个变量/维度的函数/相关因子,它显示三个维度(pers_capacity、卧室数量、浴室数量)增长时中位数(价格)的变化。例如Y(#pers_c
正如标题所说 - 我的 Sprite Kit 游戏时不时地在后台崩溃,总是出现此错误 - Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Sub
假设我尝试保存以下数据,并且Songs模型的name属性上设置了Phalcon \ Mvc \ Model \ Validator \ PresenceOf验证器 // Get an existing
我有一个 if 控件,如下所示; if (Directory.Exists(System.IO.Path.Combine(systemPath, "Reports", companyName))
有人可以告诉我我们使用 ReadLine() 从文件 (.txt) 中读取特定行吗?现在我想读取文件的全部内容(不仅仅是第一行)。为此我需要使用什么方法。我用谷歌搜索了很多,但找不到解决方案。 我的代
我相信在大学时我用从 C 派生的语言为 FPGA 编写了一个程序。我了解 VHDL 和 verilog 等语言。但是,我不明白的是程序员在使用哪个方面有多少选择?它依赖于FPGA吗?我将使用 Xili
我有一个 if 控件,如下所示; if (Directory.Exists(System.IO.Path.Combine(systemPath, "Reports", companyName))
如何在运行时更改 Dashcode (Javascript) 中图像对象的源? 我试过: var image = document.getElementById("image").object;ima
我有几个相互关联的类,它们将被多种不同的算法使用 例子: struct B; struct A { B* parent; }; struct B { std::vector child
我正在开发一个网站,用户在客户收到的表中输入金额,如果任何客户没有提供分期付款(金额),则用户不会在表中输入任何金额,并且用户希望获取违约者的信息客户以10天为基础。所以我的问题是应该定义什么表和属性
我试图从上一个条目中选择一个值,并每次将该数字加一。我让它工作到选择当前条目值(默认 1000)并递增 1 并重新插入该值(因此每次最终都是 1001)。我需要它来选择该字段的最后一个条目,这样它将变
我不擅长“制作”查询。假设这是我的数据库: artist pics ------------------- -
最近,我要为我的网站做一个即时通知系统。我听说 COMET 在这种情况下必不可少。 我已经搜索 PHP 和 Comet 一段时间了,但是,我发现的指南和文章似乎只是循环中的 ajax 请求。例如,有一
我正在开发一款 iOS 游戏,我希望 clown 在场景外生成,然后向下移动。我的想法是全部创建它们,并将它们以 360 像素的距离放置在不可见的场景中。 像这样: SKSpriteNode *clo
我有以下子订单表。 mysql> select * from suborder; +-------------+------------------+ | order_state | bookin
这可能是一个有点初学者的问题,但考虑到在 Java 中调试编码是相当相关的:什么时候编码与 String 对象相关? 假设我有一个要保存到文件中的字符串对象。 String 对象本身是否使用某种我应该
首先我想说我是 CPP 的新手(我从 cpp11 开始):)考虑以下实体:学生(名字+姓氏)和组(描述+更多学生)。我在 C++ 中创建了以下 2 个类: class Student { privat
我正在尝试在单击该复选框时同步更新我的数据库。我决定使用 aJax,但它似乎无法识别 ajax。 代码:将成为 Switch_Active(this.id) 函数的元素 ... Deactivat
我正在创建一个菜单。菜单如下。 $('.category').mouseover(function() { $(this).removeClass('category').addClass('cate
我是一名优秀的程序员,十分优秀!