批量写库操作,如何优化?

2023-11-06 16:05:37 浏览数 (1)

引言

数据库插入操作的语句如下:

代码语言:javascript复制
insert into table values (a1, b1)

涉及到SQL层和存储层,其中SQL层需要解析SQL语句,生成抽象语法树(AST),计算表达式等,存储层需要判断主键冲突,包括增量数据和基线数据上的主键冲突,如果是非重复主键,则将数据插入到增量数据中。

上条插入语句只插入一行数据,称之为单条插入,相应地,还可以在一条语句中插入多行数据,称之为批量插入。

代码语言:javascript复制
insert into table values (a1, b1), (a2, b2), (a3, b3)

批量插入的多行数据作为一个事务,所有数据插入成功,或者所有数据插入失败,不会出现部分数据插入成功的情况。批量插入相对于单条插入在性能上有很大优势,SQL解析只需要做一次,事务只需要做一次,因此理应在相同的时间内插入更多行数据。

1. 单行插入引擎

此前,OceanBase的单条插入与批量插入使用的是同一套接口,从SQL层读取一行,检查冲突,插入数据,然后反复重复这个过程,直到没有数据为止。这样的代码看起来非常优雅,却没有利用到批量插入的特点而做针对性的优化。

2. 批量插入引擎

批量插入引擎每次可以读取一批数据,比如500行,然后做批量检查冲突,再批量插入到增量数据中(内存B 树),目前做的只有批量读和检查冲突,批量插入留到以后再做。看似很简单的优化,性能却提升了很多,在递增插入场景,Sysbench bulk insert的单线程测试中,无基线数据时,性能提升30%,有基线数据时,性能提升了100%。性能提升的原因有如下几点:

2.1 系统层面
  1. 正在处理的一批数据可以始终在CPU Cache中,L1 Cache的大小是32KB,一行的大小为32 bytes(元数据,指针等),可以存储1024行,而读L1 Cache的性能是读内存性能的100倍。
  2. CPU不仅可以Cache数据,还可以Cache指令,在单条插入的时候,在一定时间内总是执行不同的指令,因此很难Cache,每次都需要从内存中取指令,将指令解码后,才能再去取数据,而在批量插入中,在一个紧凑的循环中,每次都是执行相同的指令,因此这些指令基本上可以在Cache中。
  3. CPU访问内存的过程为,进程的虚拟内存地址通过查找TLB(硬件高速缓存,空间较小),Page Table(内存中)转化为内存的物理地址,若TLB中找不到对应的虚拟地址,需要访问内存中的Page Table。若同时处理一个500行的数组,TLB的命中率会大很多,而访问TLB的速度是内存的100倍。
  4. CPU有预取内存功能,当从SQL中读到的行需要转换为存储层中的行时,以前是读内存,转换,读内存,转换,而现在是完全并行起来的,转换完一行之后,后面的行已经从内存中被预取到CPU Cache中了,而且CPU读内存的单位是Cache Line是64 bytes,每次可以读两行,而以前单行处理的时候,是把这个能力浪费了的。
  5. 存储层从SQL拿数据的时候,会调用一个虚函数get_next_row,C 里虚函数是通过虚函数表实现的,对象里有一个指向虚函数表的指针,每次调用函数的时候,需要通过指针找到这个表,然后在表里再通过一个指针,找到相应的函数实现,也就是每次调用get_next_row都有两次随机内存访问,而改成批量之后,就少了大量的这种操作,比如有4万行数据,以前需要4万次虚函数调用,而现在只需要80次。
2.2 算法层面
  1. 检查主键冲突的时候,由于基线数据是静态的,最大值不变,而后面插入的数据往往是越来越大的,因此只需要比较一下这一批数据的最小值和静态数据的最大值即可,减少了大量的冲突检测。
  2. 单行插入内存B 树时,每一行都需要从根节点搜索,直到相应的叶子节点,需要多次加读锁写锁,批量插入后,对一批数据做一个排序,然后将相应的数据直接插入到相应的叶子节点而不再从根节点搜索,减少了大量的比较和加锁操作,而且同一批数据基本在少量的叶子节点中,因此叶子节点基本都可以在CPU Cache中。

·END·

代码语言:javascript复制
相关阅读:MySQL与MongoDB,该如何做技术选型?
应该如何正确理解BFF架构设计?
私藏多年的系统性能优化十大绝招(万字干货)
面对复杂业务系统,通用架构设计法则
高可用高性能核心原理探究,Kafka 核心全面总结
服务接口优化的常见方案实战总结聊聊分布式服务下的八种异步实现方式
有没有那么一瞬间,你也曾有过“失业焦虑”?浅析分布式系统中的补偿机制设计问题聊聊分布式日志系统的设计与实践
执行个 DEL 竟然也会阻塞 Redis?深挖一下果然不简单
PHP 中数组是如何灵活支持多数据类型的?
一文带你看通透,MySQL事务ACID四大特性实现原理
通过alter table 来实现重建表,同事大呼开眼界了实习生疑问:为什么要在需要排序的字段上加索引呢?代码多版改造,应用责任链设计模式

参考文章:https://www.modb.pro/db/508155 版权申明:内容来源网络,仅供学习研究,版权归原创者所有。如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!

0 人点赞