内容简介:【摘要】当数据量巨大时,使用大批量随机键值集获取对应记录集合,不仅仅考验数据库软件本身,更在于程序员对数据的理解!如何在硬件资源有限的情况下将性能发挥到极致?点击:本次测试主要针对集算器组表索引实现的批量键值取数性能,并与 Oracle 进行同规模运算对比。
【摘要】
当数据量巨大时,使用大批量随机键值集获取对应记录集合,不仅仅考验数据库软件本身,更在于 程序员 对数据的理解!如何在硬件资源有限的情况下将性能发挥到极致?点击: 批量随机键值查询测试 ,来乾学院一探究竟!
本次测试主要针对集算器组表索引实现的批量键值取数性能,并与 Oracle 进行同规模运算对比。
一、测试环境
二、数据描述
2.1数据结构
2.2数据规模
按以上数据结构,造出 6 亿条记录的行存组表文件和对应的索引文件:
三、测试过程
3.1生成测试文件
3.1.1 建组表
A1:包含 26 个英文字母和 10 个阿拉伯数字的字符串。
A2、A3:建立结构为 (id,data) 的组表文件,@r 选项表示使用行式存储方式。
A4:循环 6000 次,循环体B4、B5,每次生成 10 万条对应结构的记录,并追加到组表文件。
执行后,生成组表文件:id_600m.ctx
3.1.2 建索引
A2:根据组表文件的 id 列,建立组表索引。
执行后,生成组表的索引文件:id_600m.ctx__id_idx
3.2查询测试
A2:循环一万次,每次获取对应组表文件 id 列中的随机一个,并排序。(可能会有少量重复值,但对测试影响不大)
A4:在组表的 icursor()这个函数中,使用索引 id_idx,以条件 A2.contain(id) 来过滤组表。集算器会自动识别出 A2.contain(id) 这个条件可以使用索引,并会自动将 A2 的内容 排序 后从前向后查找。
3.3奇怪的现象
原本希望多次执行后,求得一个平均值作为测试结果。但是发现每执行完毕一次该测试代码,都会比上一次执行快一些,这里列出从第一次执行该代码后的 5 次测试查询耗时:
手动一次次点击设计器中的执行按钮,并记录下查询耗时,太费劲了。为了找出规律,将代码改为以下形式:
B7:将循环体中 icursor() 函数每一次查询的耗时,在 A1 中追加记录下来。
执行过程中,观察 A1 中新追加的查询耗时与上一次的比较,发现经过大约 350 次循环后接近极限值 25 秒。再后续近千次循环中,查询耗时也都是如此,基本稳定。
难道是集算器对数据进行了缓存?抱着怀疑的态度,重启了集算器设计器,再次执行了查询代码。发现重启后第一次的查询耗时也是 25 秒。这样看来提速的原因和集算器本身并没有什么直接的关系了。
另一方面,可以想到基于目前测试的数据量,能够在短时间内完成查询,部分数据可能已经装载至内存,那么很可能是 linux 操作系统的文件缓存造成了这个现象。重启服务器后,再通过集算器设计器来执行查询,发现耗时又开始从 80 秒左右慢慢减少了。
进一步的测试中,使用了 linux 的 free 命令查看系统内存使用情况。发现每完成一次组表的查询,其中的 cached 一项就会变大。而随着 cached 慢慢的变大,查询的耗时又逐步减少。
3.4index@3的使用
在网络上查询了一些资料,了解到 Linux 会存在缓存内存,通常叫做 Cache Memory。就是之前使用 free 命令看到其中的 cached 一项,执行 free -h:
当我们读写文件的时候,Linux 内核为了提高读写效率与速度,会将文件在内存中进行缓存,这部分内存就是 Cache Memory(缓存内存)。即使我们的程序运行结束后,Cache Memory 也不会自动释放。这就会导致我们在 Linux 系统中程序频繁读写文件后,我们会发现可用物理内存会很少。其实这个缓存内存在我们需要使用内存的时候会自动释放,所以我们不必担心没有内存可用。并且手动去释放 Cache Memory 也是有办法的,但此处不再详细探讨。
这个函数涉及数据量有 111G,比机器的物理内存 64G 更大,显然不可能把所有数据都缓存到内存中,那么到底缓存了哪些数据后就能稳定地提高查询性能呢?是不是可以事先就把需要这些数据先缓存起来以获得高性能?请教了高手后,发现果然还有选项可以来预先缓存索引的索引。在使用 icursor()函数查询之前,对组表索引使用了 T.index@2(idx) 使用了 T.index@3(idx)。代码如下:
集算器的索引有个分级缓存,@3 的意思是将索引的第三级缓存先加载进内存。经过 index@3 预处理,第一遍查询时间也能达到上面查询数百次后才能达到的极限值。
四、与 Oracle 对比
测试环境、数据结构和规模与上文一致,测试对象如下:
Oracle建表语句为:
create table ctx_600m (id number(13),data varchar2(200));
数据由集算器生成同结构的文本文件后,使用 Oracle 的 SqlLoader 导入表中。
Oracle建索引语句为:
create unique index idx_id_600m on ctx_600m(id);
使用 Oracle 进行批量随机取数测试时,我们使用这样的 SQL:
select * from ctx_600m where id in (…)
使用单线程连接 Oracle 进行查询的集算器脚本为:
由于 oracle 的 in 个数有限制,脚本中进行分批执行后合并。
使用 10 线程连接 Oracle 进行查询的集算器脚本为:
使用单线程对行存组表进行查询的集算器脚本为:
使用 10 线程对行存组表进行查询的集算器脚本为:
从 6 亿条数据总量中取 1 万条批量随机键值,在都建立索引的测试结果:
五、列存索引测试
集算器列存采用了数据分块并压缩的算法,这样对于遍历运算来讲,访问数据量会变小,也就会具有更好的性能。但对于基于索引随机取数的场景,由于要有额外的解压过程,而且每次取数都会针对整个分块,运算复杂度会高很多。因此,从原理上分析,这时候的性能应当会比行存要差。
上述代码中把生成组表的 create() 函数不用 @r 选项,即可生成列存文件。重复上面的运算,单线程情况下 6 亿行中取 1 万行耗时为 129120 毫秒,比行存方式慢了 6 倍多。不过平均到一行也只有 13 毫秒,对于大多数单条取数的场景仍然有足够的实用性。
同一份数据不能在遍历运算和随机取数这两方面都达到最优性能,在实际应用中就要根据需求做一下取舍了,一定要追求各种运算的极限性能时,可能就要把数据冗余多份了。
六、索引冗余机制
集算器确实也提供了冗余索引机制,可以用于提高列存数据的随机访问性能,代码如下:
在对组表建立索引时,当 index 函数有数据列名参数,如本例 A2 中的 data,就会在建索引时把数据列 data 复制进索引。当有多个数据列时,可以写为:index(id_idx;id;data1,data2,…)
因为在索引中做了冗余,索引文件也自然会较大,本文中测试的列存组表和索引冗余后的文件大小为:
当数据复制进索引后,实际上读取时不再访问原数据文件了。
从 6 亿条数据总量中取 1 万条批量随机键值,完整的测试结果对比:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- SQL Server中INNER JOIN与子查询IN的性能测试
- MySQL亿级数据数据库优化方案测试-银行交易流水记录的查询
- Mybatis关联查询(嵌套查询)
- MySQL高级查询---连接查询实例
- Oracle子查询相关内容(包含TOP-N查询和分页查询)
- Laravel Query Builder 复杂查询案例:子查询实现分区查询 partition by
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Paradigms of Artificial Intelligence Programming
Peter Norvig / Morgan Kaufmann / 1991-10-01 / USD 77.95
Paradigms of AI Programming is the first text to teach advanced Common Lisp techniques in the context of building major AI systems. By reconstructing authentic, complex AI programs using state-of-the-......一起来看看 《Paradigms of Artificial Intelligence Programming》 这本书的介绍吧!