diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..f19bd29 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,51 @@ +# DataTools Pro 设计文档 + +本目录包含DataTools Pro项目的完整设计文档,为开发者、运维人员和用户提供详细的技术参考。 + +## 文档结构 + +### 📋 核心设计文档 +- **[系统架构设计](architecture.md)** - 整体系统架构、技术选型和设计原则 +- **[API接口设计](api-design.md)** - REST API接口规范和数据结构定义 +- **[数据库设计](database-design.md)** - 数据模型、表结构和关系设计 +- **[前端模块化设计](frontend-architecture.md)** - 前端架构、模块化和组件设计 + +### 🚀 部署和运维 +- **[部署指南](deployment.md)** - 生产环境部署、配置和监控 +- **[运维手册](operations.md)** - 日常运维、故障排查和性能优化 + +### 📖 用户文档 +- **[用户使用手册](user-manual.md)** - 功能使用指南和最佳实践 +- **[开发者指南](developer-guide.md)** - 开发环境搭建和二次开发指南 + +### 🔧 技术规范 +- **[代码规范](coding-standards.md)** - 代码风格、命名规范和最佳实践 +- **[安全规范](security-guidelines.md)** - 安全设计、数据保护和风险管控 + +### 📊 业务文档 +- **[需求分析](requirements.md)** - 业务需求、功能规格和用例分析 +- **[测试计划](testing-plan.md)** - 测试策略、用例设计和质量保证 + +## 文档版本 + +| 文档版本 | 系统版本 | 更新日期 | 主要变更 | +|---------|---------|----------|----------| +| v2.0.0 | DataTools Pro 2.0 | 2024-08-05 | 完整的系统设计文档创建 | + +## 文档贡献 + +如需更新文档,请遵循以下原则: +1. 保持文档结构清晰,使用标准Markdown格式 +2. 及时更新版本信息和变更日志 +3. 确保代码示例和配置信息的准确性 +4. 添加必要的图片和流程图说明 + +## 联系方式 + +- **项目维护者**: DataTools Pro Team +- **技术支持**: 请提交Issue到项目仓库 +- **文档反馈**: 欢迎提出改进建议 + +--- + +**DataTools Pro** - 企业级数据处理与比对解决方案 \ No newline at end of file diff --git a/docs/api-design.md b/docs/api-design.md new file mode 100644 index 0000000..cc8403f --- /dev/null +++ b/docs/api-design.md @@ -0,0 +1,759 @@ +# DataTools Pro API接口设计文档 + +## 1. API概述 + +### 1.1 设计原则 +- **RESTful设计**: 遵循REST架构风格 +- **统一格式**: 标准化的请求和响应格式 +- **版本控制**: 支持API版本管理 +- **错误处理**: 完整的错误码和错误信息 +- **安全性**: 输入验证和权限控制 + +### 1.2 基础信息 +- **Base URL**: `http://localhost:5000` +- **Content-Type**: `application/json` +- **字符编码**: `UTF-8` +- **API版本**: `v1.0` + +### 1.3 响应格式规范 +```json +{ + "success": true, + "data": {}, + "message": "操作成功", + "timestamp": "2024-08-05T10:30:00Z", + "request_id": "uuid-string" +} +``` + +## 2. 核心API端点 + +### 2.1 Cassandra数据比对API + +#### 2.1.1 执行单表查询比对 +**端点**: `POST /api/query` + +**功能**: 执行Cassandra单表数据查询和比对分析 + +**请求参数**: +```json +{ + "pro_config": { + "cluster_name": "production-cluster", + "datacenter": "datacenter1", + "hosts": ["10.0.1.100", "10.0.1.101"], + "port": 9042, + "username": "cassandra", + "password": "password", + "keyspace": "production_ks", + "table": "user_data" + }, + "test_config": { + "cluster_name": "test-cluster", + "datacenter": "datacenter1", + "hosts": ["10.0.2.100"], + "port": 9042, + "username": "cassandra", + "password": "password", + "keyspace": "test_ks", + "table": "user_data" + }, + "keys": ["user_id"], + "values": ["1001", "1002", "1003"], + "fields_to_compare": ["name", "email", "status"], + "exclude_fields": ["created_at", "updated_at"] +} +``` + +**响应数据**: +```json +{ + "success": true, + "data": { + "total_keys": 3, + "pro_count": 3, + "test_count": 2, + "differences": [ + { + "key": {"user_id": "1001"}, + "field": "email", + "pro_value": "user1@prod.com", + "test_value": "user1@test.com", + "message": "字段值不匹配" + } + ], + "identical_results": [ + { + "key": {"user_id": "1002"}, + "pro_fields": {"name": "User2", "email": "user2@example.com"}, + "test_fields": {"name": "User2", "email": "user2@example.com"} + } + ], + "field_diff_count": { + "email": 1 + }, + "raw_pro_data": [...], + "raw_test_data": [...], + "summary": { + "overview": "查询了3个Key,发现1处差异", + "percentages": { + "match_rate": 66.67, + "diff_rate": 33.33 + }, + "field_analysis": { + "email": {"diff_count": 1, "diff_rate": 33.33} + }, + "recommendations": ["建议检查邮箱字段的数据同步"] + } + }, + "message": "查询比对完成", + "execution_time": 1.25, + "timestamp": "2024-08-05T10:30:00Z" +} +``` + +#### 2.1.2 执行分表查询比对 +**端点**: `POST /api/sharding-query` + +**功能**: 执行Cassandra分表数据查询和比对分析 + +**请求参数**: +```json +{ + "pro_config": { /* 同单表查询配置 */ }, + "test_config": { /* 同单表查询配置 */ }, + "keys": ["doc_id"], + "values": ["wmid_1609459200", "wmid_1609545600"], + "fields_to_compare": ["content", "status"], + "exclude_fields": [], + "sharding_config": { + "use_sharding_for_pro": true, + "use_sharding_for_test": false, + "interval_seconds": 604800, + "table_count": 14 + } +} +``` + +**响应数据**: +```json +{ + "success": true, + "data": { + /* 基础比对结果同单表查询 */ + "sharding_info": { + "pro_shard_mapping": { + "wmid_1609459200": "user_data_0", + "wmid_1609545600": "user_data_1" + }, + "test_shard_mapping": { + "wmid_1609459200": "user_data", + "wmid_1609545600": "user_data" + }, + "failed_keys": [], + "shard_stats": { + "pro_tables_used": ["user_data_0", "user_data_1"], + "test_tables_used": ["user_data"], + "timestamp_extraction_success_rate": 100.0 + } + } + }, + "message": "分表查询比对完成", + "execution_time": 2.15 +} +``` + +### 2.2 Redis集群比对API + +#### 2.2.1 执行Redis集群比对 +**端点**: `POST /api/redis/compare` + +**功能**: 执行Redis集群数据比对分析 + +**请求参数**: +```json +{ + "cluster1_config": { + "name": "生产集群", + "nodes": [ + {"host": "10.0.1.100", "port": 6379}, + {"host": "10.0.1.101", "port": 6380} + ], + "password": "redis_password", + "socket_timeout": 3, + "socket_connect_timeout": 3, + "max_connections_per_node": 16 + }, + "cluster2_config": { + "name": "测试集群", + "nodes": [{"host": "10.0.2.100", "port": 6379}], + "password": null + }, + "query_mode": "specified", + "keys": ["user:1001", "user:1002", "session:abc123"], + "sample_config": { + "count": 100, + "pattern": "*", + "source_cluster": "cluster2" + } +} +``` + +**响应数据**: +```json +{ + "success": true, + "data": { + "total_keys": 3, + "cluster1_found": 2, + "cluster2_found": 3, + "differences": [ + { + "key": "user:1001", + "cluster1_value": "{\"name\":\"John\",\"age\":25}", + "cluster2_value": "{\"name\":\"John\",\"age\":26}", + "value_type": "string", + "difference_type": "value_mismatch" + } + ], + "identical": [ + { + "key": "user:1002", + "value": "{\"name\":\"Jane\",\"age\":30}", + "value_type": "string" + } + ], + "missing_in_cluster1": ["session:abc123"], + "missing_in_cluster2": [], + "cluster_stats": { + "cluster1": { + "connection_status": "connected", + "response_time_avg": 0.15, + "nodes_status": [ + {"host": "10.0.1.100", "port": 6379, "status": "connected"}, + {"host": "10.0.1.101", "port": 6380, "status": "connected"} + ] + }, + "cluster2": { + "connection_status": "connected", + "response_time_avg": 0.12, + "nodes_status": [ + {"host": "10.0.2.100", "port": 6379, "status": "connected"} + ] + } + }, + "performance_summary": { + "total_execution_time": 0.85, + "keys_per_second": 3.53, + "data_transferred_kb": 2.1 + } + }, + "message": "Redis集群比对完成" +} +``` + +### 2.3 配置管理API + +#### 2.3.1 获取默认配置 +**端点**: `GET /api/default-config` + +**功能**: 获取系统默认数据库配置 + +**响应数据**: +```json +{ + "success": true, + "data": { + "pro_config": { + "cluster_name": "production-cluster", + "datacenter": "datacenter1", + "hosts": ["127.0.0.1"], + "port": 9042, + "username": "", + "password": "", + "keyspace": "production_ks", + "table": "table_name" + }, + "test_config": { + "cluster_name": "test-cluster", + "datacenter": "datacenter1", + "hosts": ["127.0.0.1"], + "port": 9042, + "username": "", + "password": "", + "keyspace": "test_ks", + "table": "table_name" + } + } +} +``` + +#### 2.3.2 创建配置组 +**端点**: `POST /api/config-groups` + +**请求参数**: +```json +{ + "name": "生产环境配置", + "description": "生产环境Cassandra配置组", + "pro_config": { /* Cassandra配置 */ }, + "test_config": { /* Cassandra配置 */ }, + "query_config": { + "keys": ["user_id"], + "fields_to_compare": [], + "exclude_fields": [] + }, + "sharding_config": { + "use_sharding_for_pro": false, + "use_sharding_for_test": false, + "interval_seconds": 604800, + "table_count": 14 + } +} +``` + +**响应数据**: +```json +{ + "success": true, + "data": { + "id": 1, + "name": "生产环境配置", + "created_at": "2024-08-05T10:30:00Z" + }, + "message": "配置组创建成功" +} +``` + +#### 2.3.3 获取配置组列表 +**端点**: `GET /api/config-groups` + +**响应数据**: +```json +{ + "success": true, + "data": [ + { + "id": 1, + "name": "生产环境配置", + "description": "生产环境Cassandra配置组", + "created_at": "2024-08-05T10:30:00Z", + "updated_at": "2024-08-05T10:30:00Z" + } + ] +} +``` + +#### 2.3.4 获取特定配置组 +**端点**: `GET /api/config-groups/{id}` + +**响应数据**: +```json +{ + "success": true, + "data": { + "id": 1, + "name": "生产环境配置", + "description": "生产环境Cassandra配置组", + "pro_config": { /* 完整配置 */ }, + "test_config": { /* 完整配置 */ }, + "query_config": { /* 查询配置 */ }, + "sharding_config": { /* 分表配置 */ }, + "created_at": "2024-08-05T10:30:00Z", + "updated_at": "2024-08-05T10:30:00Z" + } +} +``` + +#### 2.3.5 删除配置组 +**端点**: `DELETE /api/config-groups/{id}` + +**响应数据**: +```json +{ + "success": true, + "data": null, + "message": "配置组删除成功" +} +``` + +### 2.4 查询历史管理API + +#### 2.4.1 获取查询历史列表 +**端点**: `GET /api/query-history` + +**查询参数**: +- `limit`: 返回记录数量限制 (默认50) +- `offset`: 偏移量 (默认0) +- `query_type`: 查询类型 (`single`/`sharding`) + +**响应数据**: +```json +{ + "success": true, + "data": { + "items": [ + { + "id": 1, + "name": "用户数据比对-20240805", + "description": "生产环境用户数据比对", + "query_type": "single", + "total_keys": 100, + "differences_count": 5, + "identical_count": 95, + "execution_time": 2.5, + "created_at": "2024-08-05T10:30:00Z" + } + ], + "total": 1, + "has_more": false + } +} +``` + +#### 2.4.2 保存查询历史 +**端点**: `POST /api/query-history` + +**请求参数**: +```json +{ + "name": "用户数据比对-20240805", + "description": "生产环境用户数据比对", + "pro_config": { /* 生产配置 */ }, + "test_config": { /* 测试配置 */ }, + "query_config": { /* 查询配置 */ }, + "query_keys": ["1001", "1002", "1003"], + "results_summary": { + "total_keys": 3, + "differences_count": 1, + "identical_count": 2 + }, + "execution_time": 1.25, + "query_type": "single", + "sharding_config": null, + "raw_results": { /* 完整查询结果 */ } +} +``` + +#### 2.4.3 获取历史记录详情 +**端点**: `GET /api/query-history/{id}` + +**响应数据**: +```json +{ + "success": true, + "data": { + "id": 1, + "name": "用户数据比对-20240805", + "description": "生产环境用户数据比对", + "pro_config": { /* 完整配置 */ }, + "test_config": { /* 完整配置 */ }, + "query_config": { /* 查询配置 */ }, + "query_keys": ["1001", "1002", "1003"], + "results_summary": { /* 结果摘要 */ }, + "execution_time": 1.25, + "query_type": "single", + "created_at": "2024-08-05T10:30:00Z" + } +} +``` + +#### 2.4.4 获取历史记录完整结果 +**端点**: `GET /api/query-history/{id}/results` + +**响应数据**: +```json +{ + "success": true, + "data": { + "differences": [ /* 完整差异数据 */ ], + "identical_results": [ /* 完整相同数据 */ ], + "raw_pro_data": [ /* 生产原始数据 */ ], + "raw_test_data": [ /* 测试原始数据 */ ], + "field_diff_count": { /* 字段差异统计 */ }, + "summary": { /* 详细分析报告 */ } + } +} +``` + +### 2.5 日志管理API + +#### 2.5.1 获取查询日志 +**端点**: `GET /api/query-logs` + +**查询参数**: +- `limit`: 返回记录数量 (默认100) +- `level`: 日志级别 (`INFO`/`WARNING`/`ERROR`) +- `history_id`: 关联的历史记录ID + +**响应数据**: +```json +{ + "success": true, + "data": { + "logs": [ + { + "id": 1, + "batch_id": "batch-uuid-123", + "history_id": 1, + "timestamp": "2024-08-05T10:30:01.123Z", + "level": "INFO", + "message": "开始执行Cassandra查询", + "query_type": "cassandra_single", + "created_at": "2024-08-05T10:30:01Z" + }, + { + "id": 2, + "batch_id": "batch-uuid-123", + "history_id": 1, + "timestamp": "2024-08-05T10:30:02.456Z", + "level": "INFO", + "message": "生产环境查询完成,返回3条记录", + "query_type": "cassandra_single", + "created_at": "2024-08-05T10:30:02Z" + } + ], + "total": 2 + } +} +``` + +#### 2.5.2 获取特定历史记录的日志 +**端点**: `GET /api/query-logs/history/{id}` + +**响应数据**: +```json +{ + "success": true, + "data": { + "history_id": 1, + "logs": [ /* 该历史记录相关的所有日志 */ ], + "log_summary": { + "total_logs": 10, + "info_count": 8, + "warning_count": 1, + "error_count": 1, + "start_time": "2024-08-05T10:30:00Z", + "end_time": "2024-08-05T10:30:05Z" + } + } +} +``` + +#### 2.5.3 清空查询日志 +**端点**: `DELETE /api/query-logs` + +**响应数据**: +```json +{ + "success": true, + "data": { + "deleted_count": 150 + }, + "message": "查询日志清空成功" +} +``` + +### 2.6 系统管理API + +#### 2.6.1 初始化数据库 +**端点**: `POST /api/init-db` + +**功能**: 初始化SQLite数据库表结构 + +**响应数据**: +```json +{ + "success": true, + "data": { + "tables_created": [ + "config_groups", + "query_history", + "query_logs" + ] + }, + "message": "数据库初始化成功" +} +``` + +#### 2.6.2 系统健康检查 +**端点**: `GET /api/health` + +**响应数据**: +```json +{ + "success": true, + "data": { + "status": "healthy", + "version": "2.0.0", + "uptime": "2 days, 3 hours, 45 minutes", + "database": { + "sqlite": { + "status": "connected", + "file_size_mb": 15.2 + } + }, + "memory_usage": { + "used_mb": 128.5, + "available_mb": 3967.5 + }, + "last_check": "2024-08-05T10:30:00Z" + } +} +``` + +## 3. 错误处理 + +### 3.1 错误响应格式 +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "请求参数验证失败", + "details": { + "field": "pro_config.hosts", + "issue": "hosts字段不能为空" + } + }, + "timestamp": "2024-08-05T10:30:00Z", + "request_id": "uuid-string" +} +``` + +### 3.2 错误码定义 + +| 错误码 | HTTP状态码 | 说明 | +|--------|-----------|------| +| `VALIDATION_ERROR` | 400 | 请求参数验证失败 | +| `CONNECTION_ERROR` | 500 | 数据库连接失败 | +| `QUERY_ERROR` | 500 | 查询执行失败 | +| `TIMEOUT_ERROR` | 408 | 请求超时 | +| `NOT_FOUND` | 404 | 资源不存在 | +| `CONFLICT` | 409 | 资源冲突 | +| `SYSTEM_ERROR` | 500 | 系统内部错误 | +| `AUTH_ERROR` | 401 | 认证失败 | +| `PERMISSION_DENIED` | 403 | 权限不足 | + +### 3.3 详细错误场景 + +#### 3.3.1 连接错误 +```json +{ + "success": false, + "error": { + "code": "CONNECTION_ERROR", + "message": "无法连接到Cassandra集群", + "details": { + "cluster": "production-cluster", + "hosts": ["10.0.1.100", "10.0.1.101"], + "error_detail": "Connection refused", + "suggestions": [ + "检查网络连通性", + "验证主机地址和端口", + "确认Cassandra服务状态" + ] + } + } +} +``` + +#### 3.3.2 查询错误 +```json +{ + "success": false, + "error": { + "code": "QUERY_ERROR", + "message": "CQL查询执行失败", + "details": { + "query": "SELECT * FROM user_data WHERE user_id IN (?)", + "error_detail": "Invalid keyspace name 'invalid_ks'", + "suggestions": [ + "检查keyspace名称是否正确", + "确认表名拼写无误", + "验证字段名是否存在" + ] + } + } +} +``` + +## 4. 认证和授权 + +### 4.1 认证机制 +当前版本暂未实现认证机制,所有API端点均为开放访问。在生产环境中建议实现以下认证方式: + +- **API Key认证**: 基于API密钥的简单认证 +- **JWT Token**: JSON Web Token认证 +- **OAuth 2.0**: 标准OAuth认证流程 +- **LDAP集成**: 企业级LDAP认证 + +### 4.2 权限控制 +建议实施基于角色的访问控制(RBAC): + +```json +{ + "roles": [ + { + "name": "admin", + "permissions": ["read", "write", "delete", "config"] + }, + { + "name": "operator", + "permissions": ["read", "write"] + }, + { + "name": "viewer", + "permissions": ["read"] + } + ] +} +``` + +## 5. API版本管理 + +### 5.1 版本策略 +- **URL版本控制**: `/api/v1/query`, `/api/v2/query` +- **Header版本控制**: `Accept: application/vnd.datatools.v1+json` +- **向后兼容**: 保持旧版本API的兼容性 +- **弃用策略**: 提前通知API弃用计划 + +### 5.2 版本变更记录 + +| API版本 | 发布日期 | 主要变更 | 兼容性 | +|---------|----------|----------|--------| +| v1.0 | 2024-08-05 | 初始版本发布 | N/A | + +## 6. 性能和限制 + +### 6.1 API限制 +- **请求频率**: 每分钟最多100次请求 +- **并发连接**: 最多10个并发连接 +- **响应大小**: 单次响应最大50MB +- **查询超时**: 默认120秒超时 + +### 6.2 性能优化 +- **连接池**: 复用数据库连接 +- **缓存策略**: 配置数据缓存 +- **异步处理**: 长时间查询异步执行 +- **分页处理**: 大数据集分页返回 + +## 7. 监控和日志 + +### 7.1 API监控指标 +- **响应时间**: 平均响应时间和95分位数 +- **成功率**: API调用成功率统计 +- **错误率**: 各类错误的发生率 +- **吞吐量**: 每秒处理的请求数 + +### 7.2 日志记录 +- **访问日志**: 记录所有API访问 +- **错误日志**: 详细的错误信息和堆栈 +- **性能日志**: 慢查询和性能瓶颈 +- **审计日志**: 重要操作的审计记录 + +--- + +**版本**: v1.0 +**更新日期**: 2024-08-05 +**维护者**: DataTools Pro Team \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..86b02b8 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,387 @@ +# DataTools Pro 系统架构设计 + +## 1. 系统概述 + +### 1.1 项目简介 +DataTools Pro 是一个企业级数据处理与比对工具平台,专注于提供高效、精准、可视化的数据分析解决方案。系统支持Cassandra数据库和Redis集群的数据比对分析,具备分表查询、多主键查询、配置管理等企业级功能。 + +### 1.2 设计目标 +- **高性能**: 支持大规模数据查询和比对处理 +- **高可用**: 企业级稳定性和容错能力 +- **易扩展**: 模块化设计,便于功能扩展 +- **用户友好**: 直观的Web界面和操作体验 +- **安全性**: 数据安全和访问控制 + +### 1.3 技术栈 +``` +前端技术栈: +├── 原生JavaScript (ES6+) +├── Bootstrap 5.1.3 +├── Font Awesome 6.0.0 +└── 模块化架构 (ES6 Modules) + +后端技术栈: +├── Python 3.7+ +├── Flask 2.3.3 +├── Cassandra Driver 3.29.1 +├── Redis Client +└── SQLite (配置存储) + +数据存储: +├── Apache Cassandra (主要数据源) +├── Redis Cluster (缓存和数据源) +└── SQLite (配置和历史数据) +``` + +## 2. 整体架构 + +### 2.1 架构概览 +``` +┌─────────────────────────────────────────────────────────────┐ +│ DataTools Pro 架构图 │ +├─────────────────────────────────────────────────────────────┤ +│ 前端层 (Frontend Layer) │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ +│ │ 首页模块 │ │ Cassandra工具 │ │ Redis工具 ││ +│ │ index.html │ │ db_compare.html │ │redis_compare.html││ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘│ +│ │ │ │ │ +│ ┌───────────────────────────────────────────────────────────┐│ +│ │ 模块化JavaScript架构 ││ +│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐││ +│ │ │ config │ │ utils │ │ api │ │ ui │ │ nav │││ +│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘││ +│ └───────────────────────────────────────────────────────────┘│ +├─────────────────────────────────────────────────────────────┤ +│ 网关层 (Gateway Layer) │ +│ ┌───────────────────────────────────────────────────────────┐│ +│ │ Flask Web Server ││ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐││ +│ │ │ 静态资源 │ │ API路由 │ │ 模板渲染 │││ +│ │ │ 服务 │ │ 处理 │ │ 引擎 │││ +│ │ └─────────────┘ └─────────────┘ └─────────────────────────┘││ +│ └───────────────────────────────────────────────────────────┘│ +├─────────────────────────────────────────────────────────────┤ +│ 业务逻辑层 (Business Logic Layer) │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ +│ │ 查询引擎 │ │ 数据比对 │ │ 配置管理 ││ +│ │ QueryEngine │ │ DataComparison │ │ ConfigManager ││ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘│ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ +│ │ 分表处理 │ │ 日志收集 │ │ Redis处理 ││ +│ │ ShardingCalc │ │ QueryLogger │ │ RedisClient ││ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘│ +├─────────────────────────────────────────────────────────────┤ +│ 数据访问层 (Data Access Layer) │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ +│ │ Cassandra │ │ Redis Cluster │ │ SQLite DB ││ +│ │ 客户端 │ │ 客户端 │ │ 本地存储 ││ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘│ +├─────────────────────────────────────────────────────────────┤ +│ 数据存储层 (Data Storage Layer) │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ +│ │ 生产环境 │ │ 测试环境 │ │ 配置数据 ││ +│ │ Cassandra │ │ Cassandra │ │ SQLite ││ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘│ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ 生产Redis │ │ 测试Redis │ │ +│ │ 集群 │ │ 集群 │ │ +│ └─────────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 2.2 核心组件 + +#### 2.2.1 前端架构 +```javascript +// 模块化架构设计 +static/js/ +├── app-main.js // 主应用入口,统一管理 +└── modules/ + ├── config.js // 配置管理 - API端点、UI配置 + ├── utils.js // 工具函数 - 通用功能库 + ├── api.js // HTTP客户端 - 统一请求处理 + ├── ui.js // UI组件 - 提示、模态框、分页 + └── navigation.js // 导航管理 - 路由、面包屑、快捷键 +``` + +**设计模式**: 模块化 + 单例模式 +**核心特性**: +- ES6模块化,避免全局变量污染 +- 统一的配置管理和错误处理 +- 响应式UI组件和交互增强 +- 键盘快捷键和无障碍支持 + +#### 2.2.2 后端架构 +```python +# 模块化后端设计 +modules/ +├── api_routes.py // API路由定义和请求处理 +├── cassandra_client.py // Cassandra数据库连接和操作 +├── redis_client.py // Redis集群连接和操作 +├── query_engine.py // 查询引擎和SQL构建 +├── data_comparison.py // 数据比对算法和分析 +├── sharding.py // 分表计算和时间戳处理 +├── config_manager.py // 配置组管理和持久化 +├── query_logger.py // 查询日志收集和管理 +└── database.py // SQLite数据库操作 +``` + +**设计模式**: 分层架构 + 依赖注入 +**核心特性**: +- 单一职责原则,模块解耦 +- 统一的错误处理和日志记录 +- 连接池管理和资源优化 +- 配置驱动的灵活部署 + +## 3. 核心功能模块 + +### 3.1 Cassandra数据比对模块 + +#### 3.1.1 功能特性 +- **多模式查询**: 单表查询、分表查询、多主键查询 +- **时间分表支持**: TWCS (Time Window Compaction Strategy) 分表计算 +- **复合主键**: 支持多字段组合主键查询 +- **字段级比对**: 支持包含/排除字段的精细化比较 +- **JSON处理**: 智能JSON字段识别和深度比较 + +#### 3.1.2 核心算法 +```python +# 分表索引计算算法 +def calculate_shard_index(timestamp, interval_seconds, table_count): + """ + 计算分表索引 + - timestamp: 从Key中提取的时间戳 + - interval_seconds: 分表时间间隔(默认604800秒=7天) + - table_count: 分表数量(默认14张表) + """ + return int(timestamp) // interval_seconds % table_count + +# 时间戳提取算法 +def extract_timestamp_from_key(key): + """ + 从Key中提取时间戳 - 删除所有非数字字符 + 示例: 'wmid_1609459200' -> 1609459200 + """ + return re.sub(r'\D', '', key) + +# 复合主键匹配算法 +def match_composite_key(record, key_fields, key_values): + """ + 复合主键匹配逻辑 + - 支持单主键和复合主键统一处理 + - 自动类型转换和字段验证 + """ + if len(key_fields) == 1: + return str(record.get(key_fields[0])) == str(key_values[0]) + else: + return all( + str(record.get(field)) == str(value) + for field, value in zip(key_fields, key_values) + ) +``` + +### 3.2 Redis集群比对模块 + +#### 3.2.1 功能特性 +- **多数据类型支持**: String、Hash、List、Set、ZSet +- **查询模式**: 随机采样、指定Key查询 +- **集群管理**: 多节点集群配置和连接管理 +- **性能监控**: 连接状态、查询耗时统计 +- **批量操作**: 大批量Key的并行处理 + +#### 3.2.2 Redis集群架构 +```python +# Redis集群连接配置 +{ + "cluster_name": "生产集群", + "nodes": [ + {"host": "127.0.0.1", "port": 6379}, + {"host": "127.0.0.1", "port": 6380} + ], + "password": "optional_password", + "socket_timeout": 3, + "socket_connect_timeout": 3, + "max_connections_per_node": 16 +} +``` + +### 3.3 数据比对引擎 + +#### 3.3.1 比对算法 +```python +# 数据比对核心逻辑 +class DataComparison: + def compare_results(self, pro_data, test_data, key_fields): + """ + 数据比对主算法: + 1. 构建数据索引映射 (基于主键) + 2. 逐字段深度比较 + 3. JSON字段特殊处理 + 4. 数组字段顺序无关比较 + 5. 生成差异报告和统计 + """ + differences = [] + identical_results = [] + field_diff_count = {} + + # 构建生产数据索引 + pro_index = self.build_data_index(pro_data, key_fields) + test_index = self.build_data_index(test_data, key_fields) + + # 执行比对逻辑 + for key, pro_record in pro_index.items(): + if key in test_index: + test_record = test_index[key] + diffs = self.compare_records(pro_record, test_record) + if diffs: + differences.extend(diffs) + else: + identical_results.append({ + 'key': key, + 'pro_fields': pro_record, + 'test_fields': test_record + }) + + return { + 'differences': differences, + 'identical_results': identical_results, + 'field_diff_count': field_diff_count, + 'summary': self.generate_summary(differences, identical_results) + } +``` + +#### 3.3.2 JSON处理算法 +```python +def compare_json_values(pro_value, test_value): + """ + JSON值比较算法: + 1. 智能检测JSON格式 + 2. 规范化JSON字符串 + 3. 深度比较对象结构 + 4. 数组顺序无关比较 + """ + if self.is_json_field(pro_value) and self.is_json_field(test_value): + try: + pro_obj = json.loads(pro_value) + test_obj = json.loads(test_value) + return self.deep_compare_objects(pro_obj, test_obj) + except: + return pro_value == test_value + return pro_value == test_value +``` + +## 4. 数据流架构 + +### 4.1 查询执行流程 +```mermaid +graph TD + A[用户请求] --> B[前端验证] + B --> C[API路由] + C --> D{查询类型} + D -->|单表查询| E[QueryEngine.execute_query] + D -->|分表查询| F[QueryEngine.execute_sharding_query] + E --> G[数据库连接] + F --> H[分表计算] + H --> G + G --> I[并行查询执行] + I --> J[数据收集] + J --> K[DataComparison.compare_results] + K --> L[结果分析] + L --> M[日志记录] + M --> N[响应返回] +``` + +### 4.2 配置管理流程 +```mermaid +graph TD + A[配置创建/更新] --> B[ConfigManager验证] + B --> C[SQLite持久化] + C --> D[配置缓存更新] + D --> E[前端状态同步] + + F[配置加载] --> G[SQLite查询] + G --> H[配置反序列化] + H --> I[前端配置应用] +``` + +## 5. 性能优化策略 + +### 5.1 查询优化 +- **连接池管理**: 复用数据库连接,减少连接开销 +- **并行查询**: 多线程并行执行生产和测试环境查询 +- **分批处理**: 大量Key分批查询,避免内存溢出 +- **索引优化**: 合理设计Cassandra分区键和聚簇键 + +### 5.2 前端性能 +- **懒加载**: 按需加载JS模块和数据 +- **虚拟分页**: 大数据集的分页展示 +- **防抖节流**: 用户输入和搜索的性能优化 +- **缓存策略**: 查询结果和配置数据的客户端缓存 + +### 5.3 内存管理 +- **流式处理**: 大数据集的流式读取和处理 +- **及时释放**: 查询完成后及时释放数据库连接和内存 +- **垃圾回收**: Python和JavaScript的内存回收优化 + +## 6. 扩展性设计 + +### 6.1 水平扩展 +- **无状态设计**: 应用层无状态,支持负载均衡 +- **配置外部化**: 支持配置中心和环境变量 +- **容器化**: Docker容器化部署 +- **微服务拆分**: 可拆分为独立的查询服务和比对服务 + +### 6.2 功能扩展 +- **插件架构**: 支持自定义数据源和比对算法 +- **API开放**: RESTful API支持第三方集成 +- **规则引擎**: 可配置的数据验证和比对规则 +- **报告生成**: 支持多种格式的报告导出 + +## 7. 安全架构 + +### 7.1 数据安全 +- **连接加密**: 支持SSL/TLS加密连接 +- **密码管理**: 敏感信息加密存储 +- **访问控制**: 基于角色的权限管理 +- **审计日志**: 完整的操作审计记录 + +### 7.2 系统安全 +- **输入验证**: 严格的参数验证和SQL注入防护 +- **错误处理**: 不暴露敏感系统信息 +- **会话管理**: 安全的会话管理机制 +- **网络安全**: 防火墙和网络隔离 + +## 8. 监控和运维 + +### 8.1 系统监控 +- **性能指标**: 查询耗时、成功率、错误率 +- **资源监控**: CPU、内存、网络使用情况 +- **业务指标**: 查询量、用户活跃度、功能使用统计 +- **告警机制**: 异常情况的实时告警 + +### 8.2 日志管理 +- **结构化日志**: JSON格式的结构化日志 +- **日志分级**: INFO、WARNING、ERROR、DEBUG +- **日志轮转**: 自动日志清理和归档 +- **日志分析**: 支持ELK等日志分析工具 + +## 9. 技术债务和改进计划 + +### 9.1 当前技术债务 +- **单体应用**: 所有功能集中在单个Flask应用中 +- **前端技术**: 使用原生JavaScript,缺乏现代化框架 +- **测试覆盖**: 缺乏完整的单元测试和集成测试 +- **文档完善**: API文档和代码注释需要完善 + +### 9.2 改进计划 +- **微服务改造**: 逐步拆分为独立的微服务 +- **前端升级**: 考虑引入Vue.js或React框架 +- **CI/CD**: 建立完整的持续集成和部署流程 +- **性能测试**: 建立性能基准和压力测试 + +--- + +**版本**: v2.0.0 +**更新日期**: 2024-08-05 +**维护者**: DataTools Pro Team \ No newline at end of file diff --git a/docs/database-design.md b/docs/database-design.md new file mode 100644 index 0000000..c075195 --- /dev/null +++ b/docs/database-design.md @@ -0,0 +1,603 @@ +# DataTools Pro 数据库设计文档 + +## 1. 数据库概述 + +### 1.1 数据库架构 +DataTools Pro采用多数据库架构,针对不同的数据存储需求选择最适合的数据库技术: + +``` +数据库架构图: +┌─────────────────────────────────────────────────────────┐ +│ DataTools Pro 数据库架构 │ +├─────────────────────────────────────────────────────────┤ +│ 应用层 (Application Layer) │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Flask应用程序 │ │ +│ └─────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────┤ +│ 数据访问层 (Data Access Layer) │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ Cassandra │ │ Redis │ │ SQLite │ │ +│ │ Driver │ │ Client │ │ Driver │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ +├─────────────────────────────────────────────────────────┤ +│ 数据存储层 (Data Storage Layer) │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ Apache │ │ Redis │ │ SQLite │ │ +│ │ Cassandra │ │ Cluster │ │ 本地文件 │ │ +│ │ 集群 │ │ │ │ config_groups.db │ │ +│ │ (外部数据源) │ │ (外部数据源) │ │ (配置和历史数据) │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +### 1.2 数据库分类 + +#### 1.2.1 外部数据源 +- **Apache Cassandra**: 主要的数据比对源,存储业务数据 +- **Redis Cluster**: 缓存和高性能数据存储,支持多种数据类型 + +#### 1.2.2 内部存储 +- **SQLite**: 轻量级关系数据库,存储系统配置、查询历史和日志 + +### 1.3 设计原则 +- **数据隔离**: 不同类型数据使用不同的存储方案 +- **性能优化**: 根据访问模式选择合适的数据库 +- **易维护**: 简化数据库管理和备份恢复 +- **扩展性**: 支持数据量增长和功能扩展 + +## 2. SQLite数据库设计 + +### 2.1 数据库文件 +- **文件名**: `config_groups.db` +- **位置**: 项目根目录 +- **编码**: UTF-8 +- **版本**: SQLite 3.x + +### 2.2 表结构设计 + +#### 2.2.1 config_groups表 - 配置组管理 +```sql +CREATE TABLE config_groups ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, -- 配置组名称 + description TEXT, -- 描述信息 + pro_config TEXT NOT NULL, -- 生产环境配置(JSON) + test_config TEXT NOT NULL, -- 测试环境配置(JSON) + query_config TEXT NOT NULL, -- 查询配置(JSON) + sharding_config TEXT, -- 分表配置(JSON) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- 索引 +CREATE INDEX idx_config_groups_name ON config_groups(name); +CREATE INDEX idx_config_groups_created_at ON config_groups(created_at); +``` + +**字段详解**: +- `id`: 主键,自增整数 +- `name`: 配置组名称,唯一索引 +- `description`: 配置组描述,可为空 +- `pro_config`: 生产环境Cassandra配置,JSON格式 +- `test_config`: 测试环境Cassandra配置,JSON格式 +- `query_config`: 查询参数配置,JSON格式 +- `sharding_config`: 分表查询配置,JSON格式 +- `created_at`: 创建时间 +- `updated_at`: 更新时间 + +**JSON配置示例**: +```json +{ + "pro_config": { + "cluster_name": "production-cluster", + "datacenter": "datacenter1", + "hosts": ["10.0.1.100", "10.0.1.101"], + "port": 9042, + "username": "cassandra", + "password": "encrypted_password", + "keyspace": "production_ks", + "table": "user_data" + }, + "query_config": { + "keys": ["user_id"], + "fields_to_compare": ["name", "email", "status"], + "exclude_fields": ["created_at", "updated_at"] + }, + "sharding_config": { + "use_sharding_for_pro": true, + "use_sharding_for_test": false, + "interval_seconds": 604800, + "table_count": 14 + } +} +``` + +#### 2.2.2 query_history表 - 查询历史记录 +```sql +CREATE TABLE query_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, -- 历史记录名称 + description TEXT, -- 描述信息 + pro_config TEXT NOT NULL, -- 生产环境配置(JSON) + test_config TEXT NOT NULL, -- 测试环境配置(JSON) + query_config TEXT NOT NULL, -- 查询配置(JSON) + query_keys TEXT NOT NULL, -- 查询的键值(JSON Array) + results_summary TEXT NOT NULL, -- 结果摘要(JSON) + execution_time REAL NOT NULL, -- 执行时间(秒) + total_keys INTEGER NOT NULL, -- 总Key数量 + differences_count INTEGER NOT NULL, -- 差异数量 + identical_count INTEGER NOT NULL, -- 相同数量 + query_type TEXT NOT NULL DEFAULT 'single', -- 查询类型(single/sharding) + sharding_config TEXT, -- 分表配置(JSON) + raw_results TEXT, -- 完整查询结果(JSON) + differences_data TEXT, -- 差异详细数据(JSON) + identical_data TEXT, -- 相同数据详情(JSON) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- 索引 +CREATE INDEX idx_query_history_name ON query_history(name); +CREATE INDEX idx_query_history_query_type ON query_history(query_type); +CREATE INDEX idx_query_history_created_at ON query_history(created_at); +CREATE INDEX idx_query_history_execution_time ON query_history(execution_time); +``` + +**字段详解**: +- `id`: 主键,自增整数 +- `name`: 历史记录名称 +- `description`: 历史记录描述 +- `pro_config`: 生产环境配置快照 +- `test_config`: 测试环境配置快照 +- `query_config`: 查询配置快照 +- `query_keys`: 查询的Key值列表 +- `results_summary`: 查询结果摘要统计 +- `execution_time`: 查询执行时间 +- `total_keys`: 查询的总Key数量 +- `differences_count`: 发现的差异数量 +- `identical_count`: 相同记录数量 +- `query_type`: 查询类型(single/sharding) +- `sharding_config`: 分表配置(仅分表查询) +- `raw_results`: 完整的查询结果数据 +- `differences_data`: 差异数据详情 +- `identical_data`: 相同数据详情 +- `created_at`: 创建时间 + +#### 2.2.3 query_logs表 - 查询日志记录 +```sql +CREATE TABLE query_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + batch_id TEXT NOT NULL, -- 批次ID(UUID) + history_id INTEGER, -- 关联历史记录ID + timestamp DATETIME NOT NULL, -- 日志时间戳 + level TEXT NOT NULL, -- 日志级别(INFO/WARNING/ERROR) + message TEXT NOT NULL, -- 日志消息 + query_type TEXT NOT NULL, -- 查询类型标识 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (history_id) REFERENCES query_history(id) ON DELETE SET NULL +); + +-- 索引 +CREATE INDEX idx_query_logs_batch_id ON query_logs(batch_id); +CREATE INDEX idx_query_logs_history_id ON query_logs(history_id); +CREATE INDEX idx_query_logs_level ON query_logs(level); +CREATE INDEX idx_query_logs_timestamp ON query_logs(timestamp); +CREATE INDEX idx_query_logs_query_type ON query_logs(query_type); +``` + +**字段详解**: +- `id`: 主键,自增整数 +- `batch_id`: 批次标识,同一次查询操作的日志共享同一个batch_id +- `history_id`: 关联的历史记录ID,外键关联 +- `timestamp`: 日志产生的精确时间戳 +- `level`: 日志级别(INFO/WARNING/ERROR) +- `message`: 日志消息内容 +- `query_type`: 查询类型标识(cassandra_single/cassandra_sharding/redis_compare) +- `created_at`: 记录创建时间 + +### 2.3 数据库触发器 + +#### 2.3.1 自动更新updated_at触发器 +```sql +-- 配置组更新时间触发器 +CREATE TRIGGER update_config_groups_updated_at + AFTER UPDATE ON config_groups + FOR EACH ROW + BEGIN + UPDATE config_groups + SET updated_at = CURRENT_TIMESTAMP + WHERE id = NEW.id; + END; +``` + +### 2.4 数据库视图 + +#### 2.4.1 查询历史统计视图 +```sql +CREATE VIEW query_history_stats AS +SELECT + query_type, + COUNT(*) as total_queries, + AVG(execution_time) as avg_execution_time, + AVG(total_keys) as avg_keys_per_query, + AVG(differences_count) as avg_differences, + AVG(CAST(differences_count AS REAL) / total_keys * 100) as avg_diff_rate, + MIN(created_at) as first_query, + MAX(created_at) as last_query +FROM query_history +GROUP BY query_type; +``` + +#### 2.4.2 日志统计视图 +```sql +CREATE VIEW query_logs_stats AS +SELECT + DATE(created_at) as log_date, + level, + query_type, + COUNT(*) as log_count +FROM query_logs +GROUP BY DATE(created_at), level, query_type +ORDER BY log_date DESC, level; +``` + +## 3. Cassandra数据库设计 + +### 3.1 数据模型理解 +DataTools Pro作为数据比对工具,**不直接管理**Cassandra的数据模型,而是连接到现有的Cassandra集群进行数据查询和比对。 + +### 3.2 支持的Cassandra特性 + +#### 3.2.1 表结构支持 +- **单表查询**: 支持任意Cassandra表结构 +- **分表查询**: 支持TWCS(Time Window Compaction Strategy)分表模式 +- **复合主键**: 支持多字段组合主键 +- **所有数据类型**: text、int、timestamp、uuid、blob等 + +#### 3.2.2 查询模式 +```cql +-- 单主键查询示例 +SELECT * FROM keyspace.table_name +WHERE primary_key IN (?, ?, ?); + +-- 复合主键查询示例 +SELECT * FROM keyspace.table_name +WHERE (key1='value1' AND key2='value2') + OR (key1='value3' AND key2='value4'); + +-- 分表查询示例 +SELECT * FROM keyspace.table_name_0 +WHERE primary_key IN (?, ?); +SELECT * FROM keyspace.table_name_1 +WHERE primary_key IN (?); +``` + +#### 3.2.3 分表命名规范 +``` +基础表名: user_data +分表命名: user_data_0, user_data_1, user_data_2, ..., user_data_13 +分表计算: shard_index = timestamp // interval_seconds % table_count +``` + +### 3.3 连接管理 +```python +# Cassandra连接配置 +cassandra_config = { + "cluster_name": "production-cluster", + "datacenter": "datacenter1", + "hosts": ["10.0.1.100", "10.0.1.101", "10.0.1.102"], + "port": 9042, + "username": "app_user", + "password": "secure_password", + "keyspace": "application_data", + "table": "user_profiles" +} + +# 连接池配置 +connection_settings = { + "connect_timeout": 10, + "request_timeout": 30, + "load_balancing_policy": "DCAwareRoundRobinPolicy", + "retry_policy": "RetryPolicy", + "compression": "lz4" +} +``` + +## 4. Redis数据库设计 + +### 4.1 数据模型理解 +DataTools Pro支持Redis集群的数据比对,**不直接管理**Redis的数据结构,而是连接到现有的Redis集群进行数据查询和比对。 + +### 4.2 支持的Redis数据类型 + +#### 4.2.1 基本数据类型 +- **String**: 字符串类型,最常用的数据类型 +- **Hash**: 哈希表,存储字段-值对 +- **List**: 列表,有序的字符串集合 +- **Set**: 集合,无序的字符串集合 +- **Sorted Set**: 有序集合,带分数的有序字符串集合 + +#### 4.2.2 查询操作 +```python +# 不同数据类型的查询操作 +redis_operations = { + "string": "GET key", + "hash": "HGETALL key", + "list": "LRANGE key 0 -1", + "set": "SMEMBERS key", + "zset": "ZRANGE key 0 -1 WITHSCORES" +} +``` + +### 4.3 Redis集群配置 +```python +# Redis集群连接配置 +redis_config = { + "cluster_name": "production-redis", + "nodes": [ + {"host": "10.0.1.100", "port": 6379}, + {"host": "10.0.1.101", "port": 6379}, + {"host": "10.0.1.102", "port": 6379} + ], + "password": "redis_password", + "socket_timeout": 3, + "socket_connect_timeout": 3, + "max_connections_per_node": 16, + "skip_full_coverage_check": True, + "decode_responses": True +} +``` + +## 5. 数据迁移和版本管理 + +### 5.1 数据库版本控制 + +#### 5.1.1 版本信息表 +```sql +CREATE TABLE database_version ( + version TEXT PRIMARY KEY, + applied_at DATETIME DEFAULT CURRENT_TIMESTAMP, + description TEXT +); + +INSERT INTO database_version (version, description) +VALUES ('1.0.0', '初始数据库结构'); +``` + +#### 5.1.2 迁移脚本示例 +```sql +-- 版本1.0.0 -> 1.1.0 迁移脚本 +-- 添加Redis配置支持 +ALTER TABLE config_groups +ADD COLUMN redis_config TEXT; + +INSERT INTO database_version (version, description) +VALUES ('1.1.0', '添加Redis配置支持'); +``` + +### 5.2 数据备份策略 + +#### 5.2.1 SQLite备份 +```bash +# 完整备份 +cp config_groups.db config_groups_backup_$(date +%Y%m%d_%H%M%S).db + +# 增量备份(基于时间戳) +sqlite3 config_groups.db " +SELECT * FROM config_groups +WHERE updated_at > '2024-08-05 00:00:00';" > incremental_backup.sql +``` + +#### 5.2.2 备份恢复 +```bash +# 恢复完整备份 +cp config_groups_backup_20240805_100000.db config_groups.db + +# 恢复增量数据 +sqlite3 config_groups.db < incremental_backup.sql +``` + +## 6. 性能优化 + +### 6.1 SQLite优化 + +#### 6.1.1 配置优化 +```sql +-- SQLite性能优化设置 +PRAGMA journal_mode = WAL; -- 写前日志模式 +PRAGMA synchronous = NORMAL; -- 平衡性能和安全 +PRAGMA cache_size = 10000; -- 缓存页面数量 +PRAGMA temp_store = memory; -- 临时表存储在内存 +PRAGMA mmap_size = 268435456; -- 内存映射大小(256MB) +``` + +#### 6.1.2 查询优化 +```sql +-- 分页查询优化 +SELECT * FROM query_history +WHERE created_at >= ? +ORDER BY created_at DESC +LIMIT ? OFFSET ?; + +-- 统计查询优化 +SELECT query_type, COUNT(*), AVG(execution_time) +FROM query_history +WHERE created_at >= DATE('now', '-30 days') +GROUP BY query_type; +``` + +### 6.2 连接池优化 + +#### 6.2.1 Cassandra连接池 +```python +# 连接池配置 +cluster = Cluster( + hosts=['10.0.1.100', '10.0.1.101'], + port=9042, + load_balancing_policy=DCAwareRoundRobinPolicy(local_dc='datacenter1'), + default_retry_policy=RetryPolicy(), + compression=True, + protocol_version=4 +) + +# 会话池管理 +session_pool = cluster.connect() +session_pool.default_timeout = 30 +``` + +#### 6.2.2 Redis连接池 +```python +# Redis集群连接池 +from rediscluster import RedisCluster + +redis_cluster = RedisCluster( + startup_nodes=[ + {"host": "10.0.1.100", "port": 6379}, + {"host": "10.0.1.101", "port": 6379} + ], + password="redis_password", + socket_timeout=3, + socket_connect_timeout=3, + max_connections_per_node=16, + skip_full_coverage_check=True +) +``` + +## 7. 数据安全 + +### 7.1 敏感数据处理 + +#### 7.1.1 密码加密 +```python +# 配置中的密码加密存储 +import base64 +from cryptography.fernet import Fernet + +def encrypt_password(password, key): + """加密密码""" + f = Fernet(key) + encrypted_password = f.encrypt(password.encode()) + return base64.b64encode(encrypted_password).decode() + +def decrypt_password(encrypted_password, key): + """解密密码""" + f = Fernet(key) + decoded_password = base64.b64decode(encrypted_password.encode()) + return f.decrypt(decoded_password).decode() +``` + +#### 7.1.2 数据脱敏 +```python +def mask_sensitive_data(config): + """配置数据脱敏显示""" + masked_config = config.copy() + if 'password' in masked_config: + masked_config['password'] = '***masked***' + return masked_config +``` + +### 7.2 访问控制 + +#### 7.2.1 数据库权限 +```sql +-- SQLite访问控制(文件系统级别) +chmod 600 config_groups.db -- 仅所有者可读写 +chown app:app config_groups.db -- 设置正确的所有者 +``` + +#### 7.2.2 网络安全 +```python +# Cassandra SSL连接 +from cassandra.auth import PlainTextAuthProvider +from ssl import SSLContext, PROTOCOL_TLS + +ssl_context = SSLContext(PROTOCOL_TLS) +auth_provider = PlainTextAuthProvider(username='app_user', password='password') + +cluster = Cluster( + ['10.0.1.100'], + ssl_context=ssl_context, + auth_provider=auth_provider +) +``` + +## 8. 监控和维护 + +### 8.1 数据库监控 + +#### 8.1.1 SQLite监控 +```sql +-- 数据库大小监控 +SELECT + (page_count * page_size) / 1024 / 1024 as db_size_mb +FROM pragma_page_count(), pragma_page_size(); + +-- 表大小统计 +SELECT + name, + COUNT(*) as row_count +FROM sqlite_master sm +JOIN ( + SELECT 'config_groups' as name, COUNT(*) as cnt FROM config_groups + UNION ALL + SELECT 'query_history' as name, COUNT(*) as cnt FROM query_history + UNION ALL + SELECT 'query_logs' as name, COUNT(*) as cnt FROM query_logs +) t ON sm.name = t.name; +``` + +#### 8.1.2 连接监控 +```python +# 连接健康检查 +def check_database_health(): + """检查数据库连接健康状态""" + health_status = { + "sqlite": check_sqlite_health(), + "cassandra": check_cassandra_health(), + "redis": check_redis_health() + } + return health_status + +def check_sqlite_health(): + """检查SQLite健康状态""" + try: + conn = sqlite3.connect('config_groups.db') + cursor = conn.cursor() + cursor.execute("SELECT 1") + conn.close() + return {"status": "healthy", "timestamp": datetime.now()} + except Exception as e: + return {"status": "unhealthy", "error": str(e)} +``` + +### 8.2 数据维护 + +#### 8.2.1 数据清理 +```sql +-- 清理30天前的查询日志 +DELETE FROM query_logs +WHERE created_at < DATE('now', '-30 days'); + +-- 清理大型历史记录的详细数据 +UPDATE query_history +SET raw_results = NULL, + differences_data = NULL, + identical_data = NULL +WHERE created_at < DATE('now', '-90 days'); +``` + +#### 8.2.2 数据库维护 +```sql +-- SQLite数据库优化 +VACUUM; -- 重建数据库文件,回收空间 +ANALYZE; -- 更新查询计划统计信息 +PRAGMA optimize; -- 优化数据库性能 +REINDEX; -- 重建所有索引 +``` + +--- + +**版本**: v1.0 +**更新日期**: 2024-08-05 +**维护者**: DataTools Pro Team \ No newline at end of file diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..0f857a3 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,1427 @@ +# DataTools Pro 部署指南 + +## 1. 部署概述 + +### 1.1 部署架构 +DataTools Pro支持多种部署方式,从简单的单机部署到企业级的容器化集群部署。 + +``` +部署架构选择: +┌─────────────────────────────────────────────────────────────┐ +│ 部署方案对比 │ +├─────────────────────────────────────────────────────────────┤ +│ 开发环境部署 │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ • 本地开发服务器 (Flask dev server) │ │ +│ │ • SQLite数据库 │ │ +│ │ • 适用于: 开发测试、功能验证 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ 生产环境部署 │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ • WSGI服务器 (Gunicorn/uWSGI) │ │ +│ │ • 反向代理 (Nginx/Apache) │ │ +│ │ • 进程管理 (systemd/supervisor) │ │ +│ │ • 适用于: 小型生产环境 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ 容器化部署 │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ • Docker容器化 │ │ +│ │ • Docker Compose编排 │ │ +│ │ • 负载均衡和高可用 │ │ +│ │ • 适用于: 中大型生产环境 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ Kubernetes部署 │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ • K8s Deployment和Service │ │ +│ │ • 自动扩缩容和故障恢复 │ │ +│ │ • ConfigMap和Secret管理 │ │ +│ │ • 适用于: 企业级云原生环境 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 1.2 环境要求 + +#### 1.2.1 基础环境 +- **操作系统**: Linux (推荐Ubuntu 20.04+, CentOS 8+) +- **Python版本**: Python 3.7+ +- **内存**: 最低2GB,推荐4GB+ +- **磁盘**: 最低10GB,推荐50GB+ +- **网络**: 能够访问Cassandra和Redis集群 + +#### 1.2.2 软件依赖 +```bash +# 系统包依赖 +sudo apt-get update +sudo apt-get install -y \ + python3 \ + python3-pip \ + python3-venv \ + git \ + nginx \ + supervisor \ + curl \ + wget +``` + +#### 1.2.3 Python依赖 +```txt +# requirements.txt +Flask==2.3.3 +cassandra-driver==3.29.1 +redis==4.5.4 +redis-py-cluster==2.1.3 +gunicorn==21.2.0 +gevent==23.7.0 +python-dotenv==1.0.0 +PyYAML==6.0.1 +cryptography==41.0.3 +``` + +## 2. 开发环境部署 + +### 2.1 快速启动 +```bash +# 1. 克隆项目 +git clone +cd BigDataTool + +# 2. 创建虚拟环境 +python3 -m venv venv +source venv/bin/activate # Linux/Mac +# venv\Scripts\activate # Windows + +# 3. 安装依赖 +pip install -r requirements.txt + +# 4. 初始化配置 +cp config.example.py config.py + +# 5. 启动应用 +python app.py +``` + +### 2.2 开发环境配置 +```python +# config.py - 开发环境配置 +DEBUG = True +TESTING = False + +# 数据库配置 +DATABASE_URL = 'sqlite:///config_groups.db' + +# 日志配置 +LOG_LEVEL = 'DEBUG' +LOG_FILE = 'logs/datatools.log' + +# 安全配置 +SECRET_KEY = 'dev-secret-key-change-in-production' + +# Cassandra默认配置 +DEFAULT_CASSANDRA_CONFIG = { + 'hosts': ['127.0.0.1'], + 'port': 9042, + 'keyspace': 'test_ks' +} + +# Redis默认配置 +DEFAULT_REDIS_CONFIG = { + 'host': '127.0.0.1', + 'port': 6379, + 'db': 0 +} +``` + +### 2.3 开发服务脚本 +```bash +#!/bin/bash +# dev-server.sh - 开发服务器启动脚本 + +set -e + +# 检查虚拟环境 +if [[ "$VIRTUAL_ENV" == "" ]]; then + echo "请先激活虚拟环境: source venv/bin/activate" + exit 1 +fi + +# 创建必要目录 +mkdir -p logs +mkdir -p data + +# 检查配置文件 +if [ ! -f "config.py" ]; then + echo "复制配置文件: cp config.example.py config.py" + cp config.example.py config.py +fi + +# 初始化数据库 +python -c " +from app import init_database +init_database() +print('数据库初始化完成') +" + +# 启动开发服务器 +echo "启动DataTools Pro开发服务器..." +echo "访问地址: http://localhost:5000" +python app.py +``` + +## 3. 生产环境部署 + +### 3.1 系统用户创建 +```bash +# 创建专用用户 +sudo useradd -r -s /bin/false datatools +sudo mkdir -p /opt/datatools +sudo chown datatools:datatools /opt/datatools +``` + +### 3.2 应用部署 +```bash +#!/bin/bash +# deploy-production.sh - 生产环境部署脚本 + +set -e + +APP_USER="datatools" +APP_DIR="/opt/datatools" +APP_NAME="datatools-pro" +PYTHON_VERSION="3.9" + +echo "开始部署DataTools Pro到生产环境..." + +# 1. 创建应用目录 +sudo mkdir -p $APP_DIR/{app,logs,data,backup} +sudo chown -R $APP_USER:$APP_USER $APP_DIR + +# 2. 部署应用代码 +sudo -u $APP_USER git clone $APP_DIR/app +cd $APP_DIR/app + +# 3. 创建虚拟环境 +sudo -u $APP_USER python$PYTHON_VERSION -m venv $APP_DIR/venv +sudo -u $APP_USER $APP_DIR/venv/bin/pip install --upgrade pip + +# 4. 安装依赖 +sudo -u $APP_USER $APP_DIR/venv/bin/pip install -r requirements.txt + +# 5. 创建生产配置 +sudo -u $APP_USER cp config.example.py config.py +sudo -u $APP_USER cat > config.py << 'EOF' +import os +from dotenv import load_dotenv + +# 加载环境变量 +load_dotenv() + +# 基础配置 +DEBUG = False +TESTING = False +SECRET_KEY = os.getenv('SECRET_KEY', 'change-this-in-production') + +# 数据库配置 +DATABASE_URL = os.getenv('DATABASE_URL', '/opt/datatools/data/config_groups.db') + +# 日志配置 +LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO') +LOG_FILE = '/opt/datatools/logs/datatools.log' + +# 服务器配置 +HOST = os.getenv('HOST', '127.0.0.1') +PORT = int(os.getenv('PORT', 5000)) +WORKERS = int(os.getenv('WORKERS', 4)) + +# 外部服务配置 +CASSANDRA_HOSTS = os.getenv('CASSANDRA_HOSTS', '127.0.0.1').split(',') +REDIS_HOSTS = os.getenv('REDIS_HOSTS', '127.0.0.1').split(',') +EOF + +# 6. 创建环境变量文件 +sudo -u $APP_USER cat > $APP_DIR/.env << 'EOF' +# 生产环境配置 +SECRET_KEY=your-production-secret-key-here +DATABASE_URL=/opt/datatools/data/config_groups.db +LOG_LEVEL=INFO +HOST=127.0.0.1 +PORT=5000 +WORKERS=4 + +# 外部服务 +CASSANDRA_HOSTS=10.0.1.100,10.0.1.101 +REDIS_HOSTS=10.0.2.100,10.0.2.101 +EOF + +# 7. 初始化数据库 +sudo -u $APP_USER $APP_DIR/venv/bin/python -c " +import sys +sys.path.insert(0, '$APP_DIR/app') +from app import init_database +init_database() +print('数据库初始化完成') +" + +# 8. 设置权限 +sudo chown -R $APP_USER:$APP_USER $APP_DIR +sudo chmod 600 $APP_DIR/.env +sudo chmod 755 $APP_DIR/app/app.py + +echo "应用部署完成: $APP_DIR" +``` + +### 3.3 Gunicorn配置 +```python +# gunicorn.conf.py +import os +import multiprocessing + +# 服务器socket +bind = f"{os.getenv('HOST', '127.0.0.1')}:{os.getenv('PORT', 5000)}" +backlog = 2048 + +# Worker进程 +workers = int(os.getenv('WORKERS', multiprocessing.cpu_count() * 2 + 1)) +worker_class = 'gevent' +worker_connections = 1000 +max_requests = 1000 +max_requests_jitter = 100 +preload_app = True + +# 超时设置 +timeout = 120 +keepalive = 2 +graceful_timeout = 30 + +# 日志配置 +accesslog = '/opt/datatools/logs/access.log' +errorlog = '/opt/datatools/logs/error.log' +loglevel = 'info' +access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s' + +# 进程命名 +proc_name = 'datatools-pro' + +# 用户和组 +user = 'datatools' +group = 'datatools' + +# 临时目录 +tmp_upload_dir = '/opt/datatools/tmp' + +# 启动时钩子 +def on_starting(server): + server.log.info("DataTools Pro 正在启动...") + +def when_ready(server): + server.log.info("DataTools Pro 启动完成") + +def on_exit(server): + server.log.info("DataTools Pro 正在关闭...") +``` + +### 3.4 Systemd服务配置 +```ini +# /etc/systemd/system/datatools-pro.service +[Unit] +Description=DataTools Pro - Enterprise Data Processing Platform +After=network.target +Wants=network.target + +[Service] +Type=notify +User=datatools +Group=datatools +RuntimeDirectory=datatools-pro +WorkingDirectory=/opt/datatools/app +Environment=PATH=/opt/datatools/venv/bin +EnvironmentFile=/opt/datatools/.env +ExecStart=/opt/datatools/venv/bin/gunicorn --config gunicorn.conf.py app:app +ExecReload=/bin/kill -s HUP $MAINPID +KillMode=mixed +TimeoutStopSec=30 +PrivateTmp=true +ProtectSystem=strict +ReadWritePaths=/opt/datatools +NoNewPrivileges=yes + +# 重启策略 +Restart=always +RestartSec=10 +StartLimitBurst=3 +StartLimitInterval=60 + +[Install] +WantedBy=multi-user.target +``` + +```bash +# 启用和启动服务 +sudo systemctl daemon-reload +sudo systemctl enable datatools-pro +sudo systemctl start datatools-pro +sudo systemctl status datatools-pro +``` + +### 3.5 Nginx反向代理配置 +```nginx +# /etc/nginx/sites-available/datatools-pro +upstream datatools_backend { + server 127.0.0.1:5000 fail_timeout=0; + # 如果有多个worker,可以添加多个server + # server 127.0.0.1:5001 fail_timeout=0; + # server 127.0.0.1:5002 fail_timeout=0; +} + +server { + listen 80; + listen [::]:80; + server_name datatools.yourdomain.com; + + # 重定向到HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name datatools.yourdomain.com; + + # SSL配置 + ssl_certificate /etc/ssl/certs/datatools.crt; + ssl_certificate_key /etc/ssl/private/datatools.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # 安全头 + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; + + # 基础配置 + client_max_body_size 100M; + keepalive_timeout 65; + gzip on; + gzip_vary on; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/javascript + application/xml+rss + application/json; + + # 静态文件缓存 + location /static/ { + alias /opt/datatools/app/static/; + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # API接口 + location /api/ { + proxy_pass http://datatools_backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 120s; + proxy_buffering off; + } + + # 应用主体 + location / { + proxy_pass http://datatools_backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + + # 日志配置 + access_log /var/log/nginx/datatools-access.log; + error_log /var/log/nginx/datatools-error.log; +} +``` + +```bash +# 启用站点 +sudo ln -s /etc/nginx/sites-available/datatools-pro /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +## 4. 容器化部署 + +### 4.1 Dockerfile +```dockerfile +# Dockerfile +FROM python:3.9-slim + +# 设置工作目录 +WORKDIR /app + +# 安装系统依赖 +RUN apt-get update && apt-get install -y \ + gcc \ + g++ \ + && rm -rf /var/lib/apt/lists/* + +# 复制依赖文件 +COPY requirements.txt . + +# 安装Python依赖 +RUN pip install --no-cache-dir -r requirements.txt + +# 复制应用代码 +COPY . . + +# 创建非root用户 +RUN useradd -r -s /bin/false appuser && \ + chown -R appuser:appuser /app + +# 切换到非root用户 +USER appuser + +# 暴露端口 +EXPOSE 5000 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:5000/api/health || exit 1 + +# 启动命令 +CMD ["gunicorn", "--config", "gunicorn.conf.py", "app:app"] +``` + +### 4.2 Docker Compose配置 +```yaml +# docker-compose.yml +version: '3.8' + +services: + datatools-pro: + build: + context: . + dockerfile: Dockerfile + image: datatools-pro:latest + container_name: datatools-pro + restart: unless-stopped + ports: + - "5000:5000" + environment: + - DEBUG=False + - SECRET_KEY=${SECRET_KEY} + - DATABASE_URL=/app/data/config_groups.db + - LOG_LEVEL=INFO + - CASSANDRA_HOSTS=${CASSANDRA_HOSTS} + - REDIS_HOSTS=${REDIS_HOSTS} + volumes: + - ./data:/app/data + - ./logs:/app/logs + - ./backup:/app/backup + networks: + - datatools-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + nginx: + image: nginx:alpine + container_name: datatools-nginx + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./ssl:/etc/ssl:ro + - ./logs/nginx:/var/log/nginx + depends_on: + - datatools-pro + networks: + - datatools-network + + redis: + image: redis:7-alpine + container_name: datatools-redis + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - redis-data:/data + command: redis-server --appendonly yes + networks: + - datatools-network + +volumes: + redis-data: + driver: local + +networks: + datatools-network: + driver: bridge +``` + +### 4.3 环境变量配置 +```bash +# .env - Docker Compose环境变量 +COMPOSE_PROJECT_NAME=datatools-pro + +# 应用配置 +SECRET_KEY=your-super-secret-key-for-production +DEBUG=False +LOG_LEVEL=INFO + +# 外部服务 +CASSANDRA_HOSTS=cassandra-node1,cassandra-node2,cassandra-node3 +REDIS_HOSTS=redis-node1,redis-node2,redis-node3 + +# 数据库 +DATABASE_URL=/app/data/config_groups.db + +# 监控 +ENABLE_MONITORING=true +METRICS_PORT=9090 +``` + +### 4.4 容器化部署脚本 +```bash +#!/bin/bash +# deploy-docker.sh - Docker容器化部署脚本 + +set -e + +PROJECT_NAME="datatools-pro" +DOCKER_COMPOSE_FILE="docker-compose.yml" +ENV_FILE=".env" + +echo "开始Docker容器化部署..." + +# 1. 检查Docker环境 +if ! command -v docker &> /dev/null; then + echo "错误: Docker未安装" + exit 1 +fi + +if ! command -v docker-compose &> /dev/null; then + echo "错误: Docker Compose未安装" + exit 1 +fi + +# 2. 检查环境变量文件 +if [ ! -f "$ENV_FILE" ]; then + echo "创建环境变量文件: $ENV_FILE" + cp .env.example $ENV_FILE + echo "请编辑 $ENV_FILE 文件并重新运行部署脚本" + exit 1 +fi + +# 3. 创建必要目录 +mkdir -p {data,logs,backup,ssl,nginx/conf.d} + +# 4. 构建镜像 +echo "构建Docker镜像..." +docker-compose -f $DOCKER_COMPOSE_FILE build + +# 5. 停止旧容器 +echo "停止旧容器..." +docker-compose -f $DOCKER_COMPOSE_FILE down + +# 6. 启动新容器 +echo "启动新容器..." +docker-compose -f $DOCKER_COMPOSE_FILE up -d + +# 7. 等待容器启动 +echo "等待服务启动..." +sleep 30 + +# 8. 检查服务状态 +echo "检查服务状态..." +docker-compose -f $DOCKER_COMPOSE_FILE ps + +# 9. 健康检查 +echo "执行健康检查..." +if curl -f http://localhost/api/health > /dev/null 2>&1; then + echo "✅ 部署成功! 服务已启动" + echo "访问地址: http://localhost" +else + echo "❌ 部署失败! 服务未正常启动" + echo "查看日志: docker-compose logs" + exit 1 +fi + +# 10. 显示有用信息 +echo "" +echo "🚀 DataTools Pro 容器化部署完成!" +echo "" +echo "常用命令:" +echo " 查看日志: docker-compose logs -f" +echo " 重启服务: docker-compose restart" +echo " 停止服务: docker-compose down" +echo " 更新代码: git pull && docker-compose up -d --build" +echo "" +``` + +## 5. Kubernetes部署 + +### 5.1 Kubernetes资源清单 + +#### 5.1.1 ConfigMap配置 +```yaml +# k8s/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: datatools-pro-config + namespace: datatools +data: + DEBUG: "False" + LOG_LEVEL: "INFO" + DATABASE_URL: "/app/data/config_groups.db" + CASSANDRA_HOSTS: "cassandra-service.database.svc.cluster.local" + REDIS_HOSTS: "redis-service.cache.svc.cluster.local" + WORKERS: "4" + HOST: "0.0.0.0" + PORT: "5000" +``` + +#### 5.1.2 Secret配置 +```yaml +# k8s/secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: datatools-pro-secret + namespace: datatools +type: Opaque +data: + SECRET_KEY: eW91ci1zdXBlci1zZWNyZXQta2V5LWZvci1wcm9kdWN0aW9u # base64 encoded +``` + +#### 5.1.3 PersistentVolume配置 +```yaml +# k8s/pvc.yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: datatools-pro-data + namespace: datatools +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + storageClassName: fast-ssd +``` + +#### 5.1.4 Deployment配置 +```yaml +# k8s/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: datatools-pro + namespace: datatools + labels: + app: datatools-pro + version: v2.0.0 +spec: + replicas: 3 + selector: + matchLabels: + app: datatools-pro + template: + metadata: + labels: + app: datatools-pro + version: v2.0.0 + spec: + containers: + - name: datatools-pro + image: datatools-pro:2.0.0 + ports: + - containerPort: 5000 + name: http + env: + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: datatools-pro-secret + key: SECRET_KEY + envFrom: + - configMapRef: + name: datatools-pro-config + volumeMounts: + - name: data-volume + mountPath: /app/data + - name: logs-volume + mountPath: /app/logs + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "2Gi" + cpu: "1000m" + livenessProbe: + httpGet: + path: /api/health + port: 5000 + initialDelaySeconds: 30 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /api/health + port: 5000 + initialDelaySeconds: 5 + periodSeconds: 5 + securityContext: + runAsNonRoot: true + runAsUser: 1000 + readOnlyRootFilesystem: true + volumes: + - name: data-volume + persistentVolumeClaim: + claimName: datatools-pro-data + - name: logs-volume + emptyDir: {} + securityContext: + fsGroup: 1000 +``` + +#### 5.1.5 Service配置 +```yaml +# k8s/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: datatools-pro-service + namespace: datatools + labels: + app: datatools-pro +spec: + selector: + app: datatools-pro + ports: + - port: 80 + targetPort: 5000 + protocol: TCP + name: http + type: ClusterIP +``` + +#### 5.1.6 Ingress配置 +```yaml +# k8s/ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: datatools-pro-ingress + namespace: datatools + annotations: + kubernetes.io/ingress.class: nginx + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: "100m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "120" +spec: + tls: + - hosts: + - datatools.yourdomain.com + secretName: datatools-pro-tls + rules: + - host: datatools.yourdomain.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: datatools-pro-service + port: + number: 80 +``` + +### 5.2 Kubernetes部署脚本 +```bash +#!/bin/bash +# deploy-k8s.sh - Kubernetes部署脚本 + +set -e + +NAMESPACE="datatools" +KUBECTL_CMD="kubectl" +KUSTOMIZE_DIR="k8s" + +echo "开始Kubernetes部署..." + +# 1. 检查kubectl +if ! command -v kubectl &> /dev/null; then + echo "错误: kubectl未安装" + exit 1 +fi + +# 2. 检查集群连接 +if ! kubectl cluster-info &> /dev/null; then + echo "错误: 无法连接到Kubernetes集群" + exit 1 +fi + +# 3. 创建命名空间 +echo "创建命名空间: $NAMESPACE" +kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f - + +# 4. 应用ConfigMap和Secret +echo "应用配置..." +kubectl apply -f $KUSTOMIZE_DIR/configmap.yaml +kubectl apply -f $KUSTOMIZE_DIR/secret.yaml + +# 5. 应用存储 +echo "应用存储配置..." +kubectl apply -f $KUSTOMIZE_DIR/pvc.yaml + +# 6. 等待PVC绑定 +echo "等待存储卷绑定..." +kubectl wait --for=condition=Bound pvc/datatools-pro-data -n $NAMESPACE --timeout=300s + +# 7. 应用Deployment +echo "部署应用..." +kubectl apply -f $KUSTOMIZE_DIR/deployment.yaml + +# 8. 等待Deployment就绪 +echo "等待应用就绪..." +kubectl wait --for=condition=Available deployment/datatools-pro -n $NAMESPACE --timeout=300s + +# 9. 应用Service +echo "创建服务..." +kubectl apply -f $KUSTOMIZE_DIR/service.yaml + +# 10. 应用Ingress +if [ -f "$KUSTOMIZE_DIR/ingress.yaml" ]; then + echo "创建Ingress..." + kubectl apply -f $KUSTOMIZE_DIR/ingress.yaml +fi + +# 11. 检查部署状态 +echo "检查部署状态..." +kubectl get pods -n $NAMESPACE -l app=datatools-pro +kubectl get svc -n $NAMESPACE -l app=datatools-pro + +# 12. 显示访问信息 +echo "" +echo "🚀 Kubernetes部署完成!" +echo "" +echo "查看Pod状态: kubectl get pods -n $NAMESPACE" +echo "查看日志: kubectl logs -f deployment/datatools-pro -n $NAMESPACE" +echo "端口转发测试: kubectl port-forward service/datatools-pro-service 8080:80 -n $NAMESPACE" +echo "" + +if [ -f "$KUSTOMIZE_DIR/ingress.yaml" ]; then + INGRESS_HOST=$(kubectl get ingress datatools-pro-ingress -n $NAMESPACE -o jsonpath='{.spec.rules[0].host}') + echo "访问地址: https://$INGRESS_HOST" +fi +``` + +### 5.3 HorizontalPodAutoscaler配置 +```yaml +# k8s/hpa.yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: datatools-pro-hpa + namespace: datatools +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: datatools-pro + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 10 + periodSeconds: 60 + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Percent + value: 50 + periodSeconds: 60 +``` + +## 6. 监控和日志 + +### 6.1 应用监控配置 +```python +# monitoring.py - 应用监控模块 +from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST +from flask import Response +import time +import psutil +import sqlite3 + +# 定义监控指标 +REQUEST_COUNT = Counter('datatools_requests_total', 'Total requests', ['method', 'endpoint', 'status']) +REQUEST_LATENCY = Histogram('datatools_request_duration_seconds', 'Request latency') +ACTIVE_CONNECTIONS = Gauge('datatools_active_connections', 'Active database connections') +QUERY_EXECUTION_TIME = Histogram('datatools_query_duration_seconds', 'Query execution time', ['query_type']) + +# 系统资源指标 +SYSTEM_CPU_USAGE = Gauge('datatools_system_cpu_usage_percent', 'System CPU usage') +SYSTEM_MEMORY_USAGE = Gauge('datatools_system_memory_usage_percent', 'System memory usage') +DATABASE_SIZE = Gauge('datatools_database_size_bytes', 'Database file size') + +def init_monitoring(app): + """初始化监控""" + + @app.before_request + def before_request(): + """请求前处理""" + g.start_time = time.time() + + @app.after_request + def after_request(response): + """请求后处理""" + if hasattr(g, 'start_time'): + duration = time.time() - g.start_time + REQUEST_LATENCY.observe(duration) + REQUEST_COUNT.labels( + method=request.method, + endpoint=request.endpoint or 'unknown', + status=response.status_code + ).inc() + + return response + + @app.route('/metrics') + def metrics(): + """Prometheus指标端点""" + # 更新系统指标 + update_system_metrics() + + return Response(generate_latest(), mimetype=CONTENT_TYPE_LATEST) + +def update_system_metrics(): + """更新系统指标""" + # CPU使用率 + cpu_percent = psutil.cpu_percent(interval=1) + SYSTEM_CPU_USAGE.set(cpu_percent) + + # 内存使用率 + memory = psutil.virtual_memory() + SYSTEM_MEMORY_USAGE.set(memory.percent) + + # 数据库大小 + try: + import os + db_path = 'config_groups.db' + if os.path.exists(db_path): + db_size = os.path.getsize(db_path) + DATABASE_SIZE.set(db_size) + except Exception: + pass + +def record_query_time(query_type, duration): + """记录查询执行时间""" + QUERY_EXECUTION_TIME.labels(query_type=query_type).observe(duration) +``` + +### 6.2 日志配置 +```python +# logging_config.py - 日志配置 +import logging +import logging.handlers +import os +from datetime import datetime + +def setup_logging(app): + """设置应用日志""" + + # 创建日志目录 + log_dir = app.config.get('LOG_DIR', 'logs') + os.makedirs(log_dir, exist_ok=True) + + # 配置日志格式 + formatter = logging.Formatter( + '%(asctime)s [%(levelname)s] %(name)s: %(message)s' + ) + + # 应用日志 + app_handler = logging.handlers.RotatingFileHandler( + os.path.join(log_dir, 'datatools.log'), + maxBytes=10*1024*1024, # 10MB + backupCount=10 + ) + app_handler.setFormatter(formatter) + app_handler.setLevel(logging.INFO) + + # 错误日志 + error_handler = logging.handlers.RotatingFileHandler( + os.path.join(log_dir, 'error.log'), + maxBytes=10*1024*1024, # 10MB + backupCount=5 + ) + error_handler.setFormatter(formatter) + error_handler.setLevel(logging.ERROR) + + # 访问日志 + access_handler = logging.handlers.RotatingFileHandler( + os.path.join(log_dir, 'access.log'), + maxBytes=10*1024*1024, # 10MB + backupCount=10 + ) + access_formatter = logging.Formatter( + '%(asctime)s %(remote_addr)s "%(method)s %(url)s" %(status_code)s %(response_size)s "%(user_agent)s"' + ) + access_handler.setFormatter(access_formatter) + + # 配置Flask应用日志 + app.logger.addHandler(app_handler) + app.logger.addHandler(error_handler) + app.logger.setLevel(logging.INFO) + + # 配置访问日志 + access_logger = logging.getLogger('werkzeug') + access_logger.addHandler(access_handler) + + # 配置第三方库日志级别 + logging.getLogger('cassandra').setLevel(logging.WARNING) + logging.getLogger('redis').setLevel(logging.WARNING) + logging.getLogger('urllib3').setLevel(logging.WARNING) +``` + +### 6.3 Prometheus配置 +```yaml +# prometheus.yml +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + - "datatools_rules.yml" + +scrape_configs: + - job_name: 'datatools-pro' + static_configs: + - targets: ['localhost:5000'] + metrics_path: '/metrics' + scrape_interval: 30s + scrape_timeout: 10s + +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 +``` + +### 6.4 Grafana仪表板配置 +```json +{ + "dashboard": { + "id": null, + "title": "DataTools Pro Monitoring", + "tags": ["datatools", "monitoring"], + "timezone": "browser", + "panels": [ + { + "id": 1, + "title": "Request Rate", + "type": "graph", + "targets": [ + { + "expr": "rate(datatools_requests_total[5m])", + "legendFormat": "{{method}} {{endpoint}}" + } + ] + }, + { + "id": 2, + "title": "Request Latency", + "type": "graph", + "targets": [ + { + "expr": "histogram_quantile(0.95, datatools_request_duration_seconds_bucket)", + "legendFormat": "95th percentile" + }, + { + "expr": "histogram_quantile(0.50, datatools_request_duration_seconds_bucket)", + "legendFormat": "50th percentile" + } + ] + }, + { + "id": 3, + "title": "System Resources", + "type": "graph", + "targets": [ + { + "expr": "datatools_system_cpu_usage_percent", + "legendFormat": "CPU Usage %" + }, + { + "expr": "datatools_system_memory_usage_percent", + "legendFormat": "Memory Usage %" + } + ] + }, + { + "id": 4, + "title": "Query Performance", + "type": "graph", + "targets": [ + { + "expr": "histogram_quantile(0.95, datatools_query_duration_seconds_bucket)", + "legendFormat": "{{query_type}} 95th percentile" + } + ] + } + ], + "time": { + "from": "now-1h", + "to": "now" + }, + "refresh": "30s" + } +} +``` + +## 7. 备份和恢复 + +### 7.1 数据备份脚本 +```bash +#!/bin/bash +# backup.sh - 数据备份脚本 + +set -e + +# 配置变量 +BACKUP_DIR="/opt/datatools/backup" +DATA_DIR="/opt/datatools/data" +LOG_DIR="/opt/datatools/logs" +RETENTION_DAYS=30 +DATE=$(date +%Y%m%d_%H%M%S) +BACKUP_NAME="datatools_backup_$DATE" + +echo "开始数据备份: $BACKUP_NAME" + +# 创建备份目录 +mkdir -p $BACKUP_DIR/$BACKUP_NAME + +# 1. 备份SQLite数据库 +echo "备份SQLite数据库..." +if [ -f "$DATA_DIR/config_groups.db" ]; then + sqlite3 $DATA_DIR/config_groups.db ".backup $BACKUP_DIR/$BACKUP_NAME/config_groups.db" + sqlite3 $DATA_DIR/config_groups.db ".dump" > $BACKUP_DIR/$BACKUP_NAME/config_groups.sql +fi + +# 2. 备份配置文件 +echo "备份配置文件..." +cp -r /opt/datatools/app/config.py $BACKUP_DIR/$BACKUP_NAME/ 2>/dev/null || true +cp -r /opt/datatools/.env $BACKUP_DIR/$BACKUP_NAME/ 2>/dev/null || true + +# 3. 备份日志文件(最近7天) +echo "备份日志文件..." +find $LOG_DIR -name "*.log" -mtime -7 -exec cp {} $BACKUP_DIR/$BACKUP_NAME/ \; 2>/dev/null || true + +# 4. 创建备份信息文件 +cat > $BACKUP_DIR/$BACKUP_NAME/backup_info.txt << EOF +备份时间: $(date) +备份版本: DataTools Pro 2.0 +系统信息: $(uname -a) +Python版本: $(python3 --version) +数据库大小: $(du -h $DATA_DIR/config_groups.db 2>/dev/null | cut -f1 || echo "N/A") +备份大小: $(du -sh $BACKUP_DIR/$BACKUP_NAME | cut -f1) +EOF + +# 5. 压缩备份 +echo "压缩备份文件..." +cd $BACKUP_DIR +tar -czf $BACKUP_NAME.tar.gz $BACKUP_NAME +rm -rf $BACKUP_NAME + +# 6. 清理旧备份 +echo "清理旧备份..." +find $BACKUP_DIR -name "datatools_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete + +# 7. 验证备份 +if [ -f "$BACKUP_DIR/$BACKUP_NAME.tar.gz" ]; then + BACKUP_SIZE=$(du -h $BACKUP_DIR/$BACKUP_NAME.tar.gz | cut -f1) + echo "✅ 备份完成: $BACKUP_NAME.tar.gz ($BACKUP_SIZE)" +else + echo "❌ 备份失败!" + exit 1 +fi + +# 8. 可选: 上传到远程存储 +if [ -n "$BACKUP_REMOTE_PATH" ]; then + echo "上传备份到远程存储..." + # rsync -avz $BACKUP_DIR/$BACKUP_NAME.tar.gz $BACKUP_REMOTE_PATH/ + # aws s3 cp $BACKUP_DIR/$BACKUP_NAME.tar.gz s3://your-backup-bucket/ +fi + +echo "备份脚本执行完成" +``` + +### 7.2 数据恢复脚本 +```bash +#!/bin/bash +# restore.sh - 数据恢复脚本 + +set -e + +if [ $# -eq 0 ]; then + echo "用法: $0 " + echo "示例: $0 /opt/datatools/backup/datatools_backup_20240805_100000.tar.gz" + exit 1 +fi + +BACKUP_FILE="$1" +DATA_DIR="/opt/datatools/data" +RESTORE_DIR="/tmp/datatools_restore_$$" + +echo "开始数据恢复: $BACKUP_FILE" + +# 1. 验证备份文件 +if [ ! -f "$BACKUP_FILE" ]; then + echo "错误: 备份文件不存在: $BACKUP_FILE" + exit 1 +fi + +# 2. 停止服务 +echo "停止DataTools Pro服务..." +sudo systemctl stop datatools-pro || true + +# 3. 备份当前数据 +echo "备份当前数据..." +mkdir -p $DATA_DIR.bak +cp -r $DATA_DIR/* $DATA_DIR.bak/ 2>/dev/null || true + +# 4. 解压备份文件 +echo "解压备份文件..." +mkdir -p $RESTORE_DIR +cd $RESTORE_DIR +tar -xzf $BACKUP_FILE + +# 5. 恢复数据库 +echo "恢复数据库..." +BACKUP_DB=$(find $RESTORE_DIR -name "config_groups.db" | head -1) +if [ -f "$BACKUP_DB" ]; then + cp $BACKUP_DB $DATA_DIR/config_groups.db + chown datatools:datatools $DATA_DIR/config_groups.db + chmod 644 $DATA_DIR/config_groups.db + echo "✅ 数据库恢复完成" +else + echo "❌ 未找到数据库备份文件" +fi + +# 6. 恢复配置文件 +echo "恢复配置文件..." +BACKUP_CONFIG=$(find $RESTORE_DIR -name "config.py" | head -1) +if [ -f "$BACKUP_CONFIG" ]; then + cp $BACKUP_CONFIG /opt/datatools/app/config.py + chown datatools:datatools /opt/datatools/app/config.py + echo "✅ 配置文件恢复完成" +fi + +BACKUP_ENV=$(find $RESTORE_DIR -name ".env" | head -1) +if [ -f "$BACKUP_ENV" ]; then + cp $BACKUP_ENV /opt/datatools/.env + chown datatools:datatools /opt/datatools/.env + chmod 600 /opt/datatools/.env + echo "✅ 环境变量文件恢复完成" +fi + +# 7. 验证数据库完整性 +echo "验证数据库完整性..." +if sqlite3 $DATA_DIR/config_groups.db "PRAGMA integrity_check;" | grep -q "ok"; then + echo "✅ 数据库完整性检查通过" +else + echo "❌ 数据库完整性检查失败" + echo "恢复原始数据..." + cp -r $DATA_DIR.bak/* $DATA_DIR/ + exit 1 +fi + +# 8. 启动服务 +echo "启动DataTools Pro服务..." +sudo systemctl start datatools-pro + +# 9. 等待服务启动 +echo "等待服务启动..." +sleep 10 + +# 10. 健康检查 +if curl -f http://localhost:5000/api/health > /dev/null 2>&1; then + echo "✅ 恢复完成! 服务正常运行" + rm -rf $RESTORE_DIR + rm -rf $DATA_DIR.bak +else + echo "❌ 服务启动失败,回滚到原始数据" + sudo systemctl stop datatools-pro + rm -rf $DATA_DIR/* + cp -r $DATA_DIR.bak/* $DATA_DIR/ + sudo systemctl start datatools-pro + rm -rf $RESTORE_DIR + exit 1 +fi + +echo "数据恢复脚本执行完成" +``` + +### 7.3 自动化备份Cron任务 +```bash +# 添加定时备份任务 +# crontab -e + +# 每天凌晨2点执行备份 +0 2 * * * /opt/datatools/scripts/backup.sh >> /opt/datatools/logs/backup.log 2>&1 + +# 每周日凌晨3点清理旧日志 +0 3 * * 0 find /opt/datatools/logs -name "*.log.*" -mtime +7 -delete + +# 每月1号检查数据库完整性 +0 4 1 * * sqlite3 /opt/datatools/data/config_groups.db "PRAGMA integrity_check;" >> /opt/datatools/logs/db_check.log 2>&1 +``` + +--- + +**版本**: v1.0 +**更新日期**: 2024-08-05 +**维护者**: DataTools Pro Team \ No newline at end of file diff --git a/docs/frontend-architecture.md b/docs/frontend-architecture.md new file mode 100644 index 0000000..6c1d376 --- /dev/null +++ b/docs/frontend-architecture.md @@ -0,0 +1,1742 @@ +# DataTools Pro 前端模块化设计文档 + +## 1. 前端架构概述 + +### 1.1 设计理念 +DataTools Pro前端采用现代化的模块化架构,基于ES6模块系统构建,追求高内聚、低耦合的设计原则,提供可维护、可扩展的前端解决方案。 + +### 1.2 技术栈 +``` +前端技术栈: +├── 核心技术 +│ ├── 原生JavaScript (ES6+) +│ ├── ES6 Modules (import/export) +│ └── 现代浏览器API (Fetch, Promise, async/await) +├── UI框架 +│ ├── Bootstrap 5.1.3 (响应式UI框架) +│ ├── Font Awesome 6.0.0 (图标库) +│ └── 原生CSS3 (自定义样式和动画) +└── 开发模式 + ├── 模块化开发 + ├── 组件化设计 + └── 事件驱动架构 +``` + +### 1.3 架构优势 +- **无构建依赖**: 直接在浏览器中运行,无需复杂的构建工具 +- **模块化设计**: 清晰的职责分离,便于维护和测试 +- **轻量级**: 没有框架包袱,性能优异 +- **现代化**: 使用最新的JavaScript特性和API +- **兼容性**: 支持现代浏览器(Chrome 61+, Firefox 60+, Safari 10.1+) + +## 2. 模块架构图 + +``` +DataTools Pro 前端模块架构 +┌─────────────────────────────────────────────────────────────┐ +│ 应用层 (App Layer) │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ app-main.js │ │ +│ │ 主应用入口和协调器 │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ +│ │ │ 应用初始化 │ │ 生命周期 │ │ 全局事件管理 │ │ │ +│ │ │ 管理 │ │ 管理 │ │ │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ 核心模块层 (Core Modules) │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ +│ │ config.js │ │ utils.js │ │ api.js │ │ +│ │ 配置管理 │ │ 工具函数库 │ │ HTTP客户端 │ │ +│ │ ·APP_INFO │ │ ·escapeHtml │ │ ·CassandraAPI │ │ +│ │ ·API端点 │ │ ·formatDate │ │ ·RedisAPI │ │ +│ │ ·UI配置 │ │ ·debounce │ │ ·统一错误处理 │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ +│ ┌─────────────┐ ┌─────────────────────────────────────────┐ │ +│ │ ui.js │ │ navigation.js │ │ +│ │ UI组件库 │ │ 导航管理模块 │ │ +│ │ ·AlertMgr │ │ ·面包屑导航 ·键盘快捷键 │ │ +│ │ ·ModalMgr │ │ ·活跃状态管理 ·程序化导航 │ │ +│ │ ·Pagination │ │ ·页面标题管理 │ │ +│ └─────────────┘ └─────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ 页面层 (Page Layer) │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ +│ │ index.html │ │db_compare. │ │ redis_compare.html │ │ +│ │ 首页模块 │ │html │ │ Redis比对模块 │ │ +│ │ ·工具展示 │ │Cassandra │ │ ·集群配置管理 │ │ +│ │ ·统计信息 │ │比对模块 │ │ ·随机采样查询 │ │ +│ │ ·导航入口 │ │·查询配置 │ │ ·指定Key查询 │ │ +│ └─────────────┘ │·分表查询 │ │ ·数据类型比对 │ │ +│ │·多主键查询 │ └─────────────────────────┘ │ +│ └─────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## 3. 核心模块详解 + +### 3.1 主应用模块 (app-main.js) + +#### 3.1.1 模块职责 +- **应用初始化**: 统一管理各模块启动和配置 +- **生命周期管理**: 处理应用启动、运行、销毁周期 +- **全局事件管理**: 统一处理全局事件和错误 +- **模块协调**: 协调各个功能模块之间的交互 + +#### 3.1.2 核心类设计 +```javascript +/** + * 主应用程序类 + */ +class DataToolsApp { + constructor() { + this.config = config; // 配置管理 + this.utils = utils; // 工具函数 + this.api = apiManager; // API管理 + this.navigation = navigationManager; // 导航管理 + this.ui = { // UI组件集合 + alert: alertManager, + modal: modalManager, + loading: loadingIndicator, + confirm: confirmDialog, + prompt: promptDialog + }; + this.isInitialized = false; + this.currentTool = null; + } + + // 核心方法 + async init() // 应用初始化 + checkDependencies() // 依赖检查 + initUI() // UI初始化 + bindGlobalEvents() // 全局事件绑定 + detectCurrentTool() // 当前工具检测 + destroy() // 应用销毁 +} +``` + +#### 3.1.3 初始化流程 +```javascript +// 应用初始化流程 +async function initApplication() { + try { + console.log('DataTools Pro 正在初始化...'); + + // 1. 检查浏览器兼容性和依赖 + checkDependencies(); + + // 2. 初始化UI组件 + initUI(); + + // 3. 启用导航和快捷键 + navigation.enableKeyboardShortcuts(); + + // 4. 绑定全局事件 + bindGlobalEvents(); + + // 5. 检测当前工具类型 + detectCurrentTool(); + + console.log('应用程序初始化完成'); + + // 6. 显示欢迎消息 + showWelcomeMessage(); + + } catch (error) { + console.error('应用程序初始化失败:', error); + ui.alert.error('应用程序初始化失败,请刷新页面重试'); + } +} +``` + +### 3.2 配置管理模块 (config.js) + +#### 3.2.1 模块结构 +```javascript +// 应用基础信息 +export const APP_INFO = { + name: 'DataTools Pro', + version: '2.0.0', + description: '企业级数据处理与比对工具平台' +}; + +// API端点配置 +export const API_ENDPOINTS = { + // Cassandra相关API + CASSANDRA: { + DEFAULT_CONFIG: '/api/default-config', + QUERY: '/api/query', + SHARDING_QUERY: '/api/sharding-query' + }, + + // Redis相关API + REDIS: { + COMPARE: '/api/redis/compare', + CONFIG: '/api/redis/config' + }, + + // 配置管理API + CONFIG_GROUPS: { + LIST: '/api/config-groups', + CREATE: '/api/config-groups', + GET: (id) => `/api/config-groups/${id}`, + DELETE: (id) => `/api/config-groups/${id}` + }, + + // 历史记录API + QUERY_HISTORY: { + LIST: '/api/query-history', + CREATE: '/api/query-history', + GET: (id) => `/api/query-history/${id}`, + RESULTS: (id) => `/api/query-history/${id}/results`, + DELETE: (id) => `/api/query-history/${id}` + }, + + // 日志管理API + QUERY_LOGS: { + LIST: '/api/query-logs', + HISTORY: (id) => `/api/query-logs/history/${id}`, + CLEAR: '/api/query-logs' + } +}; + +// UI配置 +export const UI_CONFIG = { + PAGINATION: { + DEFAULT_PAGE_SIZE: 10, + MAX_PAGE_SIZE: 100, + SHOW_SIZE_CHANGER: true + }, + + ALERT: { + AUTO_HIDE_DELAY: 3000, + MAX_ALERTS: 5 + }, + + LOADING: { + MIN_DISPLAY_TIME: 500, + TIMEOUT: 30000 + }, + + ANIMATION: { + DURATION: 300, + EASING: 'ease-in-out' + } +}; + +// 工具配置 +export const TOOLS_CONFIG = { + CASSANDRA: { + name: 'Cassandra数据比对工具', + url: '/cassandra-compare', + icon: 'fas fa-database', + description: '企业级Cassandra数据库比对分析' + }, + + REDIS: { + name: 'Redis集群比对工具', + url: '/redis-compare', + icon: 'fab fa-redis', + description: '专业的Redis集群数据比对分析' + } +}; +``` + +#### 3.2.2 配置管理类 +```javascript +class ConfigManager { + constructor() { + this.cache = new Map(); + this.listeners = new Map(); + } + + // 获取配置值 + get(key, defaultValue = null) { + const keys = key.split('.'); + let value = this; + + for (const k of keys) { + value = value[k]; + if (value === undefined) { + return defaultValue; + } + } + + return value; + } + + // 设置配置值 + set(key, value) { + this.cache.set(key, value); + this.notifyListeners(key, value); + } + + // 监听配置变更 + onChange(key, callback) { + if (!this.listeners.has(key)) { + this.listeners.set(key, new Set()); + } + this.listeners.get(key).add(callback); + } + + // 通知监听器 + notifyListeners(key, value) { + if (this.listeners.has(key)) { + this.listeners.get(key).forEach(callback => { + callback(value, key); + }); + } + } +} +``` + +### 3.3 工具函数模块 (utils.js) + +#### 3.3.1 核心工具函数 +```javascript +/** + * HTML转义,防止XSS攻击 + */ +export function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; +} + +/** + * 日期格式化 + */ +export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { + const d = new Date(date); + const formatMap = { + 'YYYY': d.getFullYear(), + 'MM': String(d.getMonth() + 1).padStart(2, '0'), + 'DD': String(d.getDate()).padStart(2, '0'), + 'HH': String(d.getHours()).padStart(2, '0'), + 'mm': String(d.getMinutes()).padStart(2, '0'), + 'ss': String(d.getSeconds()).padStart(2, '0') + }; + + return format.replace(/YYYY|MM|DD|HH|mm|ss/g, match => formatMap[match]); +} + +/** + * 防抖函数 + */ +export function debounce(func, wait, immediate = false) { + let timeout; + return function executedFunction(...args) { + const later = () => { + timeout = null; + if (!immediate) func.apply(this, args); + }; + const callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(this, args); + }; +} + +/** + * 节流函数 + */ +export function throttle(func, limit) { + let inThrottle; + return function(...args) { + if (!inThrottle) { + func.apply(this, args); + inThrottle = true; + setTimeout(() => inThrottle = false, limit); + } + }; +} + +/** + * 深度克隆对象 + */ +export function deepClone(obj) { + if (obj === null || typeof obj !== "object") { + return obj; + } + + if (obj instanceof Date) { + return new Date(obj.getTime()); + } + + if (obj instanceof Array) { + return obj.map(item => deepClone(item)); + } + + if (typeof obj === "object") { + const clonedObj = {}; + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + clonedObj[key] = deepClone(obj[key]); + } + } + return clonedObj; + } +} + +/** + * 剪贴板操作 + */ +export const clipboard = { + async copy(text) { + try { + await navigator.clipboard.writeText(text); + return true; + } catch (err) { + // 降级方案 + const textArea = document.createElement('textarea'); + textArea.value = text; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand('copy'); + document.body.removeChild(textArea); + return true; + } + }, + + async paste() { + try { + return await navigator.clipboard.readText(); + } catch (err) { + console.warn('无法访问剪贴板'); + return ''; + } + } +}; + +/** + * 文件操作工具 + */ +export const fileUtils = { + // 下载文件 + download(data, filename, type = 'text/plain') { + const blob = new Blob([data], { type }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }, + + // 读取文件 + async readFile(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = e => resolve(e.target.result); + reader.onerror = reject; + reader.readAsText(file); + }); + } +}; +``` + +#### 3.3.2 数据处理工具 +```javascript +/** + * 数据验证工具 + */ +export const validator = { + // 非空验证 + required(value, message = '此字段不能为空') { + if (!value || value.toString().trim() === '') { + throw new Error(message); + } + return true; + }, + + // 邮箱验证 + email(value, message = '请输入有效的邮箱地址') { + const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!pattern.test(value)) { + throw new Error(message); + } + return true; + }, + + // 数字验证 + number(value, min = null, max = null, message = '请输入有效的数字') { + const num = Number(value); + if (isNaN(num)) { + throw new Error(message); + } + if (min !== null && num < min) { + throw new Error(`数值不能小于${min}`); + } + if (max !== null && num > max) { + throw new Error(`数值不能大于${max}`); + } + return true; + }, + + // JSON验证 + json(value, message = '请输入有效的JSON格式') { + try { + JSON.parse(value); + return true; + } catch (e) { + throw new Error(message); + } + } +}; + +/** + * 字符串处理工具 + */ +export const stringUtils = { + // 首字母大写 + capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + }, + + // 驼峰命名转换 + camelCase(str) { + return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase()); + }, + + // 截断字符串 + truncate(str, maxLength, suffix = '...') { + if (str.length <= maxLength) return str; + return str.substring(0, maxLength - suffix.length) + suffix; + }, + + // 格式化文件大小 + formatBytes(bytes, decimals = 2) { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i]; + } +}; +``` + +### 3.4 API管理模块 (api.js) + +#### 3.4.1 HTTP客户端封装 +```javascript +/** + * HTTP客户端类 + */ +class HttpClient { + constructor(baseURL = '', options = {}) { + this.baseURL = baseURL; + this.defaultOptions = { + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + credentials: 'same-origin', + ...options + }; + + this.interceptors = { + request: [], + response: [] + }; + } + + // 添加请求拦截器 + addRequestInterceptor(interceptor) { + this.interceptors.request.push(interceptor); + } + + // 添加响应拦截器 + addResponseInterceptor(interceptor) { + this.interceptors.response.push(interceptor); + } + + // 执行请求 + async request(url, options = {}) { + const fullUrl = this.baseURL + url; + let config = { ...this.defaultOptions, ...options }; + + // 执行请求拦截器 + for (const interceptor of this.interceptors.request) { + config = await interceptor(config); + } + + try { + let response = await fetch(fullUrl, config); + + // 执行响应拦截器 + for (const interceptor of this.interceptors.response) { + response = await interceptor(response); + } + + // 处理HTTP错误状态 + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + // 解析JSON响应 + const data = await response.json(); + return data; + + } catch (error) { + console.error('HTTP请求失败:', error); + throw error; + } + } + + // HTTP方法快捷方式 + get(url, params = {}) { + const queryString = new URLSearchParams(params).toString(); + const fullUrl = queryString ? `${url}?${queryString}` : url; + return this.request(fullUrl, { method: 'GET' }); + } + + post(url, data = {}) { + return this.request(url, { + method: 'POST', + body: JSON.stringify(data) + }); + } + + put(url, data = {}) { + return this.request(url, { + method: 'PUT', + body: JSON.stringify(data) + }); + } + + delete(url) { + return this.request(url, { method: 'DELETE' }); + } +} +``` + +#### 3.4.2 API管理器 +```javascript +/** + * API管理器类 + */ +class APIManager { + constructor() { + this.httpClient = new HttpClient(); + this.setupInterceptors(); + } + + // 设置拦截器 + setupInterceptors() { + // 请求拦截器 - 添加时间戳和请求ID + this.httpClient.addRequestInterceptor(async (config) => { + config.headers['X-Request-ID'] = this.generateRequestId(); + config.headers['X-Timestamp'] = Date.now(); + return config; + }); + + // 响应拦截器 - 统一错误处理 + this.httpClient.addResponseInterceptor(async (response) => { + const data = await response.json(); + + if (!data.success) { + throw new APIError(data.error?.message || '请求失败', data.error?.code); + } + + return data; + }); + } + + // 生成请求ID + generateRequestId() { + return 'req_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + } + + // Cassandra API + get cassandra() { + return { + getDefaultConfig: () => + this.httpClient.get(API_ENDPOINTS.CASSANDRA.DEFAULT_CONFIG), + + executeQuery: (config) => + this.httpClient.post(API_ENDPOINTS.CASSANDRA.QUERY, config), + + executeShardingQuery: (config) => + this.httpClient.post(API_ENDPOINTS.CASSANDRA.SHARDING_QUERY, config) + }; + } + + // Redis API + get redis() { + return { + compareData: (config) => + this.httpClient.post(API_ENDPOINTS.REDIS.COMPARE, config) + }; + } + + // 配置组API + get configGroups() { + return { + list: () => + this.httpClient.get(API_ENDPOINTS.CONFIG_GROUPS.LIST), + + create: (config) => + this.httpClient.post(API_ENDPOINTS.CONFIG_GROUPS.CREATE, config), + + get: (id) => + this.httpClient.get(API_ENDPOINTS.CONFIG_GROUPS.GET(id)), + + delete: (id) => + this.httpClient.delete(API_ENDPOINTS.CONFIG_GROUPS.DELETE(id)) + }; + } + + // 查询历史API + get queryHistory() { + return { + list: (params = {}) => + this.httpClient.get(API_ENDPOINTS.QUERY_HISTORY.LIST, params), + + create: (history) => + this.httpClient.post(API_ENDPOINTS.QUERY_HISTORY.CREATE, history), + + get: (id) => + this.httpClient.get(API_ENDPOINTS.QUERY_HISTORY.GET(id)), + + getResults: (id) => + this.httpClient.get(API_ENDPOINTS.QUERY_HISTORY.RESULTS(id)), + + delete: (id) => + this.httpClient.delete(API_ENDPOINTS.QUERY_HISTORY.DELETE(id)) + }; + } + + // 日志API + get queryLogs() { + return { + list: (params = {}) => + this.httpClient.get(API_ENDPOINTS.QUERY_LOGS.LIST, params), + + getHistory: (id) => + this.httpClient.get(API_ENDPOINTS.QUERY_LOGS.HISTORY(id)), + + clear: () => + this.httpClient.delete(API_ENDPOINTS.QUERY_LOGS.CLEAR) + }; + } +} + +/** + * API错误类 + */ +class APIError extends Error { + constructor(message, code = 'UNKNOWN_ERROR') { + super(message); + this.name = 'APIError'; + this.code = code; + } +} +``` + +### 3.5 UI组件模块 (ui.js) + +#### 3.5.1 提示管理器 +```javascript +/** + * 提示信息管理器 + */ +class AlertManager { + constructor() { + this.alerts = []; + this.container = null; + this.init(); + } + + init() { + // 创建提示容器 + this.container = document.createElement('div'); + this.container.className = 'alert-container'; + this.container.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + z-index: 9999; + max-width: 400px; + `; + document.body.appendChild(this.container); + } + + show(type, message, autoHide = UI_CONFIG.ALERT.AUTO_HIDE_DELAY) { + const alertId = 'alert_' + Date.now(); + const alertElement = this.createAlertElement(alertId, type, message); + + this.container.appendChild(alertElement); + this.alerts.push({ id: alertId, element: alertElement }); + + // 限制提示数量 + if (this.alerts.length > UI_CONFIG.ALERT.MAX_ALERTS) { + this.removeOldest(); + } + + // 自动隐藏 + if (autoHide > 0) { + setTimeout(() => this.hide(alertId), autoHide); + } + + return alertId; + } + + createAlertElement(id, type, message) { + const alert = document.createElement('div'); + alert.id = id; + alert.className = `alert alert-${type} alert-dismissible fade show`; + alert.innerHTML = ` + + ${escapeHtml(message)} + + `; + + // 绑定关闭事件 + alert.querySelector('.btn-close').addEventListener('click', () => { + this.hide(id); + }); + + return alert; + } + + getIcon(type) { + const icons = { + success: 'check-circle', + error: 'exclamation-circle', + warning: 'exclamation-triangle', + info: 'info-circle' + }; + return icons[type] || 'info-circle'; + } + + hide(alertId) { + const alertIndex = this.alerts.findIndex(alert => alert.id === alertId); + if (alertIndex !== -1) { + const alert = this.alerts[alertIndex]; + alert.element.remove(); + this.alerts.splice(alertIndex, 1); + } + } + + // 便捷方法 + success(message, autoHide) { return this.show('success', message, autoHide); } + error(message, autoHide) { return this.show('error', message, autoHide); } + warning(message, autoHide) { return this.show('warning', message, autoHide); } + info(message, autoHide) { return this.show('info', message, autoHide); } +} +``` + +#### 3.5.2 分页组件 +```javascript +/** + * 分页组件 + */ +class PaginationComponent { + constructor(container, options = {}) { + this.container = typeof container === 'string' ? + document.querySelector(container) : container; + + this.options = { + pageSize: UI_CONFIG.PAGINATION.DEFAULT_PAGE_SIZE, + showSizeChanger: UI_CONFIG.PAGINATION.SHOW_SIZE_CHANGER, + showTotal: true, + showQuickJumper: true, + ...options + }; + + this.currentPage = 1; + this.totalItems = 0; + this.totalPages = 0; + + this.onPageChange = options.onPageChange || (() => {}); + this.onSizeChange = options.onSizeChange || (() => {}); + } + + // 更新分页数据 + update(totalItems, currentPage = 1) { + this.totalItems = totalItems; + this.currentPage = currentPage; + this.totalPages = Math.ceil(totalItems / this.options.pageSize); + this.render(); + } + + // 渲染分页组件 + render() { + if (!this.container) return; + + const pagination = this.createPaginationElement(); + this.container.innerHTML = ''; + this.container.appendChild(pagination); + } + + createPaginationElement() { + const nav = document.createElement('nav'); + nav.innerHTML = ` +
+
+ ${this.renderTotalInfo()} +
+
    + ${this.renderPaginationItems()} +
