TPC-C解析系列03_TPC-C基准测试之SQL优化

TPC-C是一个非常严苛的基准测试模型,考验的是一个完备的关系数据库系统全链路的能力。这也是为什么在TPC-C的榜单前列,出现的永远只是大家熟知的那几家在业界有着几十年积累、从关系数据库理论开始发展就差不多同步出现的数据库公司。接下来我们通过这篇文章为您分析在TPC-C测试中OceanBase数据库的SQL模块具体遇到了哪些挑战、做出了哪些优化。

背景

对TPC-C有所了解人都知道,TPC-C是一个典型的OLTP (On-Line Transaction Processing) 场景测试,考察的是数据库在高并发压力场景下的事务处理能力,最终的性能指标以tpmC(transaction per minute,也即每分钟系统处理TPC-C模型中的new order事务的数量)和平均到每tpmC的系统成本作为衡量标准。在OLTP场景中,每条请求的响应时间都是极短的。因此,各个数据库厂商在进行TPC-C测试时,都会尽一切可能将每一个操作时间压缩到最短,不夸张的说,在TPC-C的测试中,一些关键操作的优化往往需要细化到CPU指令级。

在进入我们的主题前,我们先来谈谈TPC-C中的事务模型,主要分为五种事务,订单创建、订单支付、订单查询、订单发货以及库存查询,这五种事务按照一定的比例发生,测试最终衡量的是每分钟订单创建事务的执行个数。大家知道,每一个数据库的事务,其实就是由一定逻辑关系关联的若干条SQL语句组成,他们在一个事务中,要么全部成功,要么全部失败,这个在数据库中称为“原子性”,也就是ACID中的“A”。那么TPC-C中的一个事务的耗时大约是多久呢?看一下报告就很清楚了——只有十几个毫秒。考虑到一个事务由多条SQL构成,那么每一条SQL的平均耗时都不到1毫秒!

在C/S(client-server)模型有中,一条SQL语句从发起到执行完成需要经历从客户端输入、网络传输、SQL优化、执行、结果返回到客户端这样一个流程。而具体每一条SQL的执行可能只是做一个字段的更新,所需要的执行时间是非常短暂的,从整个链路的角度来看,大量的时间会花费在与客户端的交互过程中,造成资源的浪费和耗时的增加。那么如何解决这个问题的呢?答案就是使用存储过程。

存储过程

所谓“存储过程”就是数据库为用户提供的一种面向过程的编程语言。基于这种语言,用户可以将应用程序的逻辑封装为一个可调用的过程(procedure)存放在数据库中并随时进行调用。通过这种方式,用户可以将本来需要与数据库进行多次交互才能完成的工作通过一次交互完成,省去了中间网络的传输和等待时间(参见图1)。假如一条事务的网络开销平均是30%,也就是说30%的CPU都花在了网络的收发和解析上。那么在6千万规模tpmC测试中节省下来30%的CPU资源换算成系统处理能力是惊人的。使用存储过程还可以带来事务响应时间的下降,导致数据库内核中事务锁的临界区缩短,间接的提升了系统CPU利用率,整个吞吐量也随之提高。存储过程在缩短应用端的等待耗时上同样有很大作用。

在TPC-C中,存储过程对于整个系统的执行效率提升是至关重要的。OceanBase 的2.2版本不仅全面支持了存储过程,而且对存储过程的执行效率做了大量极致的优化。

编译执行

存储过程作为一种面向过程的高级语言,需要转换成机器码才能够执行。这个过程一般可以分为“编译执行”和“解释执行”两种,一般来说,编译执行相比解释执行有代码优化充分、执行效率高等特点。OceanBase利用近两年逐渐成熟的LLVM编译器框架实现了一个支持存储过程的编译器,通过动态编译(Just-in-Time Compilation)的方式将存储过程翻译成高效的二进制可执行代码,在执行效率上获得了数量级的提升。同时,过程中LLVM框架将存储过程转换为与机器无关的中间代码,使得存储过程也自然而然地获得了跨平台的编译执行能力,LLVM内置的优化过程确保我们在各种不同的硬件平台上都可以获得正确、高效的可执行代码。

Array Binding

另外一个在TPC-C测试中发挥了重要作用的功能就是对DML语句进行批量处理的能力,在Oracle中该功能也称为“Array Binding”。一条SQL在数据库中的执行过程大致上可以分为“计划生成”和“执行”两个阶段。尽管我们对SQL的执行计划做了高速缓存,但找到一个合适的执行计划在整个执行过程中仍然是比较耗时的一个部分。那有没有办法省去这个时间呢?当一组SQL的执行计划完全一样而只有执行参数不同时,在存储过程中我们可以通过特定的语法将他们的执行做成一个批量处理的过程,此时“计划生成”只需要做一次即可,这就是所谓的“Array Binding”。

