ObjectBox[二] 教程:如何开始使用ObjectBox
ObjectBox[六] 数据监听和RX(Data Observers and Reactive Extensions)
ObjectBox[七] 支持LiveData(Android体系结构组件)
ObjectBox[十二] Meta Model, IDs, and UIDs
—
ObjectBox是事务完全满足ACID属性的数据库。一个事务可以把几个操作分成一个单独的工作单元,要么一起执行,要么全不执行。如果您正在寻找更详细的事务资料,请参考Wikipedia on database transactions.。对于ObjectBox事务,请继续阅读:
您可能没有注意到,几乎所有ObjectBox的操作都涉及事务。如果你调用put
方法,会使用一个写事务。另外,如果您调用get
或Query
,则会使用读事务。所有这些都是在底层封装,对你来说是透明的。完全忽略事务,也不会遇到问题。然而,对于更复杂的应用,通常值得学习事务的知识,以使您的应用程序更加的高效。
显式事务
现在我们知道ObjectBox操作都在隐式事务中运行,除非你使用显式事务。在后一种情况下,多个操作共享(显式)事务。换句话说,可以通过显式事务来控制事务边界。这样做可以大大提高您的应用程序的效率和一致性。
BoxStore
类 提供以下方法来执行显式事务:
runInTx
:在事务内运行runnable
。runInReadTx
:在只读事务中运行runnable
。与写入事务不同,多个读取事务可以同时运行。runInTxAsync
将给定的Runnable
作为单独线程中的事务运行。一旦事务完成,给定的callback
被调用(回调可能为空)。callInTx
: 类似runInTx
, 支持一个返回值和异常抛出。
显式交易相对于批量put
操作的优点是,您可以使用多个Box
执行任意数量的操作,并且在事务处理过程中,您可以获得一致的(事务处理)的数据。
写入事务的示例:
boxStore.runInTx(() -> {
for(User user: allUsers) {
if(modify(user)) box.put(user);
else box.remove(user);
}
});
事务成本
了解事务对于掌握数据库性能至关重要。如果你只记得关于这个话题的一句话,那就应该是这样的:写事务是有代价的。
提交事务包括将数据同步到物理存储,这对于数据库来说是相对昂贵的操作。只有当文件系统确认所有的数据已经以持久的方式存储(不仅仅是内存缓存)时,交易才算成功。事务所需的文件同步可能需要几毫秒。记住这一点,并尝试将若干个操作(例如 多次put
)运行在一个事务中。
看看这段代码:
for(User user: allUsers) {
modify(user); // modifies properties of given user
box.put(user);
}
这段代码有什么问题吗?对每个用户来说都有一个隐含的事务,对于大量的对象来说效率非常低。使用put重载来同时存储所有的用户会更高效:
for(User user: allUsers) {
modify(user); // modifies properties of given user
}
box.put(allUsers);
好多了!如果您有1,000个用户,则后一个示例使用单个事务来存储所有用户。第一个代码示例使用1,000个隐式事务,导致速度非常慢。
读事务
在ObjectBox中,读取操作速度很快。与写入事务相反,没有提交,因此不会对文件系统进行昂贵的同步。像get
, count
和queries
这样的操作 ,如果它们不是在一个显式事务(读或写)中的话,那就会在隐式读事务中运行。请注意, 在读事务中调用put
是非法的 :将抛出异常。
虽然读事务比写事务快得多,但是开始读事务还是有一些开销。因此,对于大量读取(例如循环中的数百个),可以通过将这些读取分组在单个读取事务中来提高性能(参见下面的显式事务)。
多版本并发
ObjectBox为开发人员提供 Multiversion并发控制(MVCC) 语义。这允许多个并发读取器(读取事务),其可以立即执行而不会阻塞或等待。这是通过MVCC保证的。即使正在进行写入事务,读取事务也可以立即读取上次一致的状态。写事务按顺序执行以确保一致的状态。因此,建议尽量减少写入操作,以避免阻塞其他未完成的写入操作。例如,在写事务内部进行联网或复杂的计算通常是一个糟糕的主意。所以,复杂的操作需要在写事务之前进行。
写事务内部锁
尽量避免在写事务中使用同步锁(e.g. via synchronized
or java.util.concurrent.locks
)
由于写事务在单独的线程,因此它们在内部有效地获取写锁。
(Because write transactions run exclusively, they effectively acquire a write lock internally)。
与所有的锁一样,当涉及多个锁时,您需要密切关注。始终以相同的顺序获取锁以避免死锁。如果您在事务内部获得锁定X”,则必须确保您的代码“X”锁时不会启动另一个写入事务。