Flutter数据库管理插件dart_lmdb2的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter数据库管理插件dart_lmdb2的使用

dart_lmdb2 是一个用于Dart应用程序的高性能嵌入式数据库,它封装了LMDB(Lightning Memory-Mapped Database)。此包提供了高级便利方法和细粒度的事务控制。

Pub Version
Linux Windows Android MacOS iOS web
💙 💙 💙 💙 💙 -

注意:所有平台的预编译本机二进制文件都包含在Dart包内,但iOS/Android需要Flutter才能在移动设备上运行。请参阅flutter_lmdb2

为什么选择LMDB?

LMDB特别适合移动和嵌入式应用,原因如下:

  • 移动友好设计

    • 数据库格式可在所有平台上移植。
    • 通过内存映射和最少的I/O操作实现节能。
    • 不需要后台进程或服务。
    • 可配置的映射大小确保可预测的内存使用。
    • 延迟数据加载实现快速应用启动。
  • 资源需求最小

    • 超紧凑的本地库(小于150KB)。
    • 固定映射大小限制的可配置数据库上限。
    • 单线程设计,几乎无进程开销。
    • 无需额外的运行时依赖。
  • 卓越性能

    • 零拷贝读取通过直接内存映射。
    • 高效的操作系统级页面缓存消除I/O瓶颈。
    • 通过直接内存访问优化读取操作。
    • 读事务不会阻塞写操作。
    • 写操作不会阻塞读操作。
    • 由于B+树设计,顺序读取非常快。
  • 可靠性

    • 完整的ACID合规性,保证原子性和崩溃恢复。
    • 写时复制设计确保数据库完整性,即使在崩溃后也是如此。
    • 直接页更新,无需单独的日志文件。
    • 可选的自包含单文件设计简化备份操作。
    • 在OpenLDAP中经过实战测试。

为什么不选择LMDB?

LMDB不是通用数据库。它适用于特定用途,并在此类领域中表现出色。如果你有以下需求,则不应使用LMDB:

  • 如果你的模式不是基于键值对,而是关系型的。
  • 如果你需要高效地查询除键之外的值。
  • 如果你想要保存时间序列或流数据(如日志记录)。
  • 如果无法为写入场景提供与数据库大小相同数量的RAM。

场景

  • 快速查询大数据库,同时占用少量内存,例如:

    • 字典
    • 瓷砖缓存
    • 纹理缓存
    • 多平台资产
  • 实时数据的一致更新

    • 生产者 -> 消费者
      • 主应用 -> 插件实例
      • 音频引擎 -> 可视化器
    • 多个生产者 -> 多个消费者
      • 工作池
      • 分布式处理
    • 资源受限的IPC
      • Flutter应用 -> AudioUnit扩展
      • 宿主 -> 沙箱插件
  • 配置数据(大量)

支持的功能

以下LMDB功能已暴露:

  • 完整的CRUD操作
  • 用于数据组织的命名数据库
  • 游标操作用于范围查询
  • 全面的事务支持,具有ACID保证
  • 丰富的统计和监控
  • 使用所有LMDB标志进行可配置初始化

LMDB版本

该包捆绑了LMDB版本 0.9.70。虽然此版本号在过去三年内未更改,但LMDB仍在积极维护。我们跟踪的是确切的git仓库版本:da9aeda

开始使用

将包添加到你的 pubspec.yaml 文件中:

dependencies:
  dart_lmdb2: ^0.9.5

然后运行:

# 对于Dart项目:
dart pub get
# 对于Flutter项目:
flutter pub get

使用

导入并使用:

import 'package:dart_lmdb2/lmdb.dart';

void main() async {
    final db = LMDB();
    await db.init('path/to/db');

    // 自动事务操作
    await db.putAuto('key', 'value'.codeUnits);
    final result = await db.getAuto('key');

    // 手动事务控制
    final txn = await db.txnStart();
    try {
        await db.put(txn, 'key1', 'value1'.codeUnits);
        await db.put(txn, 'key2', 'value2'.codeUnits);
        await db.putUtf8(txn, 'english_greeting', 'Hello World');
        await db.txnCommit(txn);
    } catch (e) {
        await db.txnAbort(txn);
        rethrow;
    }
}

使用 LMDBInitConfig 配置数据库设置:

final config = LMDBInitConfig(
  mapSize: 10 * 1024 * 1024,  // 10MB
  maxDbs: 1,
  envFlags: 0,
  mode: 0664,
);

await db.init('path/to/db', config: config);

开发

所有本地库都已包含在目录 lib/src/native 中:

lib/src/native/
├── android
│   ├── arm64-v8a
│   │   └── liblmdb.so
│   └── x86_64
│       └── liblmdb.so
├── ios
│   └── liblmdb.a
├── linux
│   └── liblmdb.so
├── macos
│   └── liblmdb.dylib
└── windows
    └── lmdb.dll

你可以通过以下步骤重建这些库:

前提条件

该包捆绑了LMDB以从源代码构建。构建过程是自动化的,但需要:

  • CMake(3.10 或更高版本)
  • C 编译器(gcc、clang 或 MSVC)
  • Dart SDK(3.0 或更高版本)

平台特定设置

Android

需要安装Android NDK,最好通过Android Studio。此外,需要设置环境变量 ANDROID_NDK_HOME 指向适当的NDK位置。

iOS / iPadOS / MacOS

请安装XCode和XCode命令行工具。

# 安装构建工具
brew install cmake
Linux
# 安装构建工具
sudo apt-get install build-essential cmake
Windows
  • 安装Visual Studio,包含C++开发工具
  • 安装CMake

