ObjectBox是一个高性能的NoSQL数据库,针对移动和边缘设备进行了优化。与SQLite不同,它使用基于平面文件的自有存储格式,没有SQL层。根据ObjectBox Benchmarks, 2024,ObjectBox在Android设备上执行插入和读取操作的速度比SQLite快10到100倍,使其成为高负载移动应用的最佳选择。
要点
ObjectBox是一种内存型NoSQL数据库,由德国公司ObjectBox.io开发。与传统关系型数据库不同,ObjectBox使用自己的SLOT(超级轻量级对象树)格式将对象直接存储在平面文件中。这消除了ORM和SQL解析器的开销。SLOT格式针对内存有限的设备进行了优化:数据会自动碎片整理,数据库大小平均比同等记录数的SQLite数据库小4到6倍。
ObjectBox支持所有主要移动平台:Android、iOS和Flutter。对于Android,可以通过ObjectBox Gradle Plugin进行代码生成,该插件处理带注解的实体并创建用于处理数据的Box类。对于Flutter,使用带有Dart包装器的原生插件。除了移动平台,ObjectBox还支持Linux、macOS、Windows和WebAssembly,使得数据模型可以在移动应用和服务器同步之间重复使用。
根据ObjectBox(2023)的研究数据,该数据库在Google Play中被超过5000个应用使用,包括拥有超过1000万用户的应用程序。主要应用场景:数据缓存、离线模式工作和时间序列存储(IoT)。ObjectBox在健康和健身追踪应用中特别受欢迎,这些应用需要在没有永久互联网连接的情况下在设备上快速记录传感器读数,并从多个设备收集分析数据。
ObjectBox作为Realm和SQLite在Android平台上的替代品出现。第一个稳定版本于2018年发布。与Realm的主要区别:ObjectBox没有GPL限制(使用Apache 2.0)并支持更多平台:从移动设备到Linux服务器。
ObjectBox提供:自动生成Box类、内置索引用于快速搜索、支持回滚的事务、通过RxJava和Kotlin Flow的响应式查询,以及通过ObjectBox Sync(专有协议)的设备间同步。与Firebase Realtime Database不同,ObjectBox Sync以点对点模式工作,无需中央服务器,从而减少延迟和基础设施成本。
ObjectBox以其二进制.mdb(ObjectBox Data File)格式存储数据。每个实体映射到一个文件,其中记录按固定偏移顺序存储。这使得无需完全扫描文件即可读取和写入单个对象。
使用ObjectBox不需要SQL:查询通过代码中的fluent-API构建。编译器生成辅助类MyObjectBox和封装所有CRUD操作的Box。数据模型通过注解@Entity、@Id、@Index和@Relation来描述。
// 在Android中连接ObjectBox
class App: Application() {
lateinit var boxStore: BoxStore
override fun onCreate() {
super.onCreate()
boxStore = MyObjectBox.builder()
.androidContext(this)
.build()
}
}
根据官方基准测试,ObjectBox在操作速度上显著超过SQLite,特别是在批量插入和批量读取方面。然而,SQLite由于其普及性和内置SQL支持,仍然是移动应用的事实标准。
| 操作 | ObjectBox | SQLite | 差异 |
|---|---|---|---|
| 插入(1000条记录) | ~5 ms | ~180 ms | x36更快 |
| 读取(1000条记录) | ~3 ms | ~70 ms | x23更快 |
| 更新(1000条记录) | ~6 ms | ~150 ms | x25更快 |
| 按索引搜索 | ~0.1 ms | ~2 ms | x20更快 |
| 数据库大小(1000个对象) | ~28 KB | ~112 KB | 小4倍 |
ObjectBox在需要复杂JOIN查询、聚合函数(SUM、COUNT带分组)或与现有SQL模式集成时不如SQLite。如果项目已经使用带有Room ORM的SQLite,迁移到ObjectBox将需要重写所有数据访问逻辑。ObjectBox也不支持自定义SQL查询,仅支持fluent-API。然而,对于典型的CRUD操作和缓存,ObjectBox提供高达100倍的性能提升,这已通过官方基准测试在不同OS版本的Android设备上得到确认。
ObjectBox使用注解来定义实体。编译后,会创建带有put、get、remove和query方法的Box类。以下是在任务列表应用中Task模型的典型操作。
每个实体都用@Entity注解。带@Id的字段成为主键,@Index加速基于该字段的搜索。ObjectBox支持在值为0时自动生成ID。
@Entity
data class Task(
@Id var id: Long = 0,
@Index var title: String = "",
var isCompleted: Boolean = false,
var priority: Int = 0
)
Box是处理实体的主要类。put()方法保存或更新对象,get()根据ID读取,remove()删除。所有基本操作都是同步的,并在调用线程上执行。
val taskBox: Box<Task> = boxStore.boxFor(Task::class.java)
// 创建
val task = Task(title = "购买产品", priority = 3)
val newId = taskBox.put(task)
// 读取
val savedTask = taskBox.get(newId)
// 更新
savedTask.isCompleted = true
taskBox.put(savedTask)
// 删除
taskBox.remove(newId)
ObjectBox通过.subscribe()支持响应式查询。订阅者会收到符合查询条件的数据变化的通知。这对于在添加或删除元素时自动更新列表的UI非常方便。
val query = taskBox.query()
.equal(Task_.isCompleted, false)
.orderDesc(Task_.priority)
.build()
query.subscribe { tasks ->
// tasks — List,自动更新
updateUi(tasks)
}
ObjectBox支持实体之间的关系——一对一、一对多和多对多。关系通过@Relation注解定义。与SQLite不同,ObjectBox不使用外键:关系通过平面文件中的ID列表实现,加速了对象图中的导航。
@Backlink关系允许从子实体导航到父实体,无需单独的引用字段。ObjectBox自动维护反向引用,消除了数据重复和两个表之间的同步。
ObjectBox Query Builder允许构建条件链:equal、notEqual、greater、less、in、contains、startsWith。所有条件通过and/or组合。结果可以排序、限制并以List或LazyList(用于大数据集)的形式获取。
ObjectBox自动索引@Id字段。对于自定义索引,在经常查询的字段上使用@Index注解。索引加速equal和in查询,但会减慢插入速度——不要索引不搜索的字段。ObjectBox支持用于同时过滤多个字段的复合索引。复合索引对列表中的过滤器特别有用:例如,使用isCompleted和priority字段上的复合索引,搜索未完成的高优先级任务要快几十倍。
ObjectBox Sync是一种用于设备间实时数据同步的专有技术。它使用基于WebSocket的协议,按last-writer-wins解决冲突。Sync适用于需要离线同步的应用:笔记、任务列表、来自多个传感器的IoT数据。然而,生产使用需要购买许可证。
// 多条件查询
val highPriorityIncomplete = taskBox.query()
.greater(Task_.priority, 5)
.equal(Task_.isCompleted, false)
.build()
.find()
// 子串搜索
val searchResults = taskBox.query()
.contains(Task_.title, "产品", StringOrder.CASE_INSENSITIVE)
.build()
.find()
常见问题
ObjectBox根据Apache 2.0许可证分发,允许无限制的商业使用。ObjectBox Sync(跨平台同步)功能是专有的,生产使用需要购买许可证。
BoxStore是线程安全的:多个线程可以同时读取数据。写入在Box级别被阻塞。对于复杂事务,请使用boxStore.runInTx(),它保证一组操作的原子性。
是的,ObjectBox从3.0版本开始支持Kotlin Multiplatform(KMP)。可用的目标包括Android、iOS、JVM和Native。对于KMP,使用单独的Gradle插件objectbox-kotlin,支持expect/actual。
ObjectBox将数据存储在应用目录的.mdb文件中。要备份,请通过BoxStore.copy()复制整个ObjectBox目录,或在调用boxStore.close()后手动复制文件。不能复制活动文件——这会损坏数据。
Room是SQLite之上的ORM,需要SQL查询并有对象映射的开销。ObjectBox是无SQL的NoSQL,快得多,但不支持复杂JOIN,也不是关系型数据库。选择取决于任务:对于关系数据——使用Room,对于快速对象存储——使用ObjectBox。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。