基于Renascence架构的sqlite查询优化

sqlite查询优化方案是一开始是在Hw时设计的,但当时只实现一些简单case,并未完成sql的普遍支持。后面考虑到这可以做为 Renascence 架构的一个实验场景,因此将其方案做了一番修改,代码也重写了一遍,现在做成一个能支持普通sql查询的demo。

sqlite架构

参考:http://wiki.dzsc.com/info/7440.html
sqlite是移动设备广泛使用的轻量级数据库引擎。它主要由前端——虚拟机——后端三部分组成,这里不做详细介绍。

VM机制

参考:
http://www.cnblogs.com/hustcat/archive/2010/06/23/1762987.html
对于任何一条sql语句,sqlite都是先编译为一串虚拟机码(执行计划),然后基于虚拟机码执行程序:

如:
select * from TEST where age >20 and salary<99999666;
被翻译为如下的虚拟机码:

addr:0  opcode:Init p1:0    p2:16   p3:0    p4: p5:00   comment:
addr:1  opcode:OpenRead p1:0    p2:2    p3:0    p4:5    p5:00   comment:
addr:2  opcode:Rewind   p1:0    p2:14   p3:0    p4: p5:00   comment:
addr:3  opcode:Column   p1:0    p2:2    p3:1    p4: p5:00   comment:
addr:4  opcode:Le   p1:2    p2:13   p3:1    p4:(BINARY) p5:53   comment:
addr:5  opcode:Column   p1:0    p2:3    p3:3    p4: p5:00   comment:
addr:6  opcode:Ge   p1:4    p2:13   p3:3    p4:(BINARY) p5:53   comment:
addr:7  opcode:Column   p1:0    p2:0    p3:5    p4: p5:00   comment:
addr:8  opcode:Column   p1:0    p2:1    p3:6    p4: p5:00   comment:
addr:9  opcode:Copy p1:1    p2:7    p3:0    p4: p5:00   comment:
addr:10 opcode:Copy p1:3    p2:8    p3:0    p4: p5:00   comment:
addr:11 opcode:Column   p1:0    p2:4    p3:9    p4: p5:00   comment:
addr:12 opcode:ResultRow    p1:5    p2:5    p3:0    p4: p5:00   comment:
addr:13 opcode:Next p1:0    p2:3    p3:0    p4: p5:01   comment:
addr:14 opcode:Close    p1:0    p2:0    p3:0    p4: p5:00   comment:
addr:15 opcode:Halt p1:0    p2:0    p3:0    p4: p5:00   comment:
addr:16 opcode:Transaction  p1:0    p2:0    p3:3    p4:0    p5:01   comment:
addr:17 opcode:TableLock    p1:0    p2:2    p3:0    p4:TEST p5:00   comment:
addr:18 opcode:Integer  p1:20   p2:2    p3:0    p4: p5:00   comment:
addr:19 opcode:Integer  p1:99999666 p2:4    p3:0    p4: p5:00   comment:
addr:20 opcode:Goto p1:0    p2:1    p3:0    p4: p5:00   comment:

架构缺陷与优化思路

缺陷:
1、采用虚拟机模式(VM),且粒度为1行,switch case 太频繁,使CPU的cache命中率不高。
2、操作粒度太小,一列一列地取值操作,不利于进一步优化(GPU加速,多线程等)。
3、进行优化时,需要同时修改前端与后端,双重工作。

优化思路:
1、保留前端与后端,绕开VM机制,采用有向无环图(DAG)——算子的架构,每个算子次处理一批数据。
2、对于算子本身可作GPU/多线程优化加速

Renascence(Genetic-Program-Frame)介绍

Renascence是自动编程架构的框架库,一般而言它可视为一个特殊的编译器,算法库不直接提供对外API,而是将往Renascence中注册函数。上层应用或上层库调用算法库时,输入一条公式,然后 Renascence将公式编译为有向无环图,间接调用算法库。
Renascence的作用,优化上层应用调用下层引擎的逻辑,并起自适配作用。

1、优化调用逻辑,防止重复计算

新查询引擎的设计,主要便利用了Renascence这个功能。
比如如下的调用逻辑:f(g(x0), g(x0)),便可优化为

T节点表示转发,R节点表示传递,如是便省去g(x0)的第二次计算。

2、允许自动化参数调节