在Array Binding中,数据库会首先找到需要使用的计划,然后执行该计划,并在每次执行完毕后,重新执行参数绑定(binding)的过程。打个比方,这就像是在一个C语言的for循环中,反复赋值而不是重新定义一个数据结构。Array Binding的使用受用户控制,需要在存储过程中使用FORALL关键字来触发这一功能,在TPC-C的测试过程中,我们多次使用了Array Binding来提升系统的处理能力,效果非常明显。

Prepared Statement与执行计划缓存

Prepared Statement是一种二进制的请求交互协议,可以大大降低系统的交互成本。OceanBase不仅支持用户程序与数据库间使用Prepared Statement, 也支持在存储过程引擎调用SQL引擎执行时使用这种交互方式。存储过程在对SQL进行一次Prepare操作并获取唯一id后, 后续的每次执行仅需要传入该id和对应的参数,系统可以通过高速缓存找到对应的存储过程或SQL计划开始执行。该过程相比使用SQL文本的交互方式,省去了大量请求文本解析的CPU开销。


OceanBase内部实现了高速缓存来缓存存储过程的可执行代码及SQL执行计划, 不同参数的存储过程和SQL可以通过这一高速缓存快速获取需要的执行对象, 耗时一般在几十微秒以内, 有效避免了重新编译带来的毫秒级的延迟和CPU消耗。

可更新视图

在OLTP场景中,通过减少应用与数据库的交互次数来实现性能提升的例子很多,可更新视图就是其中之一。我们常见的数据库视图通常是只读的,通过定义视图,用户可以定义自己感兴趣的数据以及其获取接口,但视图同时也可以作为更新操作的入口,比如在TPC-C的new order创建场景中,应用需要得到商品信息,更新库存并得到更新后的值。一般可以通过两条SQL实现这一过程:

    select i_price, i_name, i_data from item where i_id = ?;

    UPDATE stock

      SET s_order_cnt = s_order_cnt + 1,

          s_ytd = s_ytd + ?,

          s_remote_cnt = s_remote_cnt + ?,

          s_quantity = (CASE WHEN s_quantity < ? + 10 THEN s_quantity + 91 ELSE s_quantity END) – ?

      WHERE s_i_id = ?

          AND s_w_id = ?

      RETURNING s_quantity, s_dist_01,

          CASE WHEN i_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE (CASE WHEN s_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE ‘B’ END) END

      BULK COLLECT INTO …;

但通过建立一个可更新视图:

    CREATE VIEW stock_item AS

      SELECT i_price, i_name, i_data, s_i_id, s_w_id, s_order_cnt, s_ytd, s_remote_cnt, s_quantity, s_data, s_dist_01

      FROM stock s, item i WHERE s.s_i_id = i.i_id;

我们就可以通过一条语句更新库存并得到商品和库存信息:

    UPDATE stock_item

      SET s_order_cnt = s_order_cnt + 1,

          s_ytd = s_ytd + ?,

          s_remote_cnt = s_remote_cnt + ?,

          s_quantity = (CASE WHEN s_quantity < ? + 10 THEN s_quantity + 91 ELSE s_quantity END) – ?

      WHERE s_i_id = ?

          AND s_w_id = ?

      RETURNING i_price, i_name, s_quantity, s_dist_01,

          CASE WHEN i_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE (CASE WHEN s_data NOT LIKE ‘%ORIGINAL%’ THEN ‘G’ ELSE ‘B’ END) END

      BULK COLLECT INTO …;

这样就省去了一条语句的交互,并且更新逻辑更加直观。可更新视图允许用户可以像普通表一样操作视图,但不是所有视图都可以定义为可更新视图。比如带distinct, group by的视图,具体更新哪些行语义是不明确的,因此不能允许更新。具体到上面的stock_item两表join的视图,需要满足所更新表的unique key在join之后保持unique (key-preserved table),即item.i_id必须是唯一的这个前提。

需要强调,TPC-C规范禁止使用物化视图,而可更新视图并没有改变底层数据表格的存储形式,是符合规范的。

总结

因为TPC-C的设计原则是尽可能的“真实”反应一个OLTP系统的运行场景,我们所做的很多优化都具有广泛的适用性。例如,对于一个高并发的OLTP系统来说,大部分的SQL请求的耗时是非常短的,采用纯粹的C/S交互模型的后果必然使系统的时间浪费在应用与数据库的频繁交互中,而使用存储过程可以大大缓解这种交互的耗时,并且增强系统对于网络抖动的免疫力,这种核心能力对于一个分布式OLTP数据库是不可或缺的。

