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

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

引言

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

insert into table values (a1, b1)

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

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

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中。

责任编辑:华轩 来源: 架构精进之路
相关推荐

2023-12-18 16:07:15

2010-05-31 17:18:39

Cassandra数据

2011-08-04 18:00:47

SQLite数据库批量数据

2018-03-28 09:26:43

数据库缓存层优化

2013-09-22 10:25:23

MySQLSQL性能优化

2009-09-27 14:33:01

Hibernate批量

2010-11-29 13:17:00

Sybase批量操作

2020-11-23 10:50:27

MySQLSQL数据库

2018-08-09 08:59:56

数据库MySQL性能优化

2020-05-25 15:17:11

Python安装的库方法

2009-02-18 08:36:17

专家Windows优化

2010-06-01 12:51:23

MySQL数据库

2020-10-06 18:57:14

PostgreSQL数据库数据导入

2020-12-18 10:40:00

ExcelJava代码

2011-08-15 15:53:51

SQL Server数批量操作

2010-06-02 13:58:30

MySQL数据库性能

2010-05-31 16:17:56

MySQL数据库性能

2010-04-27 16:41:07

Oracle性能

2022-03-02 09:01:07

CPU使用率优化

2017-09-08 15:20:13

数据库MysqlGalera
点赞
收藏

51CTO技术栈公众号