优化的批量(块)上传对象到IndexedDB

9

我想在一个事务中将对象添加到IndexedDB中的某个表中:

_that.bulkSet = function(data, key) {
    var transaction = _db.transaction([_tblName], "readwrite"),
        store = transaction.objectStore(_tblName),
        ii = 0;

    _bulkKWVals.push(data);
    _bulkKWKeys.push(key);

    if (_bulkKWVals.length == 3000) {
        insertNext();
    }

    function insertNext() {
        if (ii < _bulkKWVals.length) {
            store.add(_bulkKWVals[ii], _bulkKWKeys[ii]).onsuccess = insertNext;
            ++ii;
        } else {
            console.log(_bulkKWVals.length);
        }
    }
};

看起来它可以正常工作,但这并不是一个非常优化的做法,特别是如果对象数量很高(~50.000-500.000)。我怎么能够可能优化它呢?理想情况下,我想先添加3000个对象,然后从数组中删除它们,再添加另外3000个对象,也就是分块添加。有什么好的建议吗?

2个回答

24
连续插入那么多行,无法获得良好的性能。
我是一个IndexedDBdev,在你所谈论的规模下(连续写入数十万行),我有实际经验。这并不太美观。
在我看来,当需要连续写入大量数据时,IDB不适合使用。如果我要设计一个需要大量数据的IndexedDB应用程序,我会想办法慢慢地进行种子播撒。
问题在于写入,我认为问题在于写入速度缓慢,加上它们的I/O密集性,随着时间的推移会变得更糟。(就价值而言,读取在IDB中始终非常快速。)
首先,您可以通过重复使用事务来节省开销。因此,您的第一直觉可能是尝试将所有内容都塞进同一个事务中。但是,例如在Chrome中,我发现浏览器似乎不喜欢长时间运行的写入操作,可能是因为某种机制旨在限制表现不佳的选项卡。
I'm not sure what kind of performance you're seeing, but average numbers might fool you depending on the size of your test. The limiting factor is throughput, but if you're trying to insert large amounts of data consecutively pay attention to writes over time specifically.
I happen to be working on a demo with several hundred thousand rows at my disposal, and have statistics. With my visualization disabled, running pure dash on IDB, here's what I see right now in Chrome 32 on a single object store with a single non-unique index with an auto-incrementing primary key.
For a much smaller dataset of 27k rows, I saw 60-70 entries/second: * Around 30 seconds: 921 entries/second on average (there's always a great burst of inserts at the start), 62/second at the moment I sampled * Around 60 seconds: 389/second average (sustained decreases starting to outweigh effect initial burst) 71/second at the moment * Around 1:30: 258/second, 67/second at the moment * Around 2:00 (~1/3 done): 188/second on average, 66/second at moment

使用较小的数据集进行的一些示例表现得更好,但具有类似的特征。同样对于更大的数据集,这种影响会被夸大,并且当多个小时离开时,我曾经看到仅不到每秒1个记录。


1
我也进行了一些基准测试,1000个插入操作分成1000个事务,猜猜看:[创建事务+获取存储] = 20秒,而[插入操作] = 10秒……第一个操作明显存在严重问题。 - lisak

16
IndexedDB实际上是为了优化批量操作而设计的。问题在于规范和某些文档并没有宣传它的工作方式。如果仔细阅读IndexedDB规范中定义IDBObjectStore所有变异操作(add()、put()、delete())的部分,您会发现它允许调用者同步调用它们并省略监听除最后一个之外的成功事件。通过省略这样做(但仍然监听onerror),您将获得巨大的性能提升。 这个使用Dexie.js的示例显示了可能的批量速度,因为它在我的MacBook Pro上插入了10,000行,在680毫秒内完成(使用Opera/Chromium)。
通过Dexie.js库中的Table.bulkPut()方法实现。
db.objects.bulkPut(arrayOfObjects)

在我深入研究IndexedDB规范或Dexie源代码之前,你知道省略事件监听器的IndexedDB功能(方法或参数名称等)的名称吗? - Dan
2
省略事件监听器是通过不订阅从操作返回的IDBRequest上的“success”事件来完成的。如果您请求1000个“add”操作,只需侦听最后一个操作的success事件,但请在所有操作上侦听error事件。结果将是:如果没有发生错误事件并触发了最后一个成功事件,则可以确定所有操作都成功了。 - David Fahlander
免责声明:此举所获得的性能优势是在几年前进行测量的,并在当时取得了巨大的性能提升。我没有使用现代浏览器的最新版本进行任何更新的性能测量。 - David Fahlander
啊,我明白了。谢谢! - Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接