OceanBase从创立伊始就坚持走自主研发的道路,这个选择确保了我们对数据库内核有着完全的掌控能力,让我们有在任何场景下追求极致性能的底气和实力的同时,也对产品形态的发展方向有更清晰的规划和目标。在这次的TPC-C测试中,我们采用了OceanBase 2.0版本开始支持的Oracle兼容模式,存储过程和SQL全部使用了兼容Oracle的数据类型和语法,这样做也是为了在追求极致优化的同时,确保产品迭代可以沿着通用和正规的方向发展。从OceanBase 2.0版本开始,OceanBase就不断朝着Oracle兼容这个大的目标前进,随着2.2版本支持的存储过程(PL/SQL)功能的完善,我们的产品功能也完成了一轮新的迭代。我们坚信这次的TPC-C测试结果不仅仅见证了OceanBase的极致性能,也将成为OceanBase数据库走向成熟产品的一个新起点。

作者介绍:

  • 陈萌萌:现任蚂蚁金服OceanBase团队资深技术专家,负责OceanBase SQL方向的研发工作。2006年毕业于清华大学,2006年到2008年在欧洲核子研究中心(CERN)负责网格计算调度器的开发工作,2009年在美国威斯康星大学麦迪逊分校获得计算机硕士学位,先后在Oracle、华为美国研究所从事数据库的开发和研究。
  • 潘毅:现任蚂蚁金服OceanBase团队资深技术专家,负责OceanBase的并行查询和新一代OLAP引擎。曾就职于美国Oracle公司,负责Oracle数据库并行查询研发工作并有多项专利申请。
Image placeholder
Geek彬
未设置
  62人点赞

没有讨论,发表一下自己的看法吧

推荐文章
TPC-C解析系列05_TPC-C基准测试之存储优化

TPC-C规范要求被测数据库的性能(tpmC)与数据量成正比。TPC-C的基本数据单元是仓库(warehouse),每个仓库的数据量通常在70MB左右(与具体实现有关)。TPC-C规定每个仓库所获得的

TPC-C解析系列04_TPC-C基准测试之数据库事务引擎的挑战

OceanBase这次TPC-C测试与榜单上Oracle和DB2等其他数据库在硬件使用上有非常大的不同,OceanBase的数据库服务器使用的是204+3台型号是ecs.i2.16xlarge阿里云E

TPC-C解析系列01_TPC-C benchmark测试介绍

作者:阳振坤2019.10导语:自从蚂蚁金服自研数据库OceanBase获得TPC-C测试第一名后,引起了行业内外大量关注,我们衷心的感谢大家对OceanBase的支持与厚爱,也虚心听取外界的意见和建

TPC-C基准测试之链路层优化

作者:易鸿伟闫建良王光树在TPC-C标准定义中,测试系统分为RTE(RemoteTerminalEmulator)和SUT两部分。在实际的TPC-C测试流程中,不只是对DB端能力的考验,对链路中的所有

TPC-C解析系列02_OceanBase如何做TPC-C测试

导语:蚂蚁金服自研数据库OceanBase登顶TPC-C引起业内广泛关注,为了更清楚的展示其中的技术细节,我们特意邀请OceanBase核心研发人员对本次测试进行技术解读,共包括五篇:1)TPC-C基

ASC19最新战况:北航打破HPL基准测试赛会纪录!

  今天(4月23日)是全球瞩目的ASC19全球总决赛第三天,经过前两天的系统搭建与调试工作后,20支队伍进入了正式竞赛阶段,向总冠军宝座发起最后冲刺。  根据赛程,今天所有参赛队伍需要完成的项目包括

MySQL优化之覆盖索引的使用

查看测试表结构:mysql>showcreatetableim_message\G ***************************1.row**************************

效率提高N倍的19条MySQL优化秘籍

一、EXPLAIN做MySQL优化,我们要善用 EXPLAIN 查看SQL执行计划。下面来个简单的示例,标注(1,2,3,4,5)我们要重点关注的数据type列,连接类型。一个好的sql语句至少要达到

SQL优化案例-定位系统中大量的rollback(十八)

系统中logfilesync比较严重,查看存储都没有问题,logfileparallelwrite很低,时间分布直方图也没问题数据库中提交和回滚操作比较频繁,每秒1000多次,rollback占比1/

【数据结构】3_程序设计的灵魂