写程序,尤其是算法类程序时,调参数是不可避免的事情,如多线程优化中开启的线程数,开启多线程优化的阈值,使用OpenCL时device端缓存的大小,一次运算的globalsize, localsize,等等。
Renascence可提供一个框架,以公式链接起来的所有函数参数,会通过一个通一的方案调优。这个优化的目标,对查询引擎的优化而言,可以设为时间的快慢或内存占用的多少。
对sqlite查询优化这个场景而言,主要就是内存池一个BLOCK的大小,每次取多少行的数据,哈希表初始大小等等。

3、允许部分程序逻辑根据实际应用场景动态生成与调节

比如一个where条件 where A > 30 and B < 50 and C >100,且 A、B、C 三列均无索引
公式解释为:FILTER(x0, AND(AND(x1, x2), x3)),其中x0为源数据,x1、x2、x3是算符,分别表示 A>30、B<50和C>100
但由于注册等价匹配: FILTER(x0, AND(x1, x2)) = FILTER(FILTER(x0, x1), x2) = FILTER(FILTER(x0, x2), x1)
将会被转换为如下一系列等价形式:
FILTER(x0, AND(AND(x1, x2), x3)) = FILTER(FILTER(x0, AND(x1, x2)), x3) | FILTER(FILTER(x0, x3), AND(x1,x2)) =
FILTER(FILTER(FILTER(x0, x1), x2), x3) | FILTER(FILTER(FILTER(x0, x2), x1), x3)
FILTER(FILTER(FILTER(x0, x3), x1), x2) | FILTER(FILTER(FILTER(x0, x3), x2), x1)
这四个等价形式中选取哪一个,可以通过数据库采样实验(目标函数)动态决定。

2、3事实上是Renascence架构的核心,但对于数据库的查询引擎而言,程序逻辑相对固定,没有太多让Renascence发挥的空间,尽管如此,在Renascence架构之上实现CBO类似的优化还是会容易一些。
注:CBO 即 Cost Based Optimizer,为ORACLE的优化器,
参考:http://blog.csdn.net/suncrafted/article/details/4239237

查询引擎设计


图中 libGP.so 即 Renascence 库。
新引擎分为如下部分:

对接层

1、转换sqlite解析的语法树为 dag库所支持的格式
2、调用sqlite后端B-Tree的代码,获取数据源
3、将dag库返回的结果转换为sqlite的内存格式(Mem)

公式层

数据类型

在描述公式生成过程之前,先说明dag库的主要数据类型定义:
RawData:从对接层调用sqlite后端,读取出来的原始数据格式,sqlite是按行存储的,但会作一个序列化操作
FormalData:RawData中的数据,经过投影器解析(实际是反序列化),将所需要的列解出而得的数据
VirtualColumn:一列数据,由 FormalData 中复制或经表达式运算而得
VirtualColumnSet:列集,VirtualColumn 的集合

每个查询子句最终输出的是一个 VirtualColumnSet,而输入是一系列 RawData

语法分析与公式生成

我们先来看一下sqlite解析出来的语法树结构

如图所示,Select 结构包含的主要是表达式ExprList(由一系列Expr组成)和源SrcList(由一系列SrcList_item组成)
解析的核心思路就是依次解析从句,然后依次解析表达式提取数值。

SQL语句:select age, age*2, SNO from TEST where salary=99999666 order by SNO, SNAME; (salary 无索引)

Sort(MergeSet(MergeSet(SetAlloc(Column(Project(Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2)), x5), x6)), Op2(Column(Project(Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2)), x5), x6), Const(x8), x7)), Column(Project(Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2)), x5), x9)),MergeSet(SetAlloc(Column(Project(Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2)), x5), x9)), Column(Project(Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2)), x5), x10)), x11)

这个公式是如何生成的呢:
第一步,决定数据源
与这一步有关的子项是SrcList和where
查看 Select 结构体的内容可知,只有一个Source,且不是子查询,存在where条件条件,需要作过滤

得到的公式为:
Project(Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2)), x5)
为了避免解析不通过where条件的行,在第一步解析where子句所需的列,在过滤之后再解析其他所需要的列。

(当需要连接时,处理逻辑会复杂一些,如两个表内连接的对应公式为Join(Project(x0, x2),SortAndMask(Project(x1, x3),x4), x5),x0 是大表的 RawData,x1是小表的 RawData,本篇暂不详述)

