@devrev/meerkat-dbm (英语)
@devrev/meerkat-dbm 是浏览器首个数据库管理层,建立在 鸭子 - 瓦斯姆. 它协调查询执行,管理 DuckDB 实例,缓存文件,在浏览器存储中坚持数据,并优化内存使用,以便在网络应用程序中实现稳健的高性能数据处理.
它旨在将分析SQL的力量带入浏览器而不损害应用程序稳定性或用户体验. 无论是构建数据密集型的仪表板,交互式报告工具,还是离线第一应用程序,Meerkat DBM都提供了您需要的基础.
建筑
Meerkat DBM由几个关键组件组成,它们共同管理数据并在浏览器中执行查询:
- DBM (数据库管理器):中央管弦乐团. 它接收询问,管理执行周期,并与其他组成部分进行协调.
- 文件管理器:处理数据存储和检索的所有方面。 它可以管理内存数据,或者坚持到索引数据库.
- 实例管理器: 由用户执行的组件,负责创建、管理和终止
duckdb-wasm实例. - DuckDB 实例: 基础
duckdb-wasm执行查询的引擎,在主线程中运行,或在 iFrames 为并行性运行.
这种模块化设计为在浏览器中管理复杂数据工作流程提供了明确分离的关切.
为什么是Meerkat DBM?
虽然 duckdb-wasm 在复杂的网络应用程序中直接使用, Meerkat DBM提供了一个结构化的,生产准备的层,解决了常见的问题:
- * 记忆安全:通过管理查询队列和内存互换来防止出记忆出错,确保您的应用即使有大数据集也能保持稳定.
- * 持久性: 提供无缝索引 DB 存储,允许数据在浏览器会话中持续.
- 高级文件管理: 简化各种文件格式(Parquet,JSON,远程URL)的处理,以智能缓存和分出.
- ++ 并行处理:解锁高性能分析器,可选iframe基于架构进行并行查询执行,防止UI被冻结.
关键特性
数据库管理
- 实例管理: DuckDB 实例的自动生命周期管理.
- 循环和关闭:通过在最终停产前回收发动机(teardown + warm recretting)来恢复工人在无活性后的高水记忆,并设有由消费者供应的门.
- 连接集合: 有效管理数据库连接.
- 查询排队:按顺序或平行执行的查询的智能调度.
- 表格锁定:确保同时访问期间的线性安全表操作.
QQ 文件管理
- 多个格式: Parquet的原生支持,JSON文件.
- 散装操作:用于批量登记和处理文件的高性能API.
- 分区: 支持表格分割,以高效地管理和查询大数据集.
- 元数据处理: 对表格和文件的丰富元数据支持.
- 多个存储模式: 有弹性存储选项,包括内存和索引数据库.
安装
npm install @devrev/meerkat-dbm @duckdb/duckdb-wasm
使用量
1. 实施实例管理
Meerkat DBM要求您提供 InstanceManager将图书馆与特定的 duckdb-wasm 版本,赋予您对其即时和配置的全部控制.
// src/instance-manager.ts
import * as duckdb from '@duckdb/duckdb-wasm';
import { InstanceManagerType } from '@devrev/meerkat-dbm';
// Select the desired DuckDB bundle
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
export class InstanceManager implements InstanceManagerType {
private db: duckdb.AsyncDuckDB | null = null;
private async initDB(): Promise<duckdb.AsyncDuckDB> {
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
const worker_url = URL.createObjectURL(new Blob([`importScripts("${bundle.mainWorker!}");`], { type: 'text/javascript' }));
const worker = new Worker(worker_url);
const logger = { log: (msg: any) => console.log(msg) };
const db = new duckdb.AsyncDuckDB(logger, worker);
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
URL.revokeObjectURL(worker_url);
return db;
}
async getDB(): Promise<duckdb.AsyncDuckDB> {
if (!this.db) {
this.db = await this.initDB();
}
return this.db;
}
async terminateDB(): Promise<void> {
if (this.db) {
await this.db.terminate();
this.db = null;
}
}
}
2. 实例:带有持久性存储的序列查询
此示例使用 DBM 带有 IndexedDBFileManager 用于安全、相继的查询执行和浏览器会话的数据持续.
import { DBM, IndexedDBFileManager } from '@devrev/meerkat-dbm';
import { InstanceManager } from './instance-manager';
// 1. Create the managers
const instanceManager = new InstanceManager();
const fileManager = new IndexedDBFileManager({
instanceManager,
// This function is called by Meerkat to fetch file data when needed
fetchTableFileBuffers: async (tableName) => {
// In a real app, you would fetch data from a indexdb
return [];
},
});
// 2. Create the DBM instance
const dbm = new DBM({
instanceManager,
fileManager,
onEvent: (event) => console.info('DBM Event:', event),
options: {
// Optional intermediate "recycle" after 2s of inactivity: tear down the
// DuckDB instance and immediately re-instantiate it. Reclaims the worker's
// high-water memory (WASM linear memory only grows) while keeping the
// engine warm for the next query. Must be less than shutdownInactiveTime.
recycleInactiveTime: 2000,
// Optional gate consulted before each recycle. Return false to skip it
// (e.g. nothing worth reclaiming). Defaults to always-recycle when omitted.
shouldRecycle: () => true,
// Automatically shut down the DuckDB instance after 5s of inactivity
shutdownInactiveTime: 5000,
},
});
// 3. Register data
await fileManager.registerJSON({
tableName: 'sales',
fileName: 'sales.json',
json: [
{ id: 1, product: 'Laptop', amount: 1200 },
{ id: 2, product: 'Mouse', amount: 25 },
{ id: 3, product: 'Keyboard', amount: 75 },
],
});
// 4. Run a query
const results = await dbm.query('SELECT * FROM sales WHERE amount > 50');
console.log(results);
3. 实例:与IFrame运行器平行查询
此设置用途 DBMParallel 财务报告和审定财务报表 ParallelIndexedDBFileManager 在多个基于 Iframe DuckDB 实例中并行执行查询.
import { DBMParallel, IFrameRunnerManager, ParallelIndexedDBFileManager } from '@devrev/meerkat-dbm';
import log from 'loglevel';
import { InstanceManager } from './instance-manager';
// 1. Create instance and file managers
const instanceManager = new InstanceManager();
const fileManager = new ParallelIndexedDBFileManager({
instanceManager,
fetchTableFileBuffers: async (table) => [],
logger: log,
});
// 2. Set up the iframe runner manager for parallel execution
const iframeManager = new IFrameRunnerManager({
// URL to the runner HTML file that hosts the DuckDB instance
runnerURL: 'http://localhost:4204/runner/indexeddb-runner.html',
origin: 'http://localhost:4204',
totalRunners: 4, // Number of parallel iframes
fetchTableFileBuffers: async (table) => [],
logger: log,
});
// 3. Create the parallel DBM instance
const parallelDBM = new DBMParallel({
instanceManager,
fileManager,
iFrameRunnerManager: iframeManager,
logger: log,
options: {
shutdownInactiveTime: 10000,
},
});
// 4. Register data
await fileManager.bulkRegisterJSON([
{
tableName: 'transactions',
fileName: 'transactions.json',
json: [
{ id: 1, product_id: 101, amount: 1200 },
{ id: 2, product_id: 102, amount: 25 },
],
},
{
tableName: 'products',
fileName: 'products.json',
json: [
{ id: 101, name: 'Laptop', category: 'Electronics' },
{ id: 102, name: 'Mouse', category: 'Accessories' },
],
},
]);
// 5. Execute queries in parallel
const results = await Promise.all([
parallelDBM.query('SELECT * FROM transactions WHERE amount > 100'),
parallelDBM.query(`
SELECT p.category, COUNT(*) as product_count
FROM transactions t
JOIN products p ON t.product_id = p.id
GROUP BY p.category
`),
]);
console.log('Query Results', results);