+
+ ${this.renderSizeChanger()} + ${this.renderQuickJumper()} +
+
+ `; + + this.bindEvents(nav); + return nav; + } + + renderTotalInfo() { + if (!this.options.showTotal) return ''; + + const start = (this.currentPage - 1) * this.options.pageSize + 1; + const end = Math.min(this.currentPage * this.options.pageSize, this.totalItems); + + return ` + + 显示 ${start}-${end} 项,共 ${this.totalItems} 项 + + `; + } + + renderPaginationItems() { + let items = []; + + // 上一页 + items.push(` +
  • + + + +
  • + `); + + // 页码 + const pageNumbers = this.generatePageNumbers(); + pageNumbers.forEach(page => { + if (page === '...') { + items.push('
  • ...
  • '); + } else { + items.push(` +
  • + ${page} +
  • + `); + } + }); + + // 下一页 + items.push(` +
  • + + + +
  • + `); + + return items.join(''); + } + + generatePageNumbers() { + const total = this.totalPages; + const current = this.currentPage; + const pages = []; + + if (total <= 7) { + for (let i = 1; i <= total; i++) { + pages.push(i); + } + } else { + if (current <= 4) { + pages.push(1, 2, 3, 4, 5, '...', total); + } else if (current >= total - 3) { + pages.push(1, '...', total - 4, total - 3, total - 2, total - 1, total); + } else { + pages.push(1, '...', current - 1, current, current + 1, '...', total); + } + } + + return pages; + } + + bindEvents(nav) { + // 分页点击事件 + nav.addEventListener('click', (e) => { + if (e.target.matches('.page-link[data-page]')) { + e.preventDefault(); + const page = parseInt(e.target.dataset.page); + if (page > 0 && page <= this.totalPages) { + this.goToPage(page); + } + } + }); + + // 页面大小变更事件 + const sizeSelect = nav.querySelector('.page-size-select'); + if (sizeSelect) { + sizeSelect.addEventListener('change', (e) => { + this.changePageSize(parseInt(e.target.value)); + }); + } + + // 快速跳转事件 + const jumpInput = nav.querySelector('.page-jump-input'); + if (jumpInput) { + jumpInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + const page = parseInt(e.target.value); + if (page > 0 && page <= this.totalPages) { + this.goToPage(page); + } + } + }); + } + } + + goToPage(page) { + if (page !== this.currentPage) { + this.currentPage = page; + this.render(); + this.onPageChange(page, this.options.pageSize); + } + } + + changePageSize(pageSize) { + this.options.pageSize = pageSize; + this.currentPage = 1; + this.totalPages = Math.ceil(this.totalItems / pageSize); + this.render(); + this.onSizeChange(pageSize, this.currentPage); + } +} +``` + +### 3.6 导航管理模块 (navigation.js) + +#### 3.6.1 导航管理器 +```javascript +/** + * 导航管理器类 + */ +class NavigationManager { + constructor() { + this.currentPath = window.location.pathname; + this.breadcrumbContainer = null; + this.shortcuts = new Map(); + this.init(); + } + + init() { + this.updateActiveNavigation(); + this.createBreadcrumb(); + this.bindNavigationEvents(); + } + + // 更新活跃导航状态 + updateActiveNavigation() { + document.querySelectorAll('.nav-link').forEach(link => { + link.classList.remove('active'); + }); + + // 根据当前路径设置活跃状态 + const currentNavLink = this.findActiveNavLink(); + if (currentNavLink) { + currentNavLink.classList.add('active'); + } + } + + findActiveNavLink() { + // 直接匹配 + let link = document.querySelector(`a[href="${this.currentPath}"]`); + if (link && link.classList.contains('nav-link')) { + return link; + } + + // 路由别名匹配 + if (this.currentPath.includes('db-compare') || + this.currentPath.includes('cassandra-compare')) { + return document.querySelector('a[href="/cassandra-compare"]'); + } + + if (this.currentPath.includes('redis-compare')) { + return document.querySelector('a[href="/redis-compare"]'); + } + + if (this.currentPath === '/') { + return document.querySelector('a[href="/"]'); + } + + return null; + } + + // 创建面包屑导航 + createBreadcrumb() { + const breadcrumbItems = this.getBreadcrumbItems(); + if (breadcrumbItems.length <= 1) return; + + this.breadcrumbContainer = this.findOrCreateBreadcrumbContainer(); + this.renderBreadcrumb(breadcrumbItems); + } + + getBreadcrumbItems() { + const items = [ + { title: '首页', url: '/', icon: 'fas fa-home' } + ]; + + if (this.currentPath.includes('cassandra-compare') || + this.currentPath.includes('db-compare')) { + items.push({ + title: 'Cassandra数据比对', + url: '/cassandra-compare', + icon: 'fas fa-database' + }); + } else if (this.currentPath.includes('redis-compare')) { + items.push({ + title: 'Redis集群比对', + url: '/redis-compare', + icon: 'fab fa-redis' + }); + } + + return items; + } + + // 启用键盘快捷键 + enableKeyboardShortcuts() { + // 定义快捷键 + this.shortcuts.set('ctrl+h', { + action: () => this.navigateTo('/'), + description: '返回首页' + }); + + this.shortcuts.set('ctrl+1', { + action: () => this.navigateTo('/cassandra-compare'), + description: 'Cassandra工具' + }); + + this.shortcuts.set('ctrl+2', { + action: () => this.navigateTo('/redis-compare'), + description: 'Redis工具' + }); + + // 绑定键盘事件 + document.addEventListener('keydown', (e) => { + const key = this.getKeyString(e); + const shortcut = this.shortcuts.get(key); + + if (shortcut) { + e.preventDefault(); + shortcut.action(); + } + }); + + // 显示快捷键提示 + this.createShortcutsTooltip(); + } + + getKeyString(event) { + const keys = []; + + if (event.ctrlKey || event.metaKey) keys.push('ctrl'); + if (event.altKey) keys.push('alt'); + if (event.shiftKey) keys.push('shift'); + + keys.push(event.key.toLowerCase()); + + return keys.join('+'); + } + + createShortcutsTooltip() { + const tooltip = document.createElement('div'); + tooltip.className = 'keyboard-shortcuts-tooltip'; + tooltip.style.cssText = ` + position: fixed; + bottom: 100px; + right: 30px; + z-index: 1000; + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 10px; + border-radius: 5px; + font-size: 12px; + display: none; + `; + + let html = '
    键盘快捷键:
    '; + this.shortcuts.forEach((shortcut, key) => { + const displayKey = key.replace('ctrl', 'Ctrl').replace('+', ' + '); + html += `
    ${displayKey} ${shortcut.description}
    `; + }); + + tooltip.innerHTML = html; + document.body.appendChild(tooltip); + + // 按?键显示/隐藏 + document.addEventListener('keydown', (e) => { + if (e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) { + e.preventDefault(); + tooltip.style.display = tooltip.style.display === 'none' ? 'block' : 'none'; + } + }); + } + + // 程序化导航 + navigateTo(url, replace = false) { + if (replace) { + window.history.replaceState(null, '', url); + } else { + window.location.href = url; + } + } + + // 更新页面标题 + updatePageTitle(title) { + const toolInfo = this.getCurrentToolInfo(); + if (toolInfo) { + document.title = `${title} - ${toolInfo.name} - DataTools Pro`; + } else { + document.title = `${title} - DataTools Pro`; + } + } +} +``` + +## 4. 模块间通信 + +### 4.1 事件驱动架构 +```javascript +/** + * 事件总线 - 模块间通信 + */ +class EventBus { + constructor() { + this.events = new Map(); + } + + // 监听事件 + on(eventName, callback) { + if (!this.events.has(eventName)) { + this.events.set(eventName, new Set()); + } + this.events.get(eventName).add(callback); + } + + // 移除监听 + off(eventName, callback) { + if (this.events.has(eventName)) { + this.events.get(eventName).delete(callback); + } + } + + // 触发事件 + emit(eventName, ...args) { + if (this.events.has(eventName)) { + this.events.get(eventName).forEach(callback => { + try { + callback(...args); + } catch (error) { + console.error(`事件处理错误 [${eventName}]:`, error); + } + }); + } + } + + // 一次性监听 + once(eventName, callback) { + const onceCallback = (...args) => { + callback(...args); + this.off(eventName, onceCallback); + }; + this.on(eventName, onceCallback); + } +} + +// 全局事件总线实例 +const eventBus = new EventBus(); +``` + +### 4.2 模块依赖注入 +```javascript +/** + * 依赖注入容器 + */ +class DIContainer { + constructor() { + this.services = new Map(); + this.instances = new Map(); + } + + // 注册服务 + register(name, factory, singleton = true) { + this.services.set(name, { factory, singleton }); + } + + // 获取服务实例 + get(name) { + if (!this.services.has(name)) { + throw new Error(`Service '${name}' not found`); + } + + const service = this.services.get(name); + + if (service.singleton) { + if (!this.instances.has(name)) { + this.instances.set(name, service.factory()); + } + return this.instances.get(name); + } else { + return service.factory(); + } + } + + // 清除实例缓存 + clear(name) { + if (name) { + this.instances.delete(name); + } else { + this.instances.clear(); + } + } +} + +// 全局DI容器 +const container = new DIContainer(); + +// 注册核心服务 +container.register('config', () => config); +container.register('utils', () => utils); +container.register('api', () => apiManager); +container.register('navigation', () => navigationManager); +container.register('eventBus', () => eventBus); +``` + +## 5. 性能优化 + +### 5.1 模块懒加载 +```javascript +/** + * 模块懒加载管理器 + */ +class LazyLoader { + constructor() { + this.loadedModules = new Set(); + this.loadingPromises = new Map(); + } + + async loadModule(modulePath) { + if (this.loadedModules.has(modulePath)) { + return Promise.resolve(); + } + + if (this.loadingPromises.has(modulePath)) { + return this.loadingPromises.get(modulePath); + } + + const loadPromise = import(modulePath).then(module => { + this.loadedModules.add(modulePath); + this.loadingPromises.delete(modulePath); + return module; + }); + + this.loadingPromises.set(modulePath, loadPromise); + return loadPromise; + } + + async loadModules(modulePaths) { + return Promise.all(modulePaths.map(path => this.loadModule(path))); + } +} +``` + +### 5.2 缓存策略 +```javascript +/** + * 缓存管理器 + */ +class CacheManager { + constructor(maxSize = 100) { + this.cache = new Map(); + this.maxSize = maxSize; + this.accessOrder = []; + } + + set(key, value, ttl = 0) { + const item = { + value, + timestamp: Date.now(), + ttl + }; + + if (this.cache.has(key)) { + this.updateAccessOrder(key); + } else { + if (this.cache.size >= this.maxSize) { + this.evictLRU(); + } + this.accessOrder.push(key); + } + + this.cache.set(key, item); + } + + get(key) { + const item = this.cache.get(key); + + if (!item) return null; + + // 检查过期时间 + if (item.ttl > 0 && Date.now() - item.timestamp > item.ttl) { + this.delete(key); + return null; + } + + this.updateAccessOrder(key); + return item.value; + } + + delete(key) { + this.cache.delete(key); + const index = this.accessOrder.indexOf(key); + if (index > -1) { + this.accessOrder.splice(index, 1); + } + } + + updateAccessOrder(key) { + const index = this.accessOrder.indexOf(key); + if (index > -1) { + this.accessOrder.splice(index, 1); + } + this.accessOrder.push(key); + } + + evictLRU() { + const oldestKey = this.accessOrder.shift(); + this.cache.delete(oldestKey); + } + + clear() { + this.cache.clear(); + this.accessOrder = []; + } +} +``` + +## 6. 错误处理和调试 + +### 6.1 全局错误处理 +```javascript +/** + * 全局错误处理器 + */ +class ErrorHandler { + constructor() { + this.setupGlobalHandlers(); + this.errorReporters = []; + } + + setupGlobalHandlers() { + // JavaScript错误 + window.addEventListener('error', (e) => { + this.handleError(e.error, 'javascript'); + }); + + // Promise拒绝错误 + window.addEventListener('unhandledrejection', (e) => { + this.handleError(e.reason, 'promise'); + e.preventDefault(); + }); + + // 资源加载错误 + window.addEventListener('error', (e) => { + if (e.target !== window) { + this.handleError(new Error(`资源加载失败: ${e.target.src || e.target.href}`), 'resource'); + } + }, true); + } + + handleError(error, type = 'unknown') { + const errorInfo = { + message: error.message || error.toString(), + stack: error.stack, + type, + timestamp: new Date().toISOString(), + url: window.location.href, + userAgent: navigator.userAgent + }; + + console.error('Global Error:', errorInfo); + + // 通知错误报告器 + this.errorReporters.forEach(reporter => { + try { + reporter(errorInfo); + } catch (reportError) { + console.error('Error reporter failed:', reportError); + } + }); + + // 显示用户友好的错误信息 + if (type !== 'resource') { + this.showUserError(error); + } + } + + showUserError(error) { + if (window.DataToolsApp && window.DataToolsApp.ui) { + window.DataToolsApp.ui.alert.error( + '系统出现错误,请刷新页面后重试' + ); + } + } + + addErrorReporter(reporter) { + this.errorReporters.push(reporter); + } +} +``` + +### 6.2 调试工具 +```javascript +/** + * 开发调试工具 + */ +class DebugTool { + constructor() { + this.isEnabled = this.checkDebugMode(); + this.logs = []; + this.maxLogs = 1000; + + if (this.isEnabled) { + this.setupDebugConsole(); + } + } + + checkDebugMode() { + return window.location.search.includes('debug=true') || + localStorage.getItem('datatools_debug') === 'true'; + } + + log(message, data = null) { + if (!this.isEnabled) return; + + const logEntry = { + timestamp: Date.now(), + message, + data, + stack: new Error().stack + }; + + this.logs.push(logEntry); + + if (this.logs.length > this.maxLogs) { + this.logs.shift(); + } + + console.log(`[DEBUG] ${message}`, data); + } + + setupDebugConsole() { + // 添加全局调试命令 + window.DataToolsDebug = { + getLogs: () => this.logs, + clearLogs: () => { this.logs = []; }, + exportLogs: () => { + const data = JSON.stringify(this.logs, null, 2); + fileUtils.download(data, 'datatools-debug.json', 'application/json'); + }, + getAppState: () => window.DataToolsApp, + getModules: () => ({ + config, + utils, + apiManager, + navigationManager + }) + }; + + console.log('DataTools Pro Debug Mode Enabled'); + console.log('Use DataToolsDebug object for debugging'); + } +} +``` + +## 7. 测试策略 + +### 7.1 单元测试框架 +```javascript +/** + * 轻量级测试框架 + */ +class TestFramework { + constructor() { + this.tests = []; + this.results = { + passed: 0, + failed: 0, + errors: [] + }; + } + + describe(description, testFn) { + console.group(`📋 ${description}`); + testFn(); + console.groupEnd(); + } + + it(description, testFn) { + try { + testFn(); + this.results.passed++; + console.log(`✅ ${description}`); + } catch (error) { + this.results.failed++; + this.results.errors.push({ description, error }); + console.error(`❌ ${description}:`, error); + } + } + + expect(actual) { + return { + toBe: (expected) => { + if (actual !== expected) { + throw new Error(`Expected ${expected}, got ${actual}`); + } + }, + toEqual: (expected) => { + if (JSON.stringify(actual) !== JSON.stringify(expected)) { + throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); + } + }, + toBeTruthy: () => { + if (!actual) { + throw new Error(`Expected truthy value, got ${actual}`); + } + }, + toBeFalsy: () => { + if (actual) { + throw new Error(`Expected falsy value, got ${actual}`); + } + } + }; + } + + async runTests() { + console.log('🚀 Running DataTools Pro Tests...'); + + // 运行所有测试 + this.testUtils(); + this.testAPI(); + this.testUI(); + + // 输出结果 + console.log(`\n📊 Test Results:`); + console.log(`✅ Passed: ${this.results.passed}`); + console.log(`❌ Failed: ${this.results.failed}`); + + if (this.results.errors.length > 0) { + console.log('\n❌ Errors:'); + this.results.errors.forEach(({ description, error }) => { + console.error(` - ${description}: ${error.message}`); + }); + } + } + + testUtils() { + this.describe('Utils Module', () => { + this.it('should escape HTML correctly', () => { + const result = utils.escapeHtml(''); + this.expect(result).toBe('<script>alert("xss")</script>'); + }); + + this.it('should format date correctly', () => { + const date = new Date('2024-08-05T10:30:00Z'); + const result = utils.formatDate(date, 'YYYY-MM-DD'); + this.expect(result).toBe('2024-08-05'); + }); + + this.it('should debounce function calls', (done) => { + let callCount = 0; + const debouncedFn = utils.debounce(() => callCount++, 100); + + debouncedFn(); + debouncedFn(); + debouncedFn(); + + setTimeout(() => { + this.expect(callCount).toBe(1); + done(); + }, 150); + }); + }); + } +} +``` + +## 8. 构建和部署 + +### 8.1 开发环境 +```javascript +/** + * 开发环境配置 + */ +const devConfig = { + // 热重载 + enableHotReload: true, + + // 调试模式 + debugMode: true, + + // 本地服务器配置 + devServer: { + port: 3000, + host: 'localhost', + open: true + }, + + // 代理配置 + proxy: { + '/api': { + target: 'http://localhost:5000', + changeOrigin: true + } + } +}; +``` + +### 8.2 生产环境优化 +```javascript +/** + * 生产环境优化 + */ +const prodOptimizations = { + // 代码压缩 + minification: true, + + // 资源合并 + bundling: false, // 保持模块化 + + // 缓存策略 + caching: { + staticAssets: '1y', + api: '5m' + }, + + // CDN配置 + cdn: { + bootstrap: 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', + fontawesome: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css' + } +}; +``` + +--- + +**版本**: v2.0 +**更新日期**: 2024-08-05 +**维护者**: DataTools Pro Team \ No newline at end of file diff --git a/docs/user-manual.md b/docs/user-manual.md new file mode 100644 index 0000000..5d5e61d --- /dev/null +++ b/docs/user-manual.md @@ -0,0 +1,708 @@ +# DataTools Pro 用户使用手册 + +## 1. 系统介绍 + +### 1.1 产品概述 +DataTools Pro 是一个企业级数据处理与比对工具平台,专为数据工程师和运维人员设计,提供高效、精准、可视化的数据分析解决方案。 + +### 1.2 主要功能 +- **Cassandra数据比对**: 支持生产环境与测试环境的数据差异分析 +- **Redis集群比对**: 支持多种数据类型的Redis集群数据比对 +- **分表查询**: 支持TWCS时间分表的智能查询 +- **多主键查询**: 支持复合主键的批量查询 +- **配置管理**: 可视化的数据库配置管理 +- **查询历史**: 完整的查询历史记录和结果复用 +- **实时日志**: 详细的查询执行日志和性能监控 + +### 1.3 系统要求 +- **浏览器**: Chrome 61+, Firefox 60+, Safari 10.1+, Edge 16+ +- **网络**: 能够访问目标Cassandra和Redis集群 +- **权限**: 具备目标数据库的读取权限 + +## 2. 快速入门 + +### 2.1 访问系统 +1. 打开浏览器,访问系统地址(如:http://your-domain.com) +2. 系统首页展示了可用的工具模块和功能介绍 +3. 选择需要使用的工具进入相应的功能页面 + +### 2.2 界面概览 +``` +DataTools Pro 界面布局: +┌─────────────────────────────────────────────────────────────┐ +│ 导航栏 │ +│ DataTools Pro | 首页 | Cassandra比对 | Redis比对 │ +├─────────────────────────────────────────────────────────────┤ +│ 面包屑导航 │ +│ 首页 > Cassandra数据比对工具 │ +├─────────────────────────────────────────────────────────────┤ +│ 配置管理面板 │ 查询操作面板 │ +│ ┌─────────────────┐ │ ┌─────────────────────────────┐ │ +│ │ • 配置组管理 │ │ │ • 查询模式选择 │ │ +│ │ • 数据库配置 │ │ │ • Key值输入 │ │ +│ │ • 查询历史 │ │ │ • 比对字段配置 │ │ +│ │ • 查询日志 │ │ │ • 执行按钮 │ │ +│ └─────────────────┘ │ └─────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ 结果展示区域 │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ • 统计信息卡片 │ │ +│ │ • 差异详情 | 相同结果 | 原始数据 | 比较总结 │ │ +│ │ • 分页导航和搜索过滤 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 2.3 键盘快捷键 +- `Ctrl/Cmd + H`: 返回首页 +- `Ctrl/Cmd + 1`: 切换到Cassandra工具 +- `Ctrl/Cmd + 2`: 切换到Redis工具 +- `?`: 显示/隐藏快捷键提示 + +## 3. Cassandra数据比对工具 + +### 3.1 功能概述 +Cassandra数据比对工具支持对生产环境和测试环境的Cassandra数据库进行精确的数据比对分析,支持单表查询、分表查询和多主键查询三种模式。 + +### 3.2 配置管理 + +#### 3.2.1 配置组管理 +配置组功能允许您保存和管理常用的数据库配置,避免重复输入。 + +**创建配置组**: +1. 点击"保存配置组"按钮 +2. 输入配置组名称和描述 +3. 系统将保存当前的所有配置信息 +4. 配置组创建成功后可在下拉列表中选择 + +**加载配置组**: +1. 在配置组下拉列表中选择已保存的配置组 +2. 点击"加载"按钮 +3. 系统将自动填充所有相关配置 + +**管理配置组**: +1. 点击"管理配置组"按钮 +2. 查看所有已保存的配置组 +3. 可以删除不需要的配置组 + +#### 3.2.2 数据库配置 + +**生产环境配置**: +``` +配置项说明: +• 集群名称: Cassandra集群的标识名称 +• 数据中心: 数据中心名称 (如: datacenter1) +• 主机地址: Cassandra节点地址列表 (支持多个,逗号分隔) +• 端口: 连接端口 (默认: 9042) +• 用户名: 数据库用户名 (可选) +• 密码: 数据库密码 (可选) +• 键空间: Keyspace名称 +• 表名: 要查询的表名 +``` + +**测试环境配置**: +配置项与生产环境相同,用于配置测试环境的Cassandra连接信息。 + +**一键导入功能**: +1. 点击"一键导入"按钮 +2. 可以从生产环境配置快速复制到测试环境 +3. 根据需要修改测试环境的特定配置 + +### 3.3 查询模式 + +#### 3.3.1 单表查询模式 +默认的查询模式,适用于普通的Cassandra表查询。 + +**操作步骤**: +1. 确保分表模式开关处于关闭状态 +2. 配置生产和测试环境的数据库连接 +3. 设置查询参数 +4. 输入要查询的Key值 +5. 点击"执行查询比对" + +#### 3.3.2 分表查询模式 +适用于使用TWCS (Time Window Compaction Strategy) 分表策略的Cassandra表。 + +**启用分表模式**: +1. 打开"分表模式"开关 +2. 分别配置生产和测试环境的分表参数 + +**分表参数配置**: +``` +配置说明: +• 生产环境分表: 是否对生产环境启用分表查询 +• 测试环境分表: 是否对测试环境启用分表查询 +• 时间间隔(秒): 分表的时间间隔,默认604800秒(7天) +• 分表数量: 总分表数量,默认14张表 +``` + +**分表算法说明**: +- 系统从Key中提取时间戳(删除所有非数字字符) +- 计算公式: `shard_index = timestamp // interval_seconds % table_count` +- 示例: Key为"wmid_1609459200",提取时间戳1609459200,计算得到分表索引 + +#### 3.3.3 多主键查询模式 +支持复合主键的批量查询,适用于具有多个主键字段的表。 + +**配置多主键**: +1. 在"主键字段"中输入多个字段名,用逗号分隔 + - 示例: `user_id,timestamp` 或 `docid,id` +2. 在查询Key值中输入复合主键值,每行一组,字段值用逗号分隔 + - 示例: + ``` + 1001,1609459200 + 1002,1609545600 + ``` + +### 3.4 查询配置 + +#### 3.4.1 基础查询参数 +``` +参数说明: +• 主键字段: 表的主键字段名,支持单个或多个字段 +• 比对字段: 指定要比对的字段列表 (空表示全部字段) +• 排除字段: 指定要排除的字段列表 +• 查询Key值: 要查询的主键值列表,每行一个 +``` + +#### 3.4.2 字段配置技巧 +- **全字段比对**: 比对字段留空,系统将比对所有字段 +- **指定字段比对**: 在比对字段中列出需要比对的字段名 +- **排除字段**: 在排除字段中列出不需要比对的字段(如时间戳字段) +- **字段名格式**: 使用英文逗号分隔多个字段名 + +### 3.5 执行查询 + +#### 3.5.1 查询执行流程 +1. **配置验证**: 系统验证数据库连接配置的完整性 +2. **连接测试**: 尝试连接到生产和测试环境 +3. **并行查询**: 同时执行生产和测试环境的数据查询 +4. **数据比对**: 对查询结果进行字段级比对分析 +5. **结果展示**: 生成详细的比对报告和统计信息 + +#### 3.5.2 执行过程监控 +- 查询执行时显示进度指示器 +- 实时显示执行状态和进度信息 +- 可以通过查询日志查看详细的执行过程 + +### 3.6 结果分析 + +#### 3.6.1 统计信息卡片 +查询完成后,系统会显示以下统计信息: +``` +统计指标: +• 查询总数: 本次查询的Key总数量 +• 生产记录数: 生产环境查询到的记录数 +• 测试记录数: 测试环境查询到的记录数 +• 差异数量: 发现的数据差异数量 +• 相同数量: 数据完全相同的记录数量 +• 匹配率: 数据匹配的百分比 +``` + +#### 3.6.2 结果选项卡 + +**差异详情选项卡**: +- 显示所有发现的数据差异 +- 每条差异记录包含: + - Key信息 (支持复合主键显示) + - 字段名 + - 生产环境值 + - 测试环境值 + - 差异类型说明 +- 支持分页浏览和搜索过滤 + +**相同结果选项卡**: +- 显示数据完全相同的记录 +- 包含生产和测试环境的完整字段值 +- 可用于验证数据同步的正确性 + +**原始数据选项卡**: +- 显示生产和测试环境的原始查询数据 +- 支持格式化和原始两种显示模式 +- 提供数据导出功能 + +**比较总结选项卡**: +- 提供详细的数据质量分析报告 +- 包含匹配率统计和字段级差异分析 +- 给出数据改进建议 + +#### 3.6.3 分表查询信息 +当使用分表查询模式时,结果页面会显示额外的分表信息: +``` +分表信息包含: +• 分表映射关系: 每个Key对应的分表名称 +• 分表统计: 使用的分表列表和分布情况 +• 时间戳提取: 时间戳提取成功率 +• 失败Key列表: 时间戳提取失败的Key +``` + +### 3.7 查询历史管理 + +#### 3.7.1 保存查询历史 +1. 查询执行完成后,点击"保存历史"按钮 +2. 输入历史记录名称和描述 +3. 系统将保存完整的查询配置和结果数据 + +#### 3.7.2 查看查询历史 +1. 点击"查询历史"按钮 +2. 浏览所有已保存的历史记录 +3. 可以查看历史记录的详细信息: + - 查询配置 + - 执行时间 + - 结果统计 + - 完整的差异和相同数据 + +#### 3.7.3 历史记录操作 +- **加载历史**: 从历史记录恢复查询配置 +- **查看结果**: 查看历史查询的完整结果 +- **删除记录**: 删除不需要的历史记录 + +### 3.8 查询日志 + +#### 3.8.1 日志功能 +查询日志记录了查询执行过程中的所有关键信息: +- SQL语句执行记录 +- 数据库连接状态 +- 查询耗时统计 +- 错误和警告信息 + +#### 3.8.2 日志查看 +1. 点击"查询日志"按钮 +2. 可以按日志级别过滤: + - INFO: 一般信息 + - WARNING: 警告信息 + - ERROR: 错误信息 +3. 支持按时间范围查看日志 + +#### 3.8.3 日志管理 +- **实时刷新**: 实时查看最新的日志信息 +- **清空日志**: 清除所有历史日志记录 +- **日志导出**: 导出日志用于分析和故障排查 + +## 4. Redis集群比对工具 + +### 4.1 功能概述 +Redis集群比对工具专门用于对比两个Redis集群之间的数据差异,支持所有Redis数据类型,提供随机采样和指定Key两种查询模式。 + +### 4.2 Redis集群配置 + +#### 4.2.1 集群配置参数 +``` +配置项说明: +• 集群名称: Redis集群的标识名称 +• Redis节点: 集群节点列表,支持添加多个节点 + - 主机地址: Redis节点IP地址 + - 端口: Redis端口 (默认: 6379) +• 密码: Redis认证密码 (可选) +• 连接超时: Socket连接超时时间 (默认: 3秒) +• 建立超时: Socket建立连接超时时间 (默认: 3秒) +• 最大连接数: 每个节点的最大连接数 (默认: 16) +``` + +#### 4.2.2 集群节点管理 +**添加节点**: +1. 在节点配置区域点击"添加节点"按钮 +2. 输入节点的IP地址和端口 +3. 可以添加多个节点以支持集群模式 + +**删除节点**: +1. 点击节点右侧的删除按钮 +2. 确认删除操作 + +**测试连接**: +1. 配置完成后点击"测试连接"按钮 +2. 系统将测试与集群的连接状态 +3. 显示连接结果和节点状态 + +### 4.3 查询模式 + +#### 4.3.1 随机采样模式 +适用于对Redis集群进行数据质量抽查和整体数据一致性验证。 + +**配置参数**: +``` +参数说明: +• 采样数量: 随机采样的Key数量 (1-10000) +• Key模式: Key的匹配模式 (默认: *) +• 源集群: 选择从哪个集群获取Key列表 +``` + +**操作步骤**: +1. 选择"随机采样模式" +2. 设置采样数量 (建议100-1000) +3. 设置Key模式 (如: user:*, session:*, 或 *) +4. 选择源集群 (通常选择数据较完整的集群) +5. 点击"开始Redis数据比较" + +#### 4.3.2 指定Key模式 +适用于对特定Key进行精确的数据比对。 + +**操作步骤**: +1. 选择"指定Key模式" +2. 在Key列表文本框中输入要查询的Key +3. 每行一个Key,支持大批量Key查询 +4. 建议单次不超过1000个Key +5. 点击"开始Redis数据比较" + +**Key输入示例**: +``` +user:1001 +user:1002 +session:abc123 +cache:product:12345 +``` + +### 4.4 数据类型支持 + +#### 4.4.1 支持的Redis数据类型 +Redis集群比对工具支持以下数据类型的比对: +- **String**: 字符串类型,包括JSON字符串 +- **Hash**: 哈希表,支持字段级比对 +- **List**: 列表,支持顺序和内容比对 +- **Set**: 集合,支持成员比对(顺序无关) +- **Sorted Set**: 有序集合,支持成员和分数比对 + +#### 4.4.2 数据比对逻辑 +``` +比对规则: +• String类型: 直接比较字符串值 +• Hash类型: 逐字段比较,显示字段级差异 +• List类型: 比较列表长度和元素内容 +• Set类型: 比较集合成员,忽略顺序 +• ZSet类型: 比较成员和分数的对应关系 +``` + +### 4.5 结果分析 + +#### 4.5.1 统计信息 +Redis比对完成后显示以下统计信息: +``` +统计指标: +• 查询总数: 本次查询的Key总数 +• 集群1找到: 集群1中存在的Key数量 +• 集群2找到: 集群2中存在的Key数量 +• 差异数量: 值不同的Key数量 +• 相同数量: 值完全相同的Key数量 +• 缺失数量: 在某个集群中不存在的Key数量 +``` + +#### 4.5.2 结果选项卡 + +**差异详情选项卡**: +- 显示值不同的Key详细信息 +- 包含Key名称、数据类型、两个集群的值 +- 对于复杂数据类型,显示具体的差异字段 + +**相同结果选项卡**: +- 显示完全相同的Key和值 +- 用于验证数据同步的正确性 + +**缺失数据选项卡**: +- 显示在某个集群中不存在的Key +- 分为"集群1缺失"和"集群2缺失"两个部分 + +**原生数据选项卡**: +- 显示两个集群的原始查询数据 +- 支持格式化和原始数据两种视图 +- 提供数据导出功能 + +**比较总结选项卡**: +- 生成性能报告和数据质量分析 +- 包含集群连接状态和响应时间统计 +- 提供数据一致性改进建议 + +### 4.6 性能监控 + +#### 4.6.1 集群状态监控 +系统实时监控Redis集群的运行状态: +``` +监控指标: +• 连接状态: 显示每个节点的连接状态 +• 平均响应时间: 集群的平均响应时间 +• 节点状态: 各个节点的运行状态 +• 数据传输量: 本次查询的数据传输量 +``` + +#### 4.6.2 性能统计 +查询完成后显示性能统计信息: +- 总执行时间 +- 每秒处理Key数量 +- 数据传输量统计 +- 集群负载分析 + +## 5. 高级功能 + +### 5.1 配置导入导出 + +#### 5.1.1 配置导出 +1. 在任何工具页面点击"导出配置"按钮 +2. 系统将生成包含所有配置信息的JSON文件 +3. 文件包含数据库配置、查询参数、分表配置等 + +#### 5.1.2 配置导入 +1. 点击"一键导入"或相关导入按钮 +2. 选择导入方式: + - **文本粘贴**: 直接粘贴YAML或JSON配置 + - **文件上传**: 上传配置文件或拖拽文件 +3. 预览配置内容 +4. 确认导入 + +#### 5.1.3 支持的配置格式 +**YAML格式示例**: +```yaml +clusterName: "production-cluster" +clusterAddress: "10.0.1.100:9042,10.0.1.101:9042" +keyspace: "production_ks" +table: "user_data" +username: "cassandra_user" +password: "password" +``` + +**JSON格式示例**: +```json +{ + "cluster_name": "production-cluster", + "hosts": ["10.0.1.100", "10.0.1.101"], + "port": 9042, + "keyspace": "production_ks", + "table": "user_data" +} +``` + +### 5.2 数据导出功能 + +#### 5.2.1 结果导出格式 +支持多种数据导出格式: +- **Excel格式 (.xlsx)**: 适合数据分析和报告 +- **CSV格式 (.csv)**: 通用的数据交换格式 +- **JSON格式 (.json)**: 适合程序处理 +- **HTML报告**: 可打印的详细报告 + +#### 5.2.2 导出内容选择 +可以选择导出以下内容: +- 差异数据详情 +- 相同数据记录 +- 原始查询数据 +- 统计分析报告 +- 完整的比对结果 + +#### 5.2.3 导出操作步骤 +1. 在结果页面点击"导出结果"按钮 +2. 选择导出格式和内容范围 +3. 系统生成并下载导出文件 +4. 文件名包含时间戳,便于管理 + +### 5.3 批量操作 + +#### 5.3.1 批量Key导入 +支持大批量Key的导入和处理: +- **文件导入**: 支持.txt, .csv, .xlsx格式 +- **粘贴导入**: 直接粘贴Key列表 +- **去重处理**: 自动去除重复的Key +- **格式验证**: 验证Key格式的正确性 + +#### 5.3.2 批量查询优化 +系统对批量查询进行了优化: +- **分批处理**: 大量Key自动分批查询 +- **并行执行**: 多线程并行处理提高效率 +- **进度显示**: 实时显示查询进度 +- **错误处理**: 单个Key失败不影响整体查询 + +### 5.4 数据过滤和搜索 + +#### 5.4.1 实时搜索 +在结果页面提供实时搜索功能: +- **Key搜索**: 根据Key名称搜索 +- **字段搜索**: 根据字段名搜索 +- **值搜索**: 根据字段值搜索 +- **模糊匹配**: 支持部分匹配和通配符 + +#### 5.4.2 高级过滤 +提供多种过滤条件: +- **差异类型过滤**: 按差异类型筛选 +- **字段名过滤**: 按特定字段筛选 +- **数据类型过滤**: 按Redis数据类型筛选 +- **时间范围过滤**: 按查询时间筛选 + +## 6. 故障排查 + +### 6.1 常见问题 + +#### 6.1.1 连接问题 +**问题**: 无法连接到Cassandra/Redis集群 +**解决方案**: +1. 检查网络连通性: `ping ` +2. 检查端口是否开放: `telnet ` +3. 验证用户名和密码 +4. 确认防火墙设置 +5. 检查集群状态 + +**问题**: 连接超时 +**解决方案**: +1. 增加连接超时时间 +2. 检查网络延迟 +3. 确认集群负载情况 +4. 检查DNS解析 + +#### 6.1.2 查询问题 +**问题**: 查询结果为空 +**解决方案**: +1. 验证Key是否存在于数据库中 +2. 检查表名和键空间是否正确 +3. 确认主键字段名拼写 +4. 检查数据权限 + +**问题**: 分表查询失败 +**解决方案**: +1. 验证分表参数配置 +2. 检查Key中是否包含时间戳 +3. 确认分表命名规则 +4. 验证分表数量设置 + +#### 6.1.3 性能问题 +**问题**: 查询速度慢 +**解决方案**: +1. 减少单次查询的Key数量 +2. 优化网络连接 +3. 检查数据库负载 +4. 使用更高效的查询模式 + +**问题**: 内存使用过高 +**解决方案**: +1. 分批处理大量数据 +2. 减少比对字段数量 +3. 清理历史查询记录 +4. 重启浏览器释放内存 + +### 6.2 错误信息解读 + +#### 6.2.1 连接错误 +``` +错误类型及解决方案: + +• "Connection refused" + - 检查目标主机是否可达 + - 确认端口是否正确 + - 检查防火墙设置 + +• "Authentication failed" + - 验证用户名和密码 + - 检查用户权限 + - 确认认证方式 + +• "Timeout occurred" + - 增加超时时间设置 + - 检查网络延迟 + - 确认集群健康状态 +``` + +#### 6.2.2 查询错误 +``` +错误类型及解决方案: + +• "Keyspace not found" + - 检查键空间名称拼写 + - 确认键空间是否存在 + - 验证用户访问权限 + +• "Table not found" + - 检查表名拼写 + - 确认表是否存在于指定键空间 + - 验证表访问权限 + +• "Invalid primary key" + - 检查主键字段名 + - 确认主键值格式 + - 验证复合主键配置 +``` + +### 6.3 性能优化建议 + +#### 6.3.1 查询优化 +1. **合理设置批次大小**: 建议单次查询1000个Key以内 +2. **选择合适的比对字段**: 只比对必要的字段 +3. **使用分表查询**: 对于大表使用分表查询提高效率 +4. **定期清理历史数据**: 删除不需要的历史记录和日志 + +#### 6.3.2 网络优化 +1. **就近部署**: 在靠近数据库的网络环境中使用工具 +2. **稳定网络**: 确保网络连接稳定,避免频繁重连 +3. **并发控制**: 合理设置并发连接数 +4. **压缩传输**: 启用数据传输压缩 + +#### 6.3.3 数据库优化 +1. **读取权限**: 确保使用只读权限账户 +2. **索引优化**: 确保查询字段有适当的索引 +3. **分区策略**: 合理设计Cassandra分区键 +4. **连接池**: 使用连接池避免频繁建立连接 + +## 7. 最佳实践 + +### 7.1 数据比对策略 + +#### 7.1.1 渐进式比对 +1. **小范围测试**: 先用少量Key测试配置和连接 +2. **分批比对**: 将大量数据分批进行比对 +3. **重点字段**: 优先比对关键业务字段 +4. **定期验证**: 建立定期数据比对的流程 + +#### 7.1.2 差异分析方法 +1. **分类分析**: 按字段类型分类分析差异 +2. **趋势观察**: 观察差异随时间的变化趋势 +3. **根因分析**: 深入分析差异产生的根本原因 +4. **修复验证**: 修复后重新验证数据一致性 + +### 7.2 配置管理最佳实践 + +#### 7.2.1 配置组织 +1. **命名规范**: 使用有意义的配置组名称 +2. **环境区分**: 清楚标识生产、测试、开发环境 +3. **版本管理**: 记录配置变更历史 +4. **权限控制**: 限制敏感配置的访问权限 + +#### 7.2.2 安全考虑 +1. **密码管理**: 避免在配置中硬编码密码 +2. **权限最小化**: 使用最小必要权限的数据库账户 +3. **网络隔离**: 在安全的网络环境中使用工具 +4. **审计日志**: 记录重要操作的审计日志 + +### 7.3 团队协作 + +#### 7.3.1 工作流程标准化 +1. **操作文档**: 制定标准的操作流程文档 +2. **结果记录**: 建立查询结果的记录和归档制度 +3. **问题跟踪**: 建立差异问题的跟踪和解决流程 +4. **知识分享**: 定期分享使用经验和最佳实践 + +#### 7.3.2 质量控制 +1. **双人验证**: 重要数据比对结果需要双人确认 +2. **结果审核**: 建立结果审核和批准流程 +3. **变更控制**: 对配置变更进行严格控制 +4. **定期回顾**: 定期回顾和改进数据比对流程 + +## 8. 技术支持 + +### 8.1 获取帮助 +- **在线文档**: 访问系统内置的帮助文档 +- **技术支持**: 联系技术支持团队 +- **社区论坛**: 参与用户社区讨论 +- **培训资源**: 获取相关的培训材料 + +### 8.2 反馈和建议 +我们欢迎您的反馈和建议: +- **功能建议**: 提出新功能需求 +- **问题报告**: 报告使用中遇到的问题 +- **改进意见**: 提供用户体验改进建议 +- **文档更新**: 建议文档内容的改进 + +### 8.3 版本更新 +- **更新通知**: 关注系统版本更新通知 +- **新功能**: 了解新版本的功能特性 +- **兼容性**: 注意版本兼容性说明 +- **升级指南**: 遵循版本升级指南 + +--- + +**DataTools Pro v2.0 用户使用手册** +**更新日期**: 2024-08-05 +**版权所有**: DataTools Pro Team \ No newline at end of file