第二步,解析Select的表达式组
解析表达式,直接按表达式树一层组织公式就好,如图所示的表达式树:
被解析为公式:
Op2(Column(x0, x1), Op2(Column(x0, x1), Const(x3), x2), x2)
Op2表示通用二目算子,x2为算符*
x0为 FormalData数据源
x3为 表达式 2,经过 Const 解析为常量 2
Column为取列算子,x1记录取的是age这一列
(其实这样解析并不太好,最好方式是每个表达式对应一个算子,但这样工作量很大,因此暂时这么做)

在解析时,Column对应的源,会被替换为第一步所得到的数据源,即Project(Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2)), x5)
因此公式中会重复出现这一段,显得所长。但后面 Renascence 作解析时,会去除冗余,因此不会影响性能。
每个表达式解析完后,输出是一个 列(VirtualColumn),需要用 SetAlloc 和 MergeSet 算子将其一个个合并为列集(VirtualColumnSet)

第三步,加前缀(GroupBy和AggFunc)
由于此语句无 GroupBy,不详细介绍

第四步,加后缀(Distinct、Sort、Limit)
这里只有一个Sort子句。
Sort算子输入是 Select 和 Order By 两个表达式组产生的列集,输出与 Select 表达式组的列集相同的一个列集。
因此解析 Order by 表达式集,将其合并,然后将其与 Select 列集一并输入 Sort 算子,即得出最终的列集。

经过 Renascence 优化之后,这个公式将被拆成如下片段计算:
Root: Sort(MergeSet(MergeSet(SetAlloc(x12), Op2(x12, Const(x8), x7)), x13), MergeSet(SetAlloc(x13), Column(x15, x10)), x11)
x12: Column(x15, x6)
x13: Column(x15, x9)
x14: Filter(x0, Op2(Column(Project(x0, x1), x3), Const(x4), x2))
x15: Project(x14, x5)

对应的有向无环图太复杂就不画了。

运行DAG并读取其输出结果

首先根据公式创建一个 ADF:

AGPProducer* producer = ProducerInstance::getInstance();
mADF = GP_Function_Create_ByFormula(producer, formula, NULL);

这个ADF的输入由两部分组成:参数与数据源
参数parameters在创建公式的过程中生成,数据源则在运行ADF时临时读取。

配置输入的代码:

int i=0; i<maxnumber; ++i)
{mInputs.push(NULL, NULL);
}
for (auto pair:parameters)
{HWASSERT(pair.first < mInputs.size());mInputs.contents[pair.first] = pair.second;
}
for (auto pair:sources)
{HWASSERT(pair.first < mInputs.size());mInputs.contents[pair.first].type = gHwOriginDataBatch;
}

运行的代码:

for (auto pair : mSources)
{auto batch = pair.second.produce(pair.second);mInputs.contents[pair.first].content = batch;
}
mOutput = GP_Function_Run(mADF, &mInputs);

算子层

1、基于RawData的运算:
投影(Project):作数据解析,将 RawData 需要提取的列取出来,生成 FormalData
过滤(Filter):作数据过滤,将 RawData 中不满足条件的行去掉。
2、基于列的运算:
Column:从FormalData复制一列数据出来
Const:将常数映射为一个数据列
Op(表达式):对数据列作运算,生成新数据列
3、基于列集的运算:
去重(Distinct):对列集进行去重,目前使用 stl的set实现,效率比较低。
排序(Sort):对列集进行排序
分组(GroupBy):实际上是在排序的基础额外做一个标志,把相同的行合并。
这部分算子是最耗时间的,最需要重点优化
4、列转换为列集的算子:SetAlloc、MergeSet
SetAlloc:生成一个列集
MergeSet:往列集中加一个列

执行的过程如图所示:

性能测试结果

小米pad上数据,TEST表数据量为91W
TEST2表行数 20W
时间为运行5次的总时间:

