使用 Dexie.js 设计数据库表、索引字段和版本控制的方法
Dexie.js 是一个轻量级、高性能的 IndexedDB 封装库,适合在浏览器中管理复杂的数据存储。以下是关于设计数据库表、索引字段和版本控制的详细介绍,以及避免常见性能陷阱的方法:
1. 表设计
Dexie.js 的数据库表定义通过 Dexie.version().stores()
方法完成。每张表需要定义主键和索引字段。
示例:
import Dexie from 'dexie';
const db = new Dexie('MyDatabase');
// 定义数据库版本和表结构
db.version(1).stores({
users: '++id, name, email, age', // ++id 表示自增主键
orders: '++id, userId, createdDate, amount', // 复合索引可以通过 "," 定义
});
- 主键:
++id
表示自增主键,id
也可以是指定的唯一标识符。 - 索引字段:
name, email, age
是普通索引字段,用于加速查询。 - 复合索引:如
userId, createdDate
,可以提高多条件查询性能。 - 多表支持:通过多个表存储不同类型的数据。
表设计建议:
- 数据归一化:避免在单表中存储冗余数据,使用
userId
等外键关联表。 - 字段命名:选择简短但有意义的名称,避免过长的键名浪费存储空间。
- 尽量避免复杂对象:IndexedDB 不擅长存储深度嵌套对象,建议将嵌套数据拆分成多表。
2. 索引字段设计
索引是优化查询性能的关键。以下是一些设计建议:
复合索引
复合索引用于多字段联合查询。
db.version(2).stores({
orders: '++id, [userId+createdDate]', // 复合索引
});
唯一索引
确保字段值唯一,避免重复数据。
db.version(2).stores({
users: '++id, &email', // & 表示唯一索引
});
多字段查询优化
- 单字段索引适用于简单查询。
- 复合索引适用于
AND
条件的多字段查询。 - 如果需要
OR
条件查询,尽量避免复合索引,而是拆分查询逻辑后合并结果。
3. 版本控制
数据库版本控制通过 db.version()
方法实现,每次更新表结构时需要增加版本号并定义迁移逻辑。
示例:
db.version(1).stores({
users: '++id, name, email',
});
db.version(2).stores({
users: '++id, name, email, age', // 新增 age 字段
}).upgrade((tx) => {
// 升级逻辑:为现有数据添加默认值
return tx.users.toCollection().modify(user => {
user.age = 0;
});
});
db.version(3).stores({
orders: '++id, userId, createdDate, amount',
});
注意事项:
- 每次修改表结构都需要提升版本号。
- 确保数据迁移逻辑的原子性,避免部分数据更新失败。
- 数据迁移尽量在非高峰时段进行,以减少对用户的影响。
4. 性能优化与避免陷阱
(1) 批量操作
避免逐条写入,使用 bulkAdd
或 bulkPut
进行批量操作。
db.users.bulkAdd([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
]);
(2) 避免扫描大表
尽量通过索引字段查询,避免全表扫描。
// 使用索引字段快速查询
db.users.where('email').equals('alice@example.com').first();
(3) 延迟查询
Dexie 支持链式延迟查询,只有在 .toArray()
或 .first()
等方法调用时才执行查询。
const query = db.orders.where('userId').equals(1);
const orders = await query.toArray(); // 仅在这里执行查询
(4) 分页查询
对于大数据集,使用分页来限制查询结果数量。
const pageSize = 20;
const page = 1;
const results = await db.orders.offset((page - 1) * pageSize).limit(pageSize).toArray();
(5) 避免索引冗余
每个索引会占用存储空间,谨慎选择有用的索引。
(6) 定期清理
对于长期不使用的数据,定期清理或归档。
await db.orders.where('createdDate').below(new Date('2024-01-01')).delete();
5. 调试与监控
调试工具
- 使用浏览器开发者工具查看 IndexedDB 数据。
- Dexie 提供
db.on()
事件监听:
db.on('populate', () => {
console.log('Database populated');
});
性能监控
监控查询性能并优化索引设计。
console.time('query');
await db.users.where('name').equals('Alice').toArray();
console.timeEnd('query');
6. 综合示例
const db = new Dexie('MyAppDB');
// 定义表结构和索引
db.version(1).stores({
users: '++id, &email, name, age',
orders: '++id, [userId+createdDate], amount',
});
// 数据操作
(async () => {
// 添加数据
await db.users.bulkAdd([
{ name: 'Alice', email: 'alice@example.com', age: 25 },
{ name: 'Bob', email: 'bob@example.com', age: 30 },
]);
// 查询数据
const alice = await db.users.where('name').equals('Alice').first();
console.log(alice);
// 更新数据
await db.users.update(alice.id, { age: 26 });
// 删除数据
await db.users.where('age').below(18).delete();
})();
通过合理设计表、索引字段和版本控制,以及避免常见的性能陷阱,可以让 Dexie.js 数据库高效、稳定地运行。