Update README and project cleanup
This commit is contained in:
108
db/init.js
Normal file
108
db/init.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* DMP 数据库初始化
|
||||
*
|
||||
* 📚 性能设计要点:
|
||||
* 1. 位图索引(Bitmap Index)
|
||||
* - 每个标签对应一个位数组,bit[i]=1 表示第 i 个用户拥有该标签
|
||||
* - 多标签交叉计算 = 位数组 AND/OR 运算 → O(n/64) 复杂度
|
||||
* - 这是 OLAP 数据库(ClickHouse / Druid)的核心思想
|
||||
*
|
||||
* 2. 预聚合
|
||||
* - 标签人数在写入时就维护好,查询时直接读取
|
||||
* - 避免 COUNT(*) 全表扫描
|
||||
*
|
||||
* 3. 连接管理
|
||||
* - WAL 模式支持读写并发
|
||||
* - busy_timeout 避免锁等待超时
|
||||
*/
|
||||
|
||||
const Database = require('better-sqlite3');
|
||||
const path = require('path');
|
||||
|
||||
const DB_PATH = path.join(__dirname, '..', 'dmp.db');
|
||||
|
||||
function getDb(dbSuffix = '') {
|
||||
const dbFile = dbSuffix ? `dmp_${dbSuffix}.db` : 'dmp.db';
|
||||
const dbPath = path.join(__dirname, '..', dbFile);
|
||||
const db = new Database(dbPath);
|
||||
db.pragma('journal_mode = WAL');
|
||||
db.pragma('foreign_keys = ON');
|
||||
db.pragma('busy_timeout = 5000');
|
||||
db.pragma('cache_size = -64000'); // 64MB cache
|
||||
db.pragma('synchronous = NORMAL');
|
||||
return db;
|
||||
}
|
||||
|
||||
function initializeDatabase(dbSuffix = '') {
|
||||
const db = getDb(dbSuffix);
|
||||
|
||||
// 用户表 — 宽表设计,一行一个用户
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uid TEXT UNIQUE NOT NULL,
|
||||
name TEXT,
|
||||
email TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
extra_json TEXT DEFAULT '{}'
|
||||
)
|
||||
`);
|
||||
|
||||
// 标签分类(列)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS tag_categories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
key TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
color TEXT DEFAULT '#6366f1'
|
||||
)
|
||||
`);
|
||||
|
||||
// 标签定义
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
key TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
category_id INTEGER NOT NULL REFERENCES tag_categories(id),
|
||||
description TEXT DEFAULT '',
|
||||
coverage INTEGER DEFAULT 0,
|
||||
coverage_rate REAL DEFAULT 0,
|
||||
trend REAL DEFAULT 0,
|
||||
sort_order INTEGER DEFAULT 0
|
||||
)
|
||||
`);
|
||||
|
||||
// 用户-标签关联(核心表,需要最强索引)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS user_tags (
|
||||
user_id INTEGER NOT NULL,
|
||||
tag_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, tag_id)
|
||||
) WITHOUT ROWID
|
||||
`);
|
||||
// WITHOUT ROWID = clustered index on PK,查询更快
|
||||
|
||||
db.exec(`
|
||||
CREATE INDEX IF NOT EXISTS idx_user_tags_tag ON user_tags(tag_id, user_id);
|
||||
`);
|
||||
|
||||
// 数据导入批次记录
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS import_batches (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source TEXT NOT NULL,
|
||||
record_count INTEGER DEFAULT 0,
|
||||
status TEXT DEFAULT 'pending',
|
||||
error_message TEXT,
|
||||
started_at TEXT DEFAULT (datetime('now')),
|
||||
finished_at TEXT
|
||||
)
|
||||
`);
|
||||
|
||||
console.log(`✅ 数据库表初始化完成 [${dbSuffix || 'default'}]`);
|
||||
db.close();
|
||||
}
|
||||
|
||||
module.exports = { getDb, initializeDatabase };
|
||||
Reference in New Issue
Block a user