语句 优化前时间/ms 优化后时间/ms 性能提升率%
select * from TEST where age >20 and salary<99999666 14252.2 12389.5 15.0
select * from TEST where age >20 13416.2 11889.1 12.8
select age, SNO, SNAME from TEST order by SNO, SNAME 30262.1 22727.2 33.2
select age*2, SNO, SNAME from TEST order by SNO, SNAME 31254.6 24319.0 28.5
select * from TEST where SNO in (select SNO from TEST2) 23383.9 19860.9 17.7
select * from TEST where SNO in (‘aa’, ‘bb’, ‘cc’) 2697.3 1914.26 40.9
select * from TEST2 3316.53 3061.97 8.3
select TEST.age, TEST.SNO, TEST.salary, TEST2.SNAME from TEST JOIN TEST2 on (TEST.salary=TEST2.SALARYDD) 50112.1 42631.2 17.5
select sno, SNAME, sum(age+salary), count(age) from TEST group by SNO, SNAME 31813.3 25966.2 22.5
select count(sno), sum(age) from TEST 3167.34 2830.28 11.9

Renascence并行机制实现

(2016.7.15修改)

Renascence并行机制

并行与代码优化的解耦

当前的并行计算框架,如多线程的openMP、大数据计算的hadoop-map-reduce、spark,都需要做算法代码本身做改造。算法并行实现后的性能、效果跟改造的情况强相关,且进一步优化受限于平台引擎(比如基于spark开发的代码,再用simd、GPGPU加速就很困难)。

而Renascence的并行计算框架,意图将代码优化与并行计算的实现解耦,以并行脚本代替算法代码的并行化改造,用并行计算引擎去解析脚本,实现多线程/分布式的并行计算。这样,代码的优化就不依赖于并行计算的平台,可以同步进行。

这个并行计算框架实现的是比较粗粒度的并行,而GPGPU、SIMD加速中的并行是数据并行,可认为是对代码本身的优化。

并行语言规范

参见
https://github.com/jxt1234/Renascence/blob/master/doc/Parallel.txt

文件组成

如下是一套完整的并行框架文件组成示例
so文件:
libGP.so:Renascence框架库
libmthpGP.so:多线程并行引擎库
libpics.so:图像算法库

xml配置文件:
func.xml:图像算法列表
Map-Reduce.xml:并行化语言描述
mgpfunc.xml:并行计算引擎的函数列表

并行化实现结果

在sql查询引擎中,排序的并行描述为

REDUCE((MAP((x0,x1,x2), Sort(x0,x1,x2), [a0,b0,c0]->[a0], a0==b0),x1,x2), MergeSort(x0, y0, x1, x2), [a0,b0,c0]->[1], a0==b0)

Sort 对一个数组进行排序,使之有序。
MergeSort对两个有序数组进行归并,合并成一个有序数组。
这个描述表示:对所有数组执行一次Sort,使各自成为有序数据,然后进行两两合并,执行MergeSort,最终合成一个大的有序数据。

如下为引入 GP并行机制后,最新的数据(其他算子未完全支持):

语句 优化前时间/ms 优化后时间/ms 性能提升率%
select age, SNO, SNAME from TEST order by SNO, SNAME 29418.7 16990.7 73.1
select age*2, SNO, SNAME from TEST order by SNO, SNAME 30823.2 20118.2 53.2

基于Renascence架构的查询引擎设计思想

依赖Renascence组织调用逻辑

查询引擎分为公式层与算子层,公式层生成一个模糊的公式,Renascence生成适合的程序逻辑(这里是有向无环图DAG),调用算子层进行计算。

性能优化集中于算子/执行层面

查询引擎的性能优化只需要优化算子,逻辑性的优化由Renascence完成。

代码不便公开,一些细节也未完全描述,关于并行化计算可参考Renascence中的文档。