学员间的对话 木暮:我发现三井真是牛,只用一行就实现了strlen 宫城:那么强!他是怎么做的呢? 木暮:不知道,我看了一下,没看懂。。。 宫城:牛人就是牛人啊! 问题:程序是否越短越好?是否别人看不

数据库中间件分片算法之stringhash

前言又是一个夜黑风高的晚上,带上无线耳机听一曲。突然很感慨一句话:生活就像心电图,一帆风顺就证明你挂了。就如同我们干运维的,觉得很简单的事情,有时候能干出无限可能。还是言归正传吧,这一次我们来说说st

敏捷开发流程之Scrum:3个角色、5个会议、12原则

本文主要从Scrum的定义和目的、敏捷宣言、Scrum中的人员角色、Scrum开发流程、敏捷的12原则等几方面帮助大家理解Scrum敏捷开发的全过程。一、Scrum的定义和目的Scrum是一个用于开发

Nebula 架构剖析系列(二)图数据库的查询引擎设计

摘要上文(存储篇)说到数据库重要的两部分为存储和计算,本篇内容为你解读图数据库Nebula在查询引擎QueryEngine方面的设计实践。在Nebula中,QueryEngine是用来处理Nebula

leveldb源代码分析系列1:MemTable的实现

MemTable及其实现这是一个第零层的主题,预计扩展如下第一层主题:1.1comparator介绍1.2skiplist实现介绍1.3数据压缩相关介绍1.4Put流程1.5Get流程leveldb中

leveldb源代码分析系列1.1:memtable中comparator的实现

leveldb中memtable封装了一个skiplist用来存储真正的数据,跳跃列表的实现一定需要定义存储项的序关系,而在leveldb中这个序关系通过comparator相关类来实现。leveld

leveldb源代码分析系列1.2:skiplist实现

skiplist的实现介绍leveldb中的SkipList是一个模板类,其模板参数的类型分别是存储的Key类型和Comparator类型。虽然名字是Key类型,但其实存储了整个entry,只不过Co

MySQL 性能优化:8 种常见 SQL 错误用法!

1、LIMIT语句分页查询是最常用的场景之一,但也通常也是最容易出问题的地方。比如对于下面简单的语句,一般DBA想到的办法是在type,name,create_time字段上加组合索引。这样条件排序都

MySQL 亿级数据数据库优化方案测试-银行交易流水记录的查询

作者:逸宸a链接:https://www.jianshu.com/p/cbdef47fb837对MySQL的性能和亿级数据的处理方法思考,以及分库分表到底该如何做,在什么场景比较合适?比如银行交易流水

S.O.L.I.D: PHP 面向对象设计的五个基准原则

S.O.L.I.D是首个5个面向对象设计(OOD)准则的首字母缩写,这些准则是由RobertC.Martin提出的,他更为人所熟知的名字是UncleBob。 这些准则使得开发出易扩展、可维护的软件变

我是怎么倒在美团第三轮面试之下的

说实话,之前我从来没想过能有大厂会给我机会让我去面试,一个专科生,不是科班出身,这些硬性条件好像都没有一个符合的,但是却获得了美团的面试机会。很庆幸自己获得那次机会,也让我深刻体会到了面试大厂,到底需

使用BCC工具分析系统性能

系统管理员可以通过利用BCC(BPFCompilerCollection)库的工具来分析操作系统性能和获取操作系统信息。BCC介绍BCC工具全称BPFCompilerCollection(BCC),是

清华退学王垠 VS 阿里P10 赵海平,这场面试之战到底谁赢了?

知乎上有一个话题:如何评价阿里P10赵海平对王垠的面试?最近成了热帖,这下赵海平跟王垠这次是真的火了。由于本事件像电影的情节一环扣一环,文章比较长,直接一句话说下该事件始末:网红王垠受邀面试阿里P9岗

ES6系列之箭头函数全解析

引言ES6中允许使用箭头=>来定义箭头函数,是ES6中较受欢迎也较常使用的新增特性。本文将从箭头函数的基本语法,与普通函数对比,箭头函数不适用场景三个方面进行梳理。基本语法//箭头函数 letfunc

SQL 查询语句的执行顺序解析

代理搭建-shadowsockshttps://www.jetbrains.com/shop/eform/opens...https://blog.csdn.net/lianghecai5217131

用 Go 构建一个 SQL 解析器

在本文中,小编将向大家简单介绍如何在Go中构造LL(1)解析器,并应用于解析SQL查询。希望大家能用Go对简单的解析器算法有一个了解和简单应用。摘要本文旨在简单介绍如何在Go中构造LL(1)解析器,在