release: v0.5.1 — 存储后端生产级硬化(CRC-32/全库加密/WAL分片/页面化存储/多标签页锁/e2e)+ 深度审查修复(假实现接线/死代码清理)
CI / test (18.x) (push) Successful in 10m10s
CI / test (20.x) (push) Successful in 10m10s
CI / test (22.x) (push) Successful in 10m6s
CI / e2e (push) Successful in 9m51s
CI / test (24.x) (push) Successful in 10m28s

This commit is contained in:
thzxx
2026-08-10 12:07:00 +08:00
parent cff98b0903
commit 334067d89e
88 changed files with 15713 additions and 10626 deletions
+61 -33
View File
@@ -79,14 +79,14 @@
<div class="container">
<div class="hero">
<h1>⚡ 性能基准测试</h1>
<p>在浏览器中实测 <code>Memory</code><code>Aria</code>LSM-Tree 自研引擎)两种模式的真实性能。
数据量分档(1K / 10K / 50K 行),测量 <code>INSERT</code> / 主键查询 / 索引查询 / <code>UPDATE</code> / <code>DELETE</code> 的吞吐(ops/sec)。
<strong>测试结果取决于你的设备与浏览器</strong>,仅供参考。</p>
<p>在浏览器中实测 <code>Memory</code><code>Aria</code>LSM-Tree 自研引擎)在纯内存 / IndexedDB / OPFS(页面化 4KB 存储)三种存储后端下的真实性能。
数据量分档(1K / 10K 行),测量 <code>INSERT</code> / 主键查询 / 索引查询 / <code>UPDATE</code> / <code>DELETE</code> 的吞吐(ops/sec)。
<strong>测试结果取决于你的设备与浏览器</strong>,仅供参考。磁盘后端结果反映真实持久化成本(WAL full 同步模式)。</p>
</div>
<div class="controls">
<button class="btn btn-run" id="run-btn" onclick="runAll()">▶ 运行完整基准</button>
<button class="btn btn-small" onclick="runAll(true)">⚡ 快速模式(1K/10K</button>
<button class="btn btn-small" onclick="runAll(true)">⚡ 快速模式(仅 1K</button>
<button class="btn btn-small" onclick="window.location.reload()">↻ 重置</button>
<span class="status" id="status"></span>
</div>
@@ -99,7 +99,9 @@
<thead><tr><th>引擎</th><th>数据量</th><th>INSERT</th><th>主键查询</th><th>索引查询</th><th>UPDATE</th><th>DELETE</th></tr></thead>
<tbody></tbody>
</table>
<p class="note">注:Memory 模式为纯内存 Map 存储Aria 模式为 LSM-Tree + MemTable 存储(<code>storageBackend: 'memory'</code>,无磁盘 I/O
<p class="note">注:Memory = 纯内存 Map 引擎Aria-memory = LSM-Tree(内存后端,无磁盘 I/O
Aria-IndexedDB = LSM-TreeIDB 后端,整 value SSTable);Aria-OPFS = LSM-TreeOPFS 后端,<strong>4KB 页面化存储</strong> + BufferPool LRU 缓存 + WAL 分片真追加)。
磁盘后端使用 <code>walSyncMode: 'full'</code>(每条写入同步落盘,最安全的崩溃一致性)。
INSERT 为单行批量写入(含 WAL 记录);UPDATE/DELETE 按主键条件执行。</p>
</div>
</div>
@@ -107,8 +109,16 @@
<script src="../dist/metona-sqlark.js"></script>
<script>
const M = window.MetonaSqlark;
const SIZES = [1000, 10000, 50000];
const SIZES_FAST = [1000, 10000];
const SIZES = [1000, 10000];
const SIZES_FAST = [1000];
// [label, mode, diskEngine]
const ENGINES = [
['memory', 'memory', 'memory'],
['aria-memory', 'aria', 'memory'],
['aria-indexeddb', 'aria', 'indexeddb'],
['aria-opfs', 'aria', 'opfs'],
];
let cancelled = false;
@@ -128,13 +138,25 @@ function makeRows(n) {
return rows;
}
async function createDb(mode) {
const db = new M.MetonaSqlark({
name: 'bench-' + mode + '-' + Date.now(),
mode,
diskEngine: 'memory',
async function createDb(mode, diskEngine) {
if (mode === 'memory') {
const db = new M.MetonaSqlark({ name: 'bench-mem-' + Date.now(), mode: 'memory' });
await db.init();
await db.defineTable('bench', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
age: { type: 'number', index: true },
city: { type: 'string' },
});
return db;
}
// aria 模式:磁盘后端走真实持久化(WAL full 同步)
const db = await M.create({
name: 'bench-aria-' + diskEngine + '-' + Date.now(),
mode: 'aria',
diskEngine,
aria: { walSyncMode: 'full', checkpointInterval: 100000, memtableSizeThreshold: 64 * 1024 * 1024 },
});
await db.init();
await db.defineTable('bench', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
@@ -150,39 +172,39 @@ async function timeIt(fn) {
return performance.now() - start;
}
async function runBench(mode, size) {
const db = await createDb(mode);
async function runBench(label, mode, diskEngine, size) {
const db = await createDb(mode, diskEngine);
const rows = makeRows(size);
const result = { mode, size, insert: 0, pk: 0, idx: 0, update: 0, del: 0 };
const result = { mode: label, size, insert: 0, pk: 0, idx: 0, update: 0, del: 0 };
try {
// INSERT(批量)
let ms = await timeIt(() => db.table('bench').insertMany(rows));
result.insert = Math.round(size / (ms / 1000));
// 主键查询 × 500
// 主键查询 × 300
ms = await timeIt(async () => {
for (let i = 0; i < 500; i++) await db.query(`SELECT * FROM bench WHERE id = 'k${(i * 37) % size}'`);
for (let i = 0; i < 300; i++) await db.query(`SELECT * FROM bench WHERE id = 'k${(i * 37) % size}'`);
});
result.pk = Math.round(500 / (ms / 1000));
result.pk = Math.round(300 / (ms / 1000));
// 索引查询 × 500
// 索引查询 × 300
ms = await timeIt(async () => {
for (let i = 0; i < 500; i++) await db.query(`SELECT * FROM bench WHERE age = ${18 + ((i * 17) % 60)}`);
for (let i = 0; i < 300; i++) await db.query(`SELECT * FROM bench WHERE age = ${18 + ((i * 17) % 60)}`);
});
result.idx = Math.round(500 / (ms / 1000));
result.idx = Math.round(300 / (ms / 1000));
// UPDATE × 200(按主键)
// UPDATE × 150(按主键)
ms = await timeIt(async () => {
for (let i = 0; i < 200; i++) await db.query(`UPDATE bench SET name = 'X${i}' WHERE id = 'k${(i * 53) % size}'`);
for (let i = 0; i < 150; i++) await db.query(`UPDATE bench SET name = 'X${i}' WHERE id = 'k${(i * 53) % size}'`);
});
result.update = Math.round(200 / (ms / 1000));
result.update = Math.round(150 / (ms / 1000));
// DELETE × 200(按主键)
// DELETE × 150(按主键)
ms = await timeIt(async () => {
for (let i = 0; i < 200; i++) await db.query(`DELETE FROM bench WHERE id = 'k${(i * 71) % size}'`);
for (let i = 0; i < 150; i++) await db.query(`DELETE FROM bench WHERE id = 'k${(i * 71) % size}'`);
});
result.del = Math.round(200 / (ms / 1000));
result.del = Math.round(150 / (ms / 1000));
} finally {
await db.close();
}
@@ -199,16 +221,16 @@ async function runAll(quick) {
document.getElementById('summary').innerHTML = '';
const results = [];
for (const mode of ['memory', 'aria']) {
for (const [label, mode, diskEngine] of ENGINES) {
for (const size of sizes) {
setStatus(`正在测试 ${mode === 'memory' ? 'Memory' : 'Aria'} 引擎 · ${size.toLocaleString()} 行…`, true);
setStatus(`正在测试 ${label} · ${size.toLocaleString()} 行…`, true);
// 让 UI 刷新
await new Promise((r) => setTimeout(r, 30));
try {
const r = await runBench(mode, size);
const r = await runBench(label, mode, diskEngine, size);
results.push(r);
} catch (e) {
setStatus(`${mode}/${size} 失败: ${e.message}`, false);
setStatus(`${label}/${size} 失败: ${e.message}`, false);
console.error(e);
}
}
@@ -239,13 +261,19 @@ function renderResults(results) {
`;
// 表格(按引擎分组)
const GROUP_LABEL = {
'memory': '🚀 Memory 引擎',
'aria-memory': '🌲 Aria 引擎(内存后端)',
'aria-indexeddb': '🌲 Aria 引擎(IndexedDB 后端)',
'aria-opfs': '🌲 Aria 引擎(OPFS · 4KB 页面化)',
};
let lastMode = '';
for (const r of results) {
if (r.mode !== lastMode) {
lastMode = r.mode;
const row = document.createElement('tr');
row.className = 'group-header';
row.innerHTML = `<td colspan="6">${r.mode === 'memory' ? '🚀 Memory 引擎' : '🌲 Aria 引擎(LSM-Tree'}</td>`;
row.innerHTML = `<td colspan="6">${GROUP_LABEL[r.mode] || r.mode}</td>`;
tbody.appendChild(row);
}
const row = document.createElement('tr');