AI知识库
开源知识库
知识库搭建组合
Fast + Ollama
RAGFlow + Ollama
深度解读RAGFlow的深度文档理解DeepDoc
Ollama + Dify.ai
AnythingLLM + Ollama
MaxKB + Ollama
LobeChat + Ollama
ChatNio + Ollama
运维知识库
02-新框架技术选型说明
07-性能与并发能力分析
05-开发规范文档
01-现有架构分析报告
06-实施计划与时间表
03-新框架详细设计文档
README
04-数据库迁移方案
00-项目总结与建议
文档管理详细设计
-
+
首页
04-数据库迁移方案
# PandaWiki 数据库迁移方案 ## 一、迁移概述 ### 1.1 迁移目标 将 PandaWiki 从 **PostgreSQL** 迁移到 **达梦数据库 DM8**,确保: 1. **数据完整性**:100% 数据迁移,无丢失 2. **功能兼容性**:所有功能正常运行 3. **性能保持**:性能不低于原系统 4. **最小停机时间**:业务影响最小化 ### 1.2 迁移策略 采用 **PostgreSQL 兼容模式** 策略: ``` 策略优势: ✓ 迁移成本最低 ✓ SQL 语法兼容度高 ✓ 应用代码改动最小 ✓ 可使用现有 PostgreSQL 工具链 ``` --- ## 二、达梦数据库配置 ### 2.1 安装达梦数据库 **环境要求**: - 操作系统:Linux (CentOS 7+, Ubuntu 18.04+) - 内存:至少 4GB - 磁盘:至少 10GB **安装步骤**: ```bash # 1. 下载达梦数据库安装包 # 从达梦官网下载 DM8 for Linux 版本 # 2. 解压安装包 tar -xvf DMInstall.tar # 3. 运行安装程序 cd DMInstall ./DMInstall -i # 4. 按照向导完成安装 # 选择安装路径:/opt/dmdbms # 创建实例:PANDAWIKI ``` ### 2.2 配置 PostgreSQL 兼容模式 **修改 dm.ini 配置文件**: ```ini # /opt/dmdbms/data/PANDAWIKI/dm.ini # PostgreSQL 兼容模式 COMPATIBLE_MODE = 2 # 端口号 PORT_NUM = 5236 # 最大连接数 MAX_SESSIONS = 1000 # 内存配置 BUFFER = 2000 # 单位:MB SORT_BUF_SIZE = 200 # 单位:MB # 字符集 CHAR_CODE = 1 # UTF-8 # 大小写敏感性 CASE_SENSITIVE = N # 其他优化参数 ENABLE_PARALLEL = 1 MAX_PARALLEL_DEGREE = 4 ``` **重启达梦数据库**: ```bash # 停止数据库 /opt/dmdbms/bin/DmServicePANDAWIKI stop # 启动数据库 /opt/dmdbms/bin/DmServicePANDAWIKI start # 检查状态 /opt/dmdbms/bin/DmServicePANDAWIKI status ``` ### 2.3 创建数据库用户 ```sql -- 连接到数据库 disql SYSDBA/SYSDBA@localhost:5236 -- 创建用户 CREATE USER pandawiki IDENTIFIED BY "PandaWiki@123"; -- 创建数据库 CREATE DATABASE pandawiki OWNER pandawiki; -- 授权 GRANT DBA TO pandawiki; -- 切换到新数据库 \c pandawiki pandawiki PandaWiki@123 -- 创建 schema (如果需要) CREATE SCHEMA IF NOT EXISTS public; ``` --- ## 三、数据迁移步骤 ### 3.1 准备工作 #### 3.1.1 备份原 PostgreSQL 数据库 ```bash # 导出数据库结构和数据 pg_dump -h localhost -U postgres -d pandawiki \ --schema-only -f schema.sql pg_dump -h localhost -U postgres -d pandawiki \ --data-only -f data.sql # 导出完整数据库(备份) pg_dump -h localhost -U postgres -d pandawiki \ -F c -f pandawiki_backup.dump ``` #### 3.1.2 分析数据库对象 ```sql -- 统计表数量 SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public'; -- 统计数据量 SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size, n_tup_ins AS inserts, n_tup_upd AS updates, n_tup_del AS deletes FROM pg_stat_user_tables ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; -- 列出所有表 SELECT tablename FROM pg_tables WHERE schemaname = 'public'; ``` ### 3.2 表结构迁移 #### 3.2.1 类型映射表 | PostgreSQL 类型 | 达梦类型 | 说明 | |----------------|---------|------| | `text` | `TEXT` | 长文本 | | `varchar(n)` | `VARCHAR(n)` | 变长字符串 | | `char(n)` | `CHAR(n)` | 定长字符串 | | `integer` / `int` | `INT` | 整数 | | `bigint` | `BIGINT` | 长整数 | | `smallint` | `SMALLINT` | 短整数 | | `boolean` | `BOOLEAN` | 布尔值 | | `timestamp` | `TIMESTAMP` | 时间戳 | | `timestamptz` | `TIMESTAMP WITH TIME ZONE` | 带时区时间戳 | | `date` | `DATE` | 日期 | | `time` | `TIME` | 时间 | | `numeric(p,s)` | `DECIMAL(p,s)` | 精确数值 | | `real` / `float4` | `REAL` | 单精度浮点 | | `double precision` / `float8` | `DOUBLE` | 双精度浮点 | | `jsonb` / `json` | `JSON` | JSON 数据 | | `bytea` | `BLOB` | 二进制数据 | | `uuid` | `VARCHAR(36)` | UUID (存储为字符串) | | `serial` | `INT IDENTITY(1,1)` | 自增整数 | | `bigserial` | `BIGINT IDENTITY(1,1)` | 自增长整数 | #### 3.2.2 处理 PostgreSQL 特有语法 **需要调整的语法**: 1. **SERIAL 类型** ```sql -- PostgreSQL CREATE TABLE users ( id SERIAL PRIMARY KEY ); -- 达梦 (方式一:使用 IDENTITY) CREATE TABLE users ( id INT IDENTITY(1,1) PRIMARY KEY ); -- 达梦 (方式二:使用序列) CREATE SEQUENCE users_id_seq START WITH 1; CREATE TABLE users ( id INT DEFAULT NEXT VALUE FOR users_id_seq PRIMARY KEY ); ``` 2. **JSONB 类型** ```sql -- PostgreSQL meta JSONB -- 达梦 (在 PG 兼容模式下可直接使用) meta JSON ``` 3. **数组类型** ```sql -- PostgreSQL tags TEXT[] -- 达梦 (使用 JSON 或文本) tags JSON -- 存储为 JSON 数组 tags TEXT -- 存储为逗号分隔字符串 ``` 4. **RETURNING 子句** ```sql -- PostgreSQL INSERT INTO nodes (...) VALUES (...) RETURNING id; -- 达梦 (PG 兼容模式支持) INSERT INTO nodes (...) VALUES (...) RETURNING id; ``` #### 3.2.3 表结构转换脚本 **自动转换工具** (使用 Python): ```python #!/usr/bin/env python3 # pg_to_dm_converter.py import re def convert_sql(pg_sql): """转换 PostgreSQL SQL 到达梦兼容格式""" dm_sql = pg_sql # 1. 替换 SERIAL dm_sql = re.sub( r'\s+SERIAL\s+', ' INT IDENTITY(1,1) ', dm_sql, flags=re.IGNORECASE ) dm_sql = re.sub( r'\s+BIGSERIAL\s+', ' BIGINT IDENTITY(1,1) ', dm_sql, flags=re.IGNORECASE ) # 2. 替换 JSONB -> JSON dm_sql = re.sub( r'\s+JSONB\s+', ' JSON ', dm_sql, flags=re.IGNORECASE ) # 3. 替换 TIMESTAMPTZ dm_sql = re.sub( r'TIMESTAMPTZ', 'TIMESTAMP WITH TIME ZONE', dm_sql, flags=re.IGNORECASE ) # 4. 删除 PostgreSQL 特有扩展 dm_sql = re.sub( r'CREATE EXTENSION.*?;', '', dm_sql, flags=re.IGNORECASE | re.DOTALL ) return dm_sql # 读取 PostgreSQL SQL with open('schema.sql', 'r', encoding='utf-8') as f: pg_sql = f.read() # 转换 dm_sql = convert_sql(pg_sql) # 写入达梦 SQL with open('schema_dm.sql', 'w', encoding='utf-8') as f: f.write(dm_sql) print("转换完成!") ``` **运行转换**: ```bash python3 pg_to_dm_converter.py ``` ### 3.3 数据迁移 #### 3.3.1 使用 pg_dump + DM 导入工具 **方案一:SQL 脚本导入** ```bash # 1. 导出 PostgreSQL 数据 (INSERT 语句) pg_dump -h localhost -U postgres -d pandawiki \ --column-inserts --data-only -f data_inserts.sql # 2. 转换 SQL (如需要) python3 pg_to_dm_converter.py data_inserts.sql > data_dm.sql # 3. 导入到达梦 disql pandawiki/PandaWiki@123@localhost:5236 < data_dm.sql ``` **方案二:CSV 文件导入** ```bash # 1. 导出 PostgreSQL 数据为 CSV psql -h localhost -U postgres -d pandawiki -c \ "COPY users TO '/tmp/users.csv' WITH CSV HEADER;" psql -h localhost -U postgres -d pandawiki -c \ "COPY knowledge_bases TO '/tmp/knowledge_bases.csv' WITH CSV HEADER;" # 2. 导入到达梦 disql pandawiki/PandaWiki@123@localhost:5236 << EOF LOAD DATA INFILE '/tmp/users.csv' INTO TABLE users FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES; EOF ``` **方案三:使用 DM 数据迁移工具** (推荐) ```bash # 达梦提供的数据迁移工具 DTS (Data Transfer Service) # 图形化界面,支持: # - PostgreSQL 到达梦的迁移 # - 表结构自动转换 # - 数据批量迁移 # - 迁移验证 # 启动 DTS /opt/dmdbms/tool/dts/dts_gui.sh ``` #### 3.3.2 大表分批迁移策略 对于大表(如 `conversation_messages`),采用分批迁移: ```sql -- PostgreSQL 导出(分批) COPY ( SELECT * FROM conversation_messages WHERE id >= 'xxx' AND id < 'yyy' ) TO '/tmp/messages_batch1.csv' WITH CSV HEADER; -- 达梦导入(分批) LOAD DATA INFILE '/tmp/messages_batch1.csv' INTO TABLE conversation_messages ...; ``` ### 3.4 索引和约束迁移 **索引迁移**: ```sql -- PostgreSQL CREATE INDEX idx_nodes_kb_id ON nodes(kb_id); CREATE INDEX idx_nodes_doc_id ON nodes(doc_id); -- 达梦(语法相同) CREATE INDEX idx_nodes_kb_id ON nodes(kb_id); CREATE INDEX idx_nodes_doc_id ON nodes(doc_id); ``` **外键约束迁移**: ```sql -- PostgreSQL ALTER TABLE nodes ADD CONSTRAINT fk_nodes_kb FOREIGN KEY (kb_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE; -- 达梦(语法相同) ALTER TABLE nodes ADD CONSTRAINT fk_nodes_kb FOREIGN KEY (kb_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE; ``` --- ## 四、.NET EF Core 配置 ### 4.1 连接字符串配置 **appsettings.json**: ```json { "ConnectionStrings": { "DmConnection": "Server=localhost;Port=5236;User Id=pandawiki;Password=PandaWiki@123;Database=pandawiki;Pooling=true;MinPoolSize=5;MaxPoolSize=100;" } } ``` ### 4.2 DbContext 配置 **Program.cs**: ```csharp using Npgsql; var builder = WebApplication.CreateBuilder(args); // 配置达梦数据库(使用 Npgsql,因为达梦在 PG 兼容模式下) builder.Services.AddDbContext<PandaWikiDbContext>(options => { var connectionString = builder.Configuration.GetConnectionString("DmConnection"); options.UseNpgsql(connectionString, npgsqlOptions => { npgsqlOptions.MigrationsHistoryTable("__EFMigrationsHistory", "public"); npgsqlOptions.CommandTimeout(60); }); if (builder.Environment.IsDevelopment()) { options.EnableSensitiveDataLogging(); options.EnableDetailedErrors(); } }); ``` ### 4.3 EF Core 迁移 **创建初始迁移**: ```bash # 添加迁移 dotnet ef migrations add InitialCreate \ --project src/PandaWiki.Infrastructure \ --startup-project src/PandaWiki.API # 生成 SQL 脚本(用于检查) dotnet ef migrations script \ --project src/PandaWiki.Infrastructure \ --startup-project src/PandaWiki.API \ --output migrations.sql # 应用迁移 dotnet ef database update \ --project src/PandaWiki.Infrastructure \ --startup-project src/PandaWiki.API ``` **Code First 模型示例**: ```csharp public class Node { public string Id { get; set; } public string KbId { get; set; } public string DocId { get; set; } public NodeType Type { get; set; } public string Name { get; set; } public string? Content { get; set; } // JSON 列(达梦支持) [Column(TypeName = "json")] public NodeMeta? Meta { get; set; } public string? ParentId { get; set; } public double Position { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } } // Entity Configuration public class NodeConfiguration : IEntityTypeConfiguration<Node> { public void Configure(EntityTypeBuilder<Node> builder) { builder.ToTable("nodes"); builder.HasKey(n => n.Id); // JSON 列配置 builder.Property(n => n.Meta) .HasColumnType("json") .HasConversion( v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), v => JsonSerializer.Deserialize<NodeMeta>(v, (JsonSerializerOptions?)null) ); // 索引 builder.HasIndex(n => n.KbId).HasDatabaseName("idx_nodes_kb_id"); builder.HasIndex(n => n.DocId).HasDatabaseName("idx_nodes_doc_id"); builder.HasIndex(n => n.ParentId).HasDatabaseName("idx_nodes_parent_id"); } } ``` --- ## 五、数据验证 ### 5.1 数据完整性验证 **验证脚本** (SQL): ```sql -- 1. 表数量验证 -- PostgreSQL SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE'; -- 达梦 SELECT count(*) FROM USER_TABLES; -- 2. 行数验证 -- PostgreSQL SELECT schemaname, tablename, n_live_tup AS row_count FROM pg_stat_user_tables ORDER BY tablename; -- 达梦 SELECT table_name, num_rows FROM USER_TABLES ORDER BY table_name; -- 3. 数据一致性验证(逐表对比) -- 示例:users 表 -- PostgreSQL SELECT count(*), sum(hashtext(id::text)) FROM users; -- 达梦 SELECT count(*), sum(hash(id, 0)) FROM users; ``` **自动验证脚本** (Python): ```python #!/usr/bin/env python3 # data_validation.py import psycopg2 import dmPython # 连接 PostgreSQL pg_conn = psycopg2.connect( host='localhost', port=5432, user='postgres', password='postgres', database='pandawiki' ) # 连接达梦 dm_conn = dmPython.connect( host='localhost', port=5236, user='pandawiki', password='PandaWiki@123' ) tables = [ 'users', 'knowledge_bases', 'nodes', 'node_versions', 'apps', 'conversations', 'conversation_messages', 'models', 'user_comments', 'contributes' ] print("开始验证数据...") errors = [] for table in tables: # 查询行数 pg_cur = pg_conn.cursor() pg_cur.execute(f"SELECT count(*) FROM {table}") pg_count = pg_cur.fetchone()[0] dm_cur = dm_conn.cursor() dm_cur.execute(f"SELECT count(*) FROM {table}") dm_count = dm_cur.fetchone()[0] if pg_count == dm_count: print(f"✓ {table}: {pg_count} 行") else: error_msg = f"✗ {table}: PostgreSQL={pg_count}, 达梦={dm_count}" print(error_msg) errors.append(error_msg) if errors: print("\n验证失败!") for error in errors: print(error) exit(1) else: print("\n✓ 所有数据验证通过!") ``` ### 5.2 功能测试 **测试清单**: - [ ] 用户登录/登出 - [ ] 知识库创建/编辑/删除 - [ ] 文档创建/编辑/删除 - [ ] 文档树操作(移动、排序) - [ ] 版本历史查看 - [ ] AI 对话功能 - [ ] 文件上传/下载 - [ ] 搜索功能 - [ ] 统计功能 - [ ] 评论功能 - [ ] 第三方集成(钉钉、飞书等) --- ## 六、性能优化 ### 6.1 达梦数据库优化 **优化参数**: ```ini # dm.ini # 内存优化 BUFFER = 4000 # 缓冲池大小(MB) SORT_BUF_SIZE = 500 # 排序缓冲区(MB) # 并行优化 ENABLE_PARALLEL = 1 MAX_PARALLEL_DEGREE = 8 # 统计信息 AUTO_STAT = 1 # 自动收集统计信息 # 查询优化 ENABLE_HASH_JOIN = 1 ENABLE_MERGE_JOIN = 1 ``` **手动优化**: ```sql -- 1. 更新统计信息 ANALYZE TABLE nodes; ANALYZE TABLE conversation_messages; -- 2. 重建索引 REINDEX INDEX idx_nodes_kb_id; -- 3. 清理碎片 VACUUM FULL nodes; ``` ### 6.2 索引优化 **创建必要的索引**: ```sql -- 复合索引 CREATE INDEX idx_nodes_kb_parent ON nodes(kb_id, parent_id); CREATE INDEX idx_messages_conv_created ON conversation_messages(conversation_id, created_at DESC); -- 部分索引 CREATE INDEX idx_nodes_documents ON nodes(kb_id) WHERE type = 1; -- JSON 索引(如果达梦支持) CREATE INDEX idx_nodes_meta_icon ON nodes((meta->>'icon')); ``` ### 6.3 查询优化 **使用 EXPLAIN 分析查询**: ```sql EXPLAIN SELECT * FROM nodes WHERE kb_id = 'xxx' ORDER BY position; ``` --- ## 七、回滚方案 ### 7.1 回滚准备 **保留原 PostgreSQL 环境**: - 不要删除原 PostgreSQL 数据库 - 保留完整备份 **双写策略** (可选): - 在迁移过渡期,同时写入 PostgreSQL 和达梦 - 确保数据一致性 ### 7.2 回滚步骤 ```bash # 1. 停止 .NET 应用 systemctl stop pandawiki-api systemctl stop pandawiki-consumer # 2. 修改配置文件,切回 PostgreSQL # appsettings.json { "ConnectionStrings": { "DefaultConnection": "Host=localhost;Port=5432;Database=pandawiki;Username=postgres;Password=postgres" } } # 3. 重启应用 systemctl start pandawiki-api systemctl start pandawiki-consumer # 4. 验证功能 curl http://localhost:8000/health ``` --- ## 八、迁移时间表 ### 8.1 迁移计划 | 阶段 | 任务 | 预计时间 | 负责人 | |------|------|----------|--------| | **准备阶段** | | | | | 1 | 安装配置达梦数据库 | 1 天 | DBA | | 2 | 备份 PostgreSQL 数据库 | 0.5 天 | DBA | | 3 | 转换表结构脚本 | 1 天 | 开发 | | **迁移阶段** | | | | | 4 | 表结构迁移 | 0.5 天 | DBA | | 5 | 数据迁移(小表) | 1 天 | DBA | | 6 | 数据迁移(大表) | 2 天 | DBA | | 7 | 索引和约束迁移 | 0.5 天 | DBA | | **验证阶段** | | | | | 8 | 数据完整性验证 | 1 天 | DBA + 测试 | | 9 | 功能测试 | 2 天 | 测试 | | 10 | 性能测试 | 1 天 | 测试 | | **上线阶段** | | | | | 11 | 灰度发布 | 1 天 | 运维 | | 12 | 全量上线 | 1 天 | 运维 | | 13 | 监控观察 | 3 天 | 运维 | **总计**:约 **15 个工作日** ### 8.2 风险控制 | 风险 | 概率 | 影响 | 应对措施 | |------|------|------|----------| | 数据丢失 | 低 | 高 | 完整备份 + 验证脚本 | | 性能下降 | 中 | 中 | 性能测试 + 优化 | | 功能异常 | 中 | 高 | 充分测试 + 灰度发布 | | 兼容性问题 | 低 | 中 | PG 兼容模式 + 预测试 | --- ## 九、总结 本迁移方案提供了从 PostgreSQL 到达梦数据库的完整迁移路径: 1. **使用 PostgreSQL 兼容模式**:最小化迁移成本 2. **分阶段迁移**:降低风险,确保可控 3. **完整验证**:数据、功能、性能全方位验证 4. **保留回滚能力**:确保业务连续性 **关键成功因素**: - 充分的准备和测试 - 完整的备份策略 - 详细的验证步骤 - 灰度发布降低风险 --- **文档版本**:v1.0 **编写时间**:2025-10-30 **审核状态**:待审核
张猛
2025年11月3日 10:13
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码