Stardog 2.1:并发性、内存管理、GC机制带来的扩展性提升

即将发布的 Stardog 2.1的查询可扩展性提高了约3个数量级,可以在一个1万美元的服务器上处理500亿个triple。我们从来没有过于关注Stardog可扩展性本身:我们首先考虑它的易用性,然后考虑它的速度。我们也只是假定会使它极具可扩展性。Stardog 2.1使查询、数据加载和可扩展性取得了巨大飞跃。

在一个1万美元的服务器硬件(32个内核,256 GB的RAM)上运行Stardog 2.1可以处理200到500亿个triple。相比Stardog 2.0.x,Stardog 2.1加载1亿(左右)triple数据集快了约两倍;加载大约10亿triple数据集快了约3倍——同时占用更少的内存。2.1版本可以以每秒30万个triple的速度加载20B数据集。我们还大大地改进了查询评价的性能,使Stardog 2.1即便在更大的数据库中仍能高速运行。我们是如何做到的呢?

增强并发性

性能的提升大多源于考虑并发性和减少线程争用,尤其是在加载批量数据中(在初始数据库创建时伴随着大量的数据被添加)。我们避免更多的锁,使用更多的非阻塞算法和更多的数据结构。例如从BitSet到ConcurrentLinkedQueue的转变,尽管前者比后者有更多的空间效率,我们还是更愿意使用ThreadLocals减少线程争用和避免同步。当加载性能有所改善时,因为回收进程被附件填满,多个LRU缓存变得不确定了。在单个线程中批处理回收有效果,但也会增加内存压力和GC时间。

糟糕的哈希算法反而更好

Stardog进行哈希处理Uri、bnodes和文本值以存储在dictionary映射中;以前我们用64位 MurmurHash,因为它是非常快、冲突率低,允许我们将数值存储为长整形。当处理冲突和缓存缺失时,需要磁盘的访问权限;在一定规模上访问这些随机磁盘太昂贵了。转移到SHA1也许是并不直观,因为哈希值大小从64位增加到了160位。但因为可以很大程度上避免哈希冲突,我们就能够实现显著的提速——这也明显地简化了dictionary映射。

离堆内存管理

在2.1之前,为了使用操作系统的虚拟机、内存管理等等,我们在加载时积极地使用mmap。但在JVM中的内存映射文件的蹩脚(unmap!)是臭名远播的,而且当我们有很多周围的内存映射文件时,JVM崩溃时有发生。实在是不怎么样。

我们也认识到当有超过64GB的空闲RAM时,内存映射文件会导致性能下降。而且一旦我们失去控制权,内存映射文件被刷新到磁盘的问题就太常见了。但在 Java 堆上大规模保留这一信息显然不可行,因为GC处理机制。Stardog 2.1转向了离堆内存分配方案,这时我们能够非常精细地控制磁盘刷新、更高效地使用空闲的系统内存,几乎和内存映射一样快。

减少GC暂停

最后,要想显著提高加载性能,我们得解决GC开销问题了,这在大数据量加载过程中很重要,因为对象会被频繁地创建和销毁。使用不可变的对象使它能够提高并发性,但同时也伴随着产生附带垃圾的系统开销。使用GC机制并没有起到明显的效果。我们已经通过修改不必要创建对象的地方解决了GC处理成本。那种精细软件工程在相对较小Stardog代码库支持下进行着。类似于机械制造!

精细软件再造工程,例如深入研究使用不断重置单个 StringBuilder的RDF分析器本质,而不是为每个RDF值产生一个新的创建者。我们也减少了在堆上使用高速缓存,以减轻内存的压力。我们现在看到的GC暂停只用了1%甚至更少的整体批量加载时间。

查询评估

如果你连起码的查询评估性能也没有提升的话,那对提高大容量数据加载性也不会有太大作用。因此我们也是这样做的。提高2.1中的查询评价性能的关键是内存使用情况以及如何处理中间结果。思考一下从 SP2B中的SPARQL 查询:

SELECT DISTINCT ?name1 ?name2 WHERE { ?article1 rdf:type bench:Article . ?article2 rdf:type bench:Article . ?article1 dc:creator ?author1 . ?author1 foaf:name ?name1 . ?article2 dc:creator ?author2 . ?author2 foaf:name ?name2 . ?article1 swrc:journal ?journal . ?article2 swrc:journal ?journal FILTER (?name1 < ?name2)}

对于一个500万triple的数据库,这个查询将生成18,362,955(!)个结果,DISINCT将把它们放入内存中。在大规模使用情况下是不可行的。Stardog 2.1通过重用新离堆内存分配计划解决这个问题。一个小堆不能工作时,GC 进程结束它,代之以大堆,所以我们尽力避免堆。当然,这意味着我们必须手动管理内存,但这在JVM中的应用确实是成功的。我们使用了基于各种JVM内件的内存管理器,负责分配(和取消分配)查询评估期间的数据,包括保留中间查询结果。它管理JVM外部的堆,并在需要时流至磁盘。新的内存管理器还执行一些静态分析使用的数据库统计信息以指导其查询操作。

更多详情

