首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在没有内存泄漏的情况下将大量记录写入Xodus数据库?

如何在没有内存泄漏的情况下将大量记录写入Xodus数据库?
EN

Stack Overflow用户
提问于 2020-04-17 08:19:29
回答 1查看 352关注 0票数 0

我需要插入约150米的记录到Xodus数据库使用xodus-dnq在一批。基于示例,我实现了以下方法:

代码语言:javascript
复制
        XdModel.registerNodes(
                XDTrip
        )
        val store = StaticStoreContainer.init(
                dbFolder = File(target),
                environmentName = "trips"
        )
        initMetaData(XdModel.hierarchy, store)
        store.use { store ->
            store.persistentStore.use {
                // TripLoader(File(src)) returns a stream with roughly 150M elements
                TripLoader(File(src)).forEach { databaseTrip ->
                    store.transactional {
                        XDTrip.new {
                            id = databaseTrip.id
                            start = databaseTrip.start.epochSecond
                            end = databaseTrip.end.epochSecond
                        }
                    }
                }
            }
        }

它工作得很好,但会泄漏内存。大概我需要手动提交/刷新/持久化事务吗?

根据另一项建议,我对此进行了重构,以便在事务中运行较小的批:

代码语言:javascript
复制
    XdModel.registerNodes(
            XDTrip,
            XDPosition,
            XDPositionSource
    )
    val store = StaticStoreContainer.init(
            dbFolder = File(target),
            environmentName = "trips"
    )
    initMetaData(XdModel.hierarchy, store)
    store.use {
        it.use { store ->
            Sequence { TripLoader(File(src)).iterator() }.chunked(100).forEachIndexed { index, csvChunk ->
                store.transactional {
                    println("Chunk $index")
                    csvChunk.forEach { csvTrip ->
                        XDTrip.new {
                            id = csvTrip.id
                            start = csvTrip.start.epochSecond
                            end = csvTrip.end.epochSecond
                            // ...
                        }
                    }
                }
            }
        }
    }

然而,这仍然会泄漏内存。

我分析了内存,发现jetbrains.exodus.core.dataStructures.ConcurrentLongObjectCache占用了堆的大部分部分:

由“"jetbrains.exodus.core.dataStructures.ConcurrentLongObjectCache”@ 0x4c0481d58“加载的

的一个实例占45,075,952 (80.24%)字节。内存是在“"jetbrains.exodus.core.dataStructures.ConcurrentLongObjectCache$CacheEntry[]”@ 0x4c0481d58“加载的一个sun.misc.Launcher$AppClassLoader实例中积累的。

关键词jetbrains.exodus.core.dataStructures.ConcurrentLongObjectCache sun.misc.Launcher$AppClassLoader @ 0x4c0481d58 jetbrains.exodus.core.dataStructures.ConcurrentLongObjectCache$CacheEntry[]

当程序运行时,我可以看到磁盘上的数据库大小是如何增加的,所以我不知道为什么缓存会在这里填满。

EN

回答 1

Stack Overflow用户

发布于 2020-04-17 11:59:07

用批处理大量操作是一种推荐的方法。如果使用Kotlin序列,则有windowed扩展函数。所以你的代码就像:

代码语言:javascript
复制
TripLoader(File(src)).windowed(1000) { trips ->
                    store.transactional {
                    trips.forEach { databaseTrip ->
                        XDTrip.new {
                            id = databaseTrip.id
                            start = databaseTrip.start.epochSecond
                            end = databaseTrip.end.epochSecond
                        }
                    }
                }

实际上,如果您在运行时或整个操作中做了一些事情,消耗了大量时间,那么最好深入一点,使用低级别的api。代码将变得更加复杂,但运行速度会快一倍。

例如,如果您认为数据是一致的,并且不需要检查所有约束、关系和其他,您可以使用以下内容:

代码语言:javascript
复制
val type =  XdTrip.entityType
TripLoader(File(src)).windowed(1000) { trips ->
        persistentStore.executeInExclusiveTransaction { txn -> 
               trips.forEach { databaseTrip ->
                        txn.newEntity(type).also {
                            it.setProperty("id", databaseTrip.id)
                            ...
                        }
               }
        }

窗口大小应根据导入的数据进行检查。在原始数据结构的情况下,它可以增加。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61267052

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档