构建和测试

  1. 克隆仓库并切换到项目子目录:
git clone https://github.com/grammatek/dart_lmdb2.git
cd dart_lmdb2/dart_lmdb2
  1. 安装依赖项:
dart pub get
  1. (可选)通过 ffigen 重新生成绑定:
dart run ffigen
  1. 构建本地库:
dart run tool/build.dart

对于Android,需要传递 --android 标志:

dart run tool/build.dart --android

对于iOS/iPadOS,需要传递 --ios 标志:

dart run tool/build.dart --ios
  1. 运行当前平台的测试:
dart test

示例代码

以下是使用 dart_lmdb2 的示例代码:

import 'package:dart_lmdb2/lmdb.dart';
import 'package:path/path.dart' as path;
import 'dart:io';

void main() async {
  // 使用自定义配置初始化数据库
  final config = LMDBInitConfig(
    mapSize: 10 * 1024 * 1024, // 10MB
    maxDbs: 1,
    mode: "0664",
  );

  final dbPath = path.join(Directory.current.path, 'example_db');
  final db = LMDB();

  try {
    await db.init(dbPath, config: config);

    // 自动事务操作
    await db.putAuto('key1', 'Hello World!'.codeUnits);
    final result = await db.getAuto('key1');
    if (result != null) {
      print('Retrieved: ${String.fromCharCodes(result)}');
    }

    // 单个事务中的多个操作
    final txn = await db.txnStart();
    try {
      await db.put(txn, 'key2', 'Transaction'.codeUnits);
      await db.put(txn, 'key3', 'Example'.codeUnits);

      final stats = await db.stats(txn);
      print('\nDatabase Statistics:');
      print('- Total Entries: ${stats.entries}');
      print('- Tree Depth: ${stats.depth}');
      print('- Leaf Pages: ${stats.leafPages}');

      await db.txnCommit(txn);
    } catch (e) {
      await db.txnAbort(txn);
      rethrow;
    }

    // 自动事务删除
    await db.deleteAuto('key1');

    // 验证删除
    final deletedResult = await db.getAuto('key1');
    print(
        '\nAfter deletion: ${deletedResult == null ? 'Entry removed' : 'Entry still exists'}');
  } finally {
    db.close();
    // 清理示例数据库
    if (Directory(dbPath).existsSync()) {
      Directory(dbPath).deleteSync(recursive: true);
    }
  }
}

更多关于Flutter数据库管理插件dart_lmdb2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据库管理插件dart_lmdb2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用dart_lmdb2插件进行数据库管理的示例代码。dart_lmdb2是一个用于与Lightning Memory-Mapped Database (LMDB)交互的Dart库,非常适合高性能的嵌入式数据库需求。

首先,确保你已经在pubspec.yaml文件中添加了dart_lmdb2依赖:

dependencies:
  flutter:
    sdk: flutter
  dart_lmdb2: ^latest_version  # 请替换为实际的最新版本号

然后,运行flutter pub get来获取依赖。

接下来,让我们编写一些代码来展示如何使用dart_lmdb2进行基本的数据库操作,如打开数据库环境、创建事务、存储和检索数据。

import 'package:dart_lmdb2/dart_lmdb2.dart';
import 'dart:typed_data';
import 'dart:convert';

void main() async {
  // 设置数据库环境路径
  String path = './testdb';

  // 打开数据库环境
  final env = await MdbEnv.create(path);
  await env.setMaxReaders(1);
  await env.setMapSize(1024 * 1024 * 1024);  // 设置最大数据库大小(1GB)

  // 开启一个读写事务
  final txn = await env.beginTxn(write: true);

  // 打开数据库(如果不存在则创建)
  final dbi = await txn.openDbi('mydb', create: true);

  // 要存储的数据(键值对)
  String keyStr = 'key1';
  String valueStr = 'value1';
  Uint8List key = Uint8List.fromList(utf8.encode(keyStr));
  Uint8List value = Uint8List.fromList(utf8.encode(valueStr));

  // 存储数据
  await dbi.put(txn, key, value);

  // 提交事务
  await txn.commit();

  // 开启一个只读事务
  final readTxn = await env.beginTxn();

  // 检索数据
  Uint8List retrievedValue = await dbi.get(readTxn, key);
  String retrievedValueStr = utf8.decode(retrievedValue);

  // 打印检索到的数据
  print('Retrieved value for key "$keyStr": $retrievedValueStr');

  // 关闭只读事务
  await readTxn.abort();

  // 关闭数据库环境
  await env.close();
}

代码解释:

  1. 设置数据库环境路径:指定一个路径来存储LMDB数据库文件。
  2. 打开数据库环境:使用MdbEnv.create方法创建并打开一个数据库环境。
  3. 设置环境参数:通过setMaxReaderssetMapSize方法设置环境参数。
  4. 开启事务:使用env.beginTxn(write: true)开启一个读写事务。
  5. 打开数据库:在事务中,使用txn.openDbi方法打开或创建一个数据库。
  6. 存储数据:将要存储的键值对编码为Uint8List,然后使用dbi.put方法存储数据。
  7. 提交事务:使用txn.commit方法提交事务。
  8. 开启只读事务:使用env.beginTxn()开启一个只读事务。
  9. 检索数据:使用dbi.get方法根据键检索数据,并将结果解码为字符串。
  10. 关闭事务和环境:最后,关闭事务和数据库环境以释放资源。

这段代码展示了如何使用dart_lmdb2进行基本的数据库操作。根据实际需求,你可以扩展这个示例,添加更多的功能和错误处理逻辑。

回到顶部