基于Renascence架构的SQL查询引擎设计相关推荐

  1. 分布式大数据sql查询引擎Presto初识

    1.背景: Hive使用MapReduce作为底层计算框架,是专为批处理设计的.但随着数据越来越多,使用Hive进行一个简单的数据查询可能要花费几分到几小时,显然不能满足交互式查询的需求.Presto ...

  2. 【360开源】Quicksql——更简单,更安全,更快速的跨数据源统一SQL查询引擎

    话说天下大势,分久必合,合久必分. --罗贯中大大 前言 SQL,全称Structured Query Language,是当今使用最广泛的数据查询语言.最初的设计仅仅是适用于RDBMS,可是随着数据 ...

  3. 2020-12-03《Presto分布式SQL查询引擎——kkb笔记复习》

    Presto分布式SQL查询引擎 一.课前准备 jdk版本要求:Java 8 Update 151 or higher (8u151+), 64-bit 安装好hadoop集群 安装好hive 二.课 ...

  4. 数据仓库(六)---分布式SQL查询引擎---presto介绍

    我们在之前的文章中已经学习了数据仓库hive,如果要对数据仓库进行交互查询,则需要交互查询的引擎用于提高查询效率.本章介绍presto. 简介 Presto是一个开源的分布式SQL查询引擎,适用于实时 ...

  5. Presto 分布式SQL查询引擎

    文章目录 Presto的介绍 Presto的数据模型 通过Presto客户端查询Presto命令 Presto的架构 Java程序访问Presto Presto注意点 Presto的介绍 Presto ...

  6. bs架构 mysql_基于BS架构OA办公系统的设计(PHP,MySQL)(三人组)(含录像)

    基于BS架构OA办公系统的设计(,MySQL)(三人组)(含录像)(毕业论文13000字,程序代码,MySQL数据库,答辩PPT) 系统模块划分 企业部门管理系统(以管理员部分为核心)从功能上划分为5 ...

  7. .sql文件_自己动手写SQL查询引擎

    自己动手写SQL查询引擎 起因 笔者在实际工作中经常需要解析文件,每次文件稍有变化,都得拷贝粘贴一堆代码. 于是就想着能不能做一个通用的服务,通过配置的方式解析文件. 配置通用 最通用的方法就是自己定 ...

  8. 基于 RICS-V 架构的单周期处理器设计(含所有格式指令)—— 逻辑部件概述

    文章目录 一.概述 二.原理图 2.1 ALU 2.1.1 ALU 操作控制信号生成部件 2.1.2 ALU 逻辑计算部件 2.1.3 ALU 整体逻辑 2.2 寄存器 2.2.1 Reg 整体逻辑 ...

  9. java 分布式sql引擎_分布式SQL查询引擎 Presto 性能调优的五大技巧

    概述 Presto架构 Presto是一个分布式的查询引擎,本身并不存储数据,但是可以接入多种数据源,并且支持跨数据源的级联查询. Presto的架构分为: Coodinator:解析SQL语句,生成 ...

  10. 大数据的分布式SQL查询引擎 -- Presto的详细使用

    Presto – Distributed SQL Query Engine for Big Data 官网 项目源码 官方文档 目录 1 Presto 概述 2 概念 2.1 服务进程 2.2 数据源 ...

最新文章

  1. Android之对Volley网络框架的一些理解
  2. 从输入字段读取属性时,HTML编码丢失
  3. 解决Visual SVN在linux下不能访问的问题
  4. span标签style的优先级_css样式引入优先级?
  5. 【火爆全网,好评如潮】DS100手持示波器现货发售拉,附原子哥发布会详细讲解视频以及宣传视频...
  6. ISBN号码【模拟】
  7. linux 密码修改下次,问题:如何强制用户在下次登录Linux时更改密码
  8. mac下打开多个相同应用程序
  9. 关于 rm -rf * 你需要知道的
  10. 如何成为一个优秀的C++开发工程师?
  11. 网络安全实验室 脚本关 解析
  12. java 工作流框架_java工作流是什么?哪些工作流框架比较好?
  13. 动态规划——状态转移方程
  14. 路由器(无线桥接)中继模式遇到无法上网的解决方案
  15. journey、voyage、trip、tour、travel的用法区别
  16. referenced libraries
  17. 2019年第十届蓝桥杯C/C++ 省赛B组真题+题解
  18. 交流电源和直流电源有什么区别?
  19. Simultaneous Feature Learning and Hash Coding with Deep Neural Networks
  20. Storm Trident API实践

热门文章

  1. php微信公众号群发,php实现微信公众号无限群发,php信公众群发
  2. 模板消息php 群发,微信公众号模板消息群发php代码示例
  3. Ubuntu20.04安装教程
  4. 视频编解码器讲解-H.264、VP9、HEVC、AV1
  5. mysql 唯一索引为null_mysql 唯一索引与null.md
  6. 磁性能测量-软磁材料的退磁方法
  7. 微信小程序下拉刷新真机没效果_微信小程序下拉刷新上拉加载的两种实现方法...
  8. kux文件怎么打开 苹果手机如何观看kux视频
  9. 微信小程序上传视频功能的简单实现
  10. 淘宝直通车怎样设置定向推广出价问题总结