新可扩展性的改进不会影响到公共API。要了解Stardog 2.1中其他一些改进,请参阅 此幻灯片。

时间: 2024-10-29 08:10:55

Stardog 2.1:并发性、内存管理、GC机制带来的扩展性提升的相关文章

DB2通用数据库的并发性

在数据库管理系统(DBMS)的领域中,术语"并发性"用于表示不止一个应用程序基本上(从用户的角度来看)同时访问同一数据的能力.因为 DBMS 的主要优点之一就是可以在多个用户和多个应用程序中共享数据,所以数据库系统应该提供一种管理并发访问数据的方法.DBMS 必须确保维护数据的一致状态和数据的完整性. 取得该效果的一种方法就是实施只串行(serial-only)模式来处理数据库请求.即每个事务都要等待另一事务(具有更高的优先权或者比它早启动)完成其工作.然而,对于现在的在线系统和客户异

IOS有关内存管理的二三事

IOS有关内存管理的二三事 一.前引 随着移动设备的内存越来越大,程序员也已经度过了为了那一两M的内存在系统的抽丝剥茧的年代,对于JAVA的开发者,对内存更是伸手即取,并且从不关心什么时候还回去.但是,程序的掌控度对程序员来说是至关重要的,任何语言的内存管理机制的初衷也是在有限的空间里完成最精致的逻辑. 二.Xcode工程设置ARC ARC是xcode5中引入的自动引用计数,其原理与MRC是一样,只是系统帮助我们添加了retain和release.现在在xcode中新建的项目默认都是ARC的环境

JVM内存管理及GC机制

一.概述 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.经过这么长时间的发展,Java GC机制已经日臻完善,几乎可以自动的为我们做绝大多数的事情. 虽然java不需要开发人员显示的分配和回收内存,这对开发人员确实降低了不少编程难度,但也可能带来一些副作用: 1. 有可能不知不觉浪费了很多内存 2. JVM花

现代JVM内存管理方法的发展历程,GC的实现及相关设计概述(转)

JVM区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分:Code Cache(代码缓存区).Perm Gen(永久代).Jvm Stack(java虚拟机栈).Local Method Statck(本地方法栈). HotSpot虚拟机GC算法采用分代收集算法: 1.一个人(对象)出来(new 出来)后会在Eden Space(伊甸园)无忧无虑的生活,直

.NET中的内存管理,GC机制,内存释放过程

引言 作为一个.NET程序员,我们知道托管代码的内存管理是自动的..NET可以保证我们的托管程序在结束时全部释放,这为我们编程人员省去了不少麻烦,我们可以连想都不想怎么去管理内存,反正.NET自己会保证一切.好吧,有道理,有一定的道理.问题是,当我们用到非托管资源时.NET就不能自动管理了.这是因为非托管代码不受CLR(Common Language Runtime)控制,超出CLR的管理范围.那么如何处理这些非托管资源呢,.NET又是如何管理并释放托管资源的呢? 自动内存管理和GC 在原始程序

JVM内存管理:GC算法精解---分代搜集算法

引言 何为终极算法? 其实就是现在的JVM采用的算法,并非真正的终极.说不定若干年以后,还会有新的终极算法,而且几乎是一定会有,因为LZ相信高人们的能力. 那么分代搜集算法是怎么处理GC的呢? 对象分类 上一章已经说过,分代搜集算法是针对对象的不同特性,而使用适合的算法,这里面并没有实际上的新算法产生.与其说分代搜集算法是第四个算法,不如说它是对前三个算法的实际应用. 首先我们来探讨一下对象的不同特性,接下来LZ和各位来一起给这些对象选择GC算法. 内存中的对象按照生命周期的长短大致可以分为三种

《Java安全编码标准》一1.7 并发性、可见性和内存

1.7 并发性.可见性和内存 可以在不同线程之间共享的内存称为共享内存(shared memory)或内存堆(heap memory).本节使用变量(variable)这个名词来代表字段和数组元素[JLS2005].在不同的线程中共享的变量称为共享变量.所有的实例字段.静态字段以及数组元素作为共享变量存储在共享内存中.局部变量.形式方法参数以及异常例程参数是从来不能在线程之间共享的,不会受到内存模型的 影响. 在现代多处理器共享内存的架构下,每个处理器有一个或多个层次的缓存,会定期地与主存储器进

从JVM的内存管理角度分析Java的GC垃圾回收机制_java

一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能.    GC的基本原理    Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放.     对于程序员来说,分配对象使用ne

Java内存管理及GC算法

概述 内存划分 虚拟机规范中将内存分为六大部分,分别为PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量及本地方法栈. 1.PC寄存器:线程独占: 2.JAVA虚拟机栈:线程独有:JAVA虚拟机栈是在创建线程的同时创建的,用于存储栈帧,JAVA虚拟机栈也是线程独有的. 3.JAVA堆:全局共享: 4.方法区:全局共享:它主要存储的是 运行时常量池 字段信息 方法信息 构造方法 普通函数的字节码内容以及一些特殊方法. 5.本地方法栈:线程独有,本地方法栈是一个传统的栈,它用来支持nati