优化项目整合内容
This commit is contained in:
212
README.md
Normal file
212
README.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# BigDataTool - 大数据查询比对工具
|
||||
|
||||
[](https://python.org)
|
||||
[](https://flask.palletsprojects.com/)
|
||||
[](LICENSE)
|
||||
|
||||
BigDataTool是一个功能强大的数据库查询比对工具,专门用于Cassandra数据库和Redis集群的数据一致性验证。支持单表查询、TWCS分表查询、多主键查询等多种复杂场景。
|
||||
|
||||
## 🚀 核心功能
|
||||
|
||||
### Cassandra数据比对
|
||||
- **单表查询**:标准的生产环境与测试环境数据比对
|
||||
- **分表查询**:基于TWCS策略的时间分表查询支持
|
||||
- **多主键查询**:支持复合主键的精确匹配和比对
|
||||
- **智能数据比较**:JSON、数组等复杂数据类型的深度比较
|
||||
|
||||
### Redis数据比对
|
||||
- **全类型支持**:String、Hash、List、Set、ZSet、Stream等所有Redis数据类型
|
||||
- **集群支持**:单节点和集群模式的自动检测和连接
|
||||
- **随机采样**:支持随机Key采样和指定Key比对两种模式
|
||||
- **性能监控**:详细的连接时间和查询性能统计
|
||||
|
||||
### 配置管理
|
||||
- **配置组管理**:数据库连接配置的保存、加载和复用
|
||||
- **查询历史**:查询记录的持久化存储和一键回放
|
||||
- **实时日志**:详细的操作日志和性能监控
|
||||
- **YAML导入**:支持YAML格式配置的一键导入
|
||||
|
||||
## 📋 系统要求
|
||||
|
||||
- Python 3.8+
|
||||
- Flask 2.3.3
|
||||
- Cassandra Driver 3.29.1
|
||||
- Redis 5.0.1
|
||||
|
||||
## 🛠️ 安装部署
|
||||
|
||||
### 1. 克隆项目
|
||||
```bash
|
||||
git clone https://github.com/your-org/BigDataTool.git
|
||||
cd BigDataTool
|
||||
```
|
||||
|
||||
### 2. 安装依赖
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 3. 启动应用
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
应用将在 `http://localhost:5000` 启动
|
||||
|
||||
## 🎯 快速开始
|
||||
|
||||
### Cassandra数据比对
|
||||
|
||||
1. 访问 `http://localhost:5000/db-compare`
|
||||
2. 配置生产环境和测试环境的Cassandra连接信息
|
||||
3. 设置主键字段和查询参数
|
||||
4. 输入要比对的Key值列表
|
||||
5. 点击"开始查询"执行比对
|
||||
|
||||
#### 单主键查询示例
|
||||
```
|
||||
主键字段: id
|
||||
查询Key值:
|
||||
1001
|
||||
1002
|
||||
1003
|
||||
```
|
||||
|
||||
#### 复合主键查询示例
|
||||
```
|
||||
主键字段: docid,id
|
||||
查询Key值:
|
||||
8825C293B3609175B2224236E984FEDB,8825C293B3609175B2224236E984FED
|
||||
9925C293B3609175B2224236E984FEDB,9925C293B3609175B2224236E984FED
|
||||
```
|
||||
|
||||
### Redis数据比对
|
||||
|
||||
1. 访问 `http://localhost:5000/redis-compare`
|
||||
2. 配置两个Redis集群的连接信息
|
||||
3. 选择查询模式(随机采样或指定Key)
|
||||
4. 设置查询参数
|
||||
5. 执行比对分析
|
||||
|
||||
## 📊 功能特性
|
||||
|
||||
### 数据比对引擎
|
||||
- **智能JSON比较**:自动处理JSON格式差异和嵌套结构
|
||||
- **数组顺序无关比较**:忽略数组元素顺序的深度比较
|
||||
- **字段级差异分析**:详细的字段差异统计和热点分析
|
||||
- **数据质量评估**:自动生成数据一致性报告和改进建议
|
||||
|
||||
### 分表查询支持
|
||||
- **TWCS策略**:基于Time Window Compaction Strategy的分表计算
|
||||
- **时间戳提取**:智能从Key中提取时间戳信息
|
||||
- **混合查询**:支持生产分表+测试单表等组合场景
|
||||
- **并行查询**:多分表并行查询以提高性能
|
||||
|
||||
### 用户界面
|
||||
- **响应式设计**:基于Bootstrap的现代化界面
|
||||
- **实时反馈**:查询进度和结果的实时显示
|
||||
- **分页展示**:大数据集的高效分页显示
|
||||
- **多视图模式**:原始数据、格式化、差异对比等多种视图
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### Cassandra配置
|
||||
```json
|
||||
{
|
||||
"cluster_name": "生产集群",
|
||||
"hosts": ["192.168.1.100", "192.168.1.101"],
|
||||
"port": 9042,
|
||||
"datacenter": "dc1",
|
||||
"username": "cassandra",
|
||||
"password": "password",
|
||||
"keyspace": "my_keyspace",
|
||||
"table": "my_table"
|
||||
}
|
||||
```
|
||||
|
||||
### Redis配置
|
||||
```json
|
||||
{
|
||||
"name": "生产Redis",
|
||||
"nodes": [
|
||||
{"host": "192.168.1.200", "port": 7000},
|
||||
{"host": "192.168.1.201", "port": 7001}
|
||||
],
|
||||
"password": "redis_password",
|
||||
"socket_timeout": 3,
|
||||
"socket_connect_timeout": 3,
|
||||
"max_connections_per_node": 16
|
||||
}
|
||||
```
|
||||
|
||||
## 📈 性能优化
|
||||
|
||||
- **连接池管理**:优化的数据库连接复用
|
||||
- **批量查询**:支持大批量Key的高效查询
|
||||
- **内存管理**:大结果集的内存友好处理
|
||||
- **并行处理**:多表并行查询和数据比对
|
||||
- **缓存机制**:查询结果和配置的智能缓存
|
||||
|
||||
## 🔍 故障排查
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **Cassandra连接失败**
|
||||
- 检查网络连通性:`telnet <host> <port>`
|
||||
- 验证认证信息:用户名、密码、keyspace
|
||||
- 确认防火墙设置
|
||||
|
||||
2. **Redis连接失败**
|
||||
- 检查Redis服务状态:`redis-cli ping`
|
||||
- 验证集群配置:节点地址和端口
|
||||
- 确认密码设置
|
||||
|
||||
3. **查询超时**
|
||||
- 调整连接超时参数
|
||||
- 检查数据库服务器负载
|
||||
- 优化查询条件和索引
|
||||
|
||||
## 📝 API文档
|
||||
|
||||
### 主要API端点
|
||||
|
||||
- `POST /api/query` - 单表查询比对
|
||||
- `POST /api/sharding-query` - 分表查询比对
|
||||
- `POST /api/redis/compare` - Redis数据比对
|
||||
- `GET /api/config-groups` - 获取配置组列表
|
||||
- `POST /api/config-groups` - 创建配置组
|
||||
- `GET /api/query-history` - 获取查询历史
|
||||
- `GET /api/query-logs` - 获取查询日志
|
||||
|
||||
详细API文档请参考 [API.md](docs/API.md)
|
||||
|
||||
## 📚 文档目录
|
||||
|
||||
- [API文档](docs/API.md) - 完整的API接口说明
|
||||
- [使用指南](docs/USER_GUIDE.md) - 详细的功能使用说明
|
||||
- [架构设计](docs/ARCHITECTURE.md) - 系统架构和设计原理
|
||||
- [部署指南](docs/DEPLOYMENT.md) - 生产环境部署说明
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
1. Fork 项目
|
||||
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 开启 Pull Request
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
|
||||
|
||||
## 👥 作者
|
||||
|
||||
BigDataTool项目组
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
感谢所有为这个项目做出贡献的开发者和用户。
|
||||
|
||||
---
|
||||
|
||||
**注意**:使用前请确保已正确配置数据库连接信息,并在生产环境中谨慎使用。
|
28
app.py
28
app.py
@@ -1,6 +1,30 @@
|
||||
"""
|
||||
BigDataTool - 主应用文件
|
||||
模块化重构后的主应用,使用分离的模块组织代码
|
||||
BigDataTool - 大数据查询比对工具主应用
|
||||
|
||||
这是一个基于Flask的数据库查询比对工具,主要用于:
|
||||
1. Cassandra数据库的生产环境与测试环境数据比对
|
||||
2. Redis集群数据的一致性验证
|
||||
3. 支持单表查询、TWCS分表查询、多主键查询等多种场景
|
||||
4. 提供完整的配置管理、查询历史和日志记录功能
|
||||
|
||||
主要特性:
|
||||
- 模块化架构:清晰的代码组织和职责分离
|
||||
- 多数据源支持:Cassandra + Redis
|
||||
- 智能数据比对:支持JSON、数组等复杂数据类型
|
||||
- 分表查询:基于TWCS策略的时间分表支持
|
||||
- 多主键查询:支持复合主键的精确匹配
|
||||
- 配置管理:数据库配置的保存、加载和复用
|
||||
- 查询历史:查询记录的持久化存储和回放
|
||||
- 实时日志:详细的操作日志和性能监控
|
||||
|
||||
技术栈:
|
||||
- 后端:Flask + SQLite + Cassandra Driver + Redis
|
||||
- 前端:原生JavaScript + Bootstrap
|
||||
- 数据库:SQLite(配置存储)+ Cassandra(数据查询)+ Redis(数据比对)
|
||||
|
||||
作者:BigDataTool项目组
|
||||
版本:v2.0
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -38,26 +38,15 @@ def setup_routes(app, query_log_collector):
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/test-config-load')
|
||||
def test_config_load():
|
||||
"""配置加载测试页面"""
|
||||
return send_from_directory('.', 'test_config_load.html')
|
||||
|
||||
@app.route('/db-compare')
|
||||
def db_compare():
|
||||
"""Cassandra数据库比对工具页面"""
|
||||
return render_template('db_compare.html')
|
||||
|
||||
@app.route('/redis-compare')
|
||||
def redis_compare():
|
||||
"""Redis数据比对工具页面"""
|
||||
return render_template('redis_compare.html')
|
||||
|
||||
@app.route('/redis-js-test')
|
||||
def redis_js_test():
|
||||
return render_template('redis_js_test.html')
|
||||
|
||||
@app.route('/redis-test')
|
||||
def redis_test():
|
||||
return render_template('redis_test.html')
|
||||
|
||||
# 基础API
|
||||
@app.route('/api/default-config')
|
||||
|
@@ -1,6 +1,38 @@
|
||||
"""
|
||||
Cassandra连接管理模块
|
||||
负责Cassandra数据库的连接和错误诊断
|
||||
====================
|
||||
|
||||
本模块负责Cassandra数据库的连接管理和高级错误诊断功能。
|
||||
|
||||
核心功能:
|
||||
1. 智能连接管理:自动处理集群连接和故障转移
|
||||
2. 错误诊断系统:详细的连接失败分析和解决建议
|
||||
3. 性能监控:连接时间和集群状态的实时监控
|
||||
4. 容错机制:连接超时、重试和优雅降级
|
||||
5. 安全认证:支持用户名密码认证和SSL连接
|
||||
|
||||
连接特性:
|
||||
- 负载均衡:使用DCAwareRoundRobinPolicy避免单点故障
|
||||
- 连接池管理:优化的连接复用和资源管理
|
||||
- 超时控制:可配置的连接和查询超时时间
|
||||
- 协议版本:使用稳定的CQL协议版本4
|
||||
- Schema同步:自动等待集群Schema一致性
|
||||
|
||||
错误诊断系统:
|
||||
- 连接拒绝:检查服务状态和网络连通性
|
||||
- 认证失败:验证用户名密码和权限设置
|
||||
- 超时错误:分析网络延迟和服务器负载
|
||||
- Keyspace错误:验证Keyspace存在性和访问权限
|
||||
- 未知错误:提供通用的故障排查指南
|
||||
|
||||
监控功能:
|
||||
- 集群状态:实时显示可用和故障节点
|
||||
- 连接时间:精确的连接建立时间测量
|
||||
- 元数据获取:集群名称和节点信息展示
|
||||
- 性能指标:连接成功率和响应时间统计
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import time
|
||||
@@ -12,7 +44,57 @@ from cassandra.policies import DCAwareRoundRobinPolicy
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def create_connection(config):
|
||||
"""创建Cassandra连接,带有增强的错误诊断和容错机制"""
|
||||
"""
|
||||
创建Cassandra数据库连接,具备增强的错误诊断和容错机制
|
||||
|
||||
本函数提供企业级的Cassandra连接管理,包括:
|
||||
- 智能连接建立:自动选择最优连接参数
|
||||
- 详细错误诊断:针对不同错误类型提供具体解决方案
|
||||
- 性能监控:记录连接时间和集群状态
|
||||
- 容错处理:连接失败时的优雅降级
|
||||
|
||||
Args:
|
||||
config (dict): Cassandra连接配置,包含以下字段:
|
||||
- hosts (list): Cassandra节点地址列表
|
||||
- port (int): 连接端口,默认9042
|
||||
- username (str): 认证用户名
|
||||
- password (str): 认证密码
|
||||
- keyspace (str): 目标keyspace名称
|
||||
- datacenter (str): 数据中心名称,默认'dc1'
|
||||
|
||||
Returns:
|
||||
tuple: (cluster, session) 连接对象元组
|
||||
- cluster: Cassandra集群对象,用于管理连接
|
||||
- session: 数据库会话对象,用于执行查询
|
||||
- 连接失败时返回 (None, None)
|
||||
|
||||
连接配置优化:
|
||||
- 协议版本:使用稳定的协议版本4
|
||||
- 连接超时:15秒连接超时,避免长时间等待
|
||||
- 负载均衡:DCAwareRoundRobinPolicy避免跨DC查询
|
||||
- Schema同步:30秒Schema一致性等待时间
|
||||
- 查询超时:30秒默认查询超时时间
|
||||
|
||||
错误诊断:
|
||||
- 连接拒绝:提供服务状态检查建议
|
||||
- 认证失败:提供用户权限验证指南
|
||||
- 超时错误:提供网络和性能优化建议
|
||||
- Keyspace错误:提供Keyspace创建和权限指南
|
||||
|
||||
使用示例:
|
||||
config = {
|
||||
'hosts': ['192.168.1.100', '192.168.1.101'],
|
||||
'port': 9042,
|
||||
'username': 'cassandra',
|
||||
'password': 'password',
|
||||
'keyspace': 'my_keyspace',
|
||||
'datacenter': 'dc1'
|
||||
}
|
||||
cluster, session = create_connection(config)
|
||||
if session:
|
||||
result = session.execute("SELECT * FROM my_table LIMIT 10")
|
||||
cluster.shutdown()
|
||||
"""
|
||||
start_time = time.time()
|
||||
|
||||
logger.info(f"=== 开始创建Cassandra连接 ===")
|
||||
|
@@ -1,6 +1,35 @@
|
||||
"""
|
||||
配置管理模块
|
||||
负责配置组和查询历史的CRUD操作
|
||||
============
|
||||
|
||||
本模块负责BigDataTool项目的配置管理和查询历史管理,提供完整的CRUD操作。
|
||||
|
||||
核心功能:
|
||||
1. Cassandra配置组管理:数据库连接配置的保存、加载、删除
|
||||
2. Redis配置组管理:Redis集群配置的完整生命周期管理
|
||||
3. 查询历史管理:查询记录的持久化存储和检索
|
||||
4. 配置解析和验证:YAML格式配置的智能解析
|
||||
|
||||
支持的配置类型:
|
||||
- Cassandra配置:集群地址、认证信息、keyspace等
|
||||
- Redis配置:集群节点、连接参数、查询选项等
|
||||
- 查询配置:主键字段、比较字段、排除字段等
|
||||
- 分表配置:TWCS分表参数、时间间隔、表数量等
|
||||
|
||||
数据存储格式:
|
||||
- 所有配置以JSON格式存储在SQLite数据库中
|
||||
- 支持复杂嵌套结构和数组类型
|
||||
- 自动处理序列化和反序列化
|
||||
- 保持数据类型完整性
|
||||
|
||||
设计特点:
|
||||
- 类型安全:完整的参数验证和类型检查
|
||||
- 事务安全:数据库操作的原子性保证
|
||||
- 错误恢复:数据库异常时的优雅降级
|
||||
- 向后兼容:支持旧版本配置格式的自动升级
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -10,7 +39,8 @@ from .database import ensure_database, get_db_connection
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 默认配置(不显示敏感信息)
|
||||
# Cassandra数据库默认配置模板
|
||||
# 注意:此配置不包含敏感信息,仅作为UI表单的初始模板使用
|
||||
DEFAULT_CONFIG = {
|
||||
'pro_config': {
|
||||
'cluster_name': '',
|
||||
@@ -37,7 +67,8 @@ DEFAULT_CONFIG = {
|
||||
'exclude_fields': []
|
||||
}
|
||||
|
||||
# Redis默认配置
|
||||
# Redis集群默认配置模板
|
||||
# 支持单节点和集群模式,自动检测连接类型
|
||||
REDIS_DEFAULT_CONFIG = {
|
||||
'cluster1_config': {
|
||||
'name': '生产集群',
|
||||
|
@@ -1,6 +1,39 @@
|
||||
"""
|
||||
数据比较模块
|
||||
负责两个数据集之间的比较、JSON处理和差异分析
|
||||
数据比较引擎模块
|
||||
================
|
||||
|
||||
本模块是BigDataTool的智能数据比较引擎,提供高级的数据差异分析功能。
|
||||
|
||||
核心功能:
|
||||
1. 数据集比较:生产环境与测试环境数据的精确比对
|
||||
2. JSON智能比较:支持复杂JSON结构的深度比较
|
||||
3. 数组顺序无关比较:数组元素的智能匹配算法
|
||||
4. 复合主键支持:多字段主键的精确匹配
|
||||
5. 差异分析:详细的字段级差异统计和分析
|
||||
6. 数据质量评估:自动生成数据一致性报告
|
||||
|
||||
比较算法特性:
|
||||
- JSON标准化:自动处理JSON格式差异(空格、顺序等)
|
||||
- 数组智能比较:忽略数组元素顺序的深度比较
|
||||
- 类型容错:自动处理字符串与数字的类型差异
|
||||
- 编码处理:完善的UTF-8和二进制数据处理
|
||||
- 性能优化:大数据集的高效比较算法
|
||||
|
||||
支持的数据类型:
|
||||
- 基础类型:字符串、数字、布尔值、null
|
||||
- JSON对象:嵌套对象的递归比较
|
||||
- JSON数组:元素级别的智能匹配
|
||||
- 二进制数据:字节级别的精确比较
|
||||
- 复合主键:多字段组合的精确匹配
|
||||
|
||||
输出格式:
|
||||
- 差异记录:详细的字段级差异信息
|
||||
- 统计报告:数据一致性的量化分析
|
||||
- 质量评估:数据质量等级和改进建议
|
||||
- 性能指标:比较过程的性能统计
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import json
|
||||
|
@@ -1,6 +1,36 @@
|
||||
"""
|
||||
数据库管理模块
|
||||
负责SQLite数据库的初始化、连接和表结构管理
|
||||
==============
|
||||
|
||||
本模块负责BigDataTool项目的SQLite数据库管理,包括:
|
||||
|
||||
核心功能:
|
||||
1. 数据库初始化和表结构创建
|
||||
2. 数据库连接管理和事务处理
|
||||
3. 表结构版本控制和字段动态添加
|
||||
4. 数据库完整性检查和自动修复
|
||||
|
||||
数据表结构:
|
||||
- config_groups: 配置组管理(Cassandra/Redis连接配置)
|
||||
- query_history: 查询历史记录(单表/分表/Redis查询)
|
||||
- sharding_config_groups: 分表配置组(TWCS分表参数)
|
||||
- query_logs: 查询日志(实时操作日志和性能监控)
|
||||
- redis_config_groups: Redis配置组(集群连接配置)
|
||||
- redis_query_history: Redis查询历史(Redis数据比对记录)
|
||||
|
||||
设计特点:
|
||||
- 自动化表结构管理:支持字段动态添加和版本升级
|
||||
- 向后兼容性:确保旧版本数据的正常访问
|
||||
- 错误恢复:数据库损坏时自动重建表结构
|
||||
- 索引优化:为查询性能优化的索引设计
|
||||
|
||||
使用方式:
|
||||
- ensure_database(): 确保数据库和表结构存在
|
||||
- get_db_connection(): 获取标准的数据库连接
|
||||
- init_database(): 手动初始化数据库(通常自动调用)
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
@@ -14,7 +44,27 @@ logger = logging.getLogger(__name__)
|
||||
DATABASE_PATH = 'config_groups.db'
|
||||
|
||||
def init_database():
|
||||
"""初始化数据库"""
|
||||
"""
|
||||
初始化SQLite数据库和所有必要的表结构
|
||||
|
||||
创建以下数据表:
|
||||
1. config_groups - Cassandra配置组存储
|
||||
2. query_history - 查询历史记录存储
|
||||
3. sharding_config_groups - 分表配置组存储
|
||||
4. query_logs - 查询日志存储
|
||||
5. redis_config_groups - Redis配置组存储
|
||||
6. redis_query_history - Redis查询历史存储
|
||||
|
||||
同时创建必要的索引以优化查询性能。
|
||||
|
||||
Returns:
|
||||
bool: 初始化成功返回True,失败返回False
|
||||
|
||||
注意:
|
||||
- 使用IF NOT EXISTS确保重复调用安全
|
||||
- 自动创建性能优化索引
|
||||
- 支持外键约束和级联删除
|
||||
"""
|
||||
try:
|
||||
conn = sqlite3.connect(DATABASE_PATH)
|
||||
cursor = conn.cursor()
|
||||
@@ -135,7 +185,28 @@ def init_database():
|
||||
return False
|
||||
|
||||
def ensure_database():
|
||||
"""确保数据库和表存在"""
|
||||
"""
|
||||
确保数据库文件和表结构完整存在
|
||||
|
||||
执行以下检查和操作:
|
||||
1. 检查数据库文件是否存在,不存在则创建
|
||||
2. 验证所有必要表是否存在,缺失则重建
|
||||
3. 检查表结构是否完整,缺少字段则动态添加
|
||||
4. 确保索引完整性
|
||||
|
||||
支持的表结构升级:
|
||||
- config_groups表:添加sharding_config字段
|
||||
- query_history表:添加sharding_config、query_type、raw_results等字段
|
||||
- query_logs表:添加history_id外键字段
|
||||
|
||||
Returns:
|
||||
bool: 数据库就绪返回True,初始化失败返回False
|
||||
|
||||
特性:
|
||||
- 向后兼容:支持从旧版本数据库升级
|
||||
- 自动修复:检测到问题时自动重建
|
||||
- 零停机:升级过程不影响现有数据
|
||||
"""
|
||||
if not os.path.exists(DATABASE_PATH):
|
||||
logger.info("数据库文件不存在,正在创建...")
|
||||
return init_database()
|
||||
@@ -222,7 +293,26 @@ def ensure_database():
|
||||
return init_database()
|
||||
|
||||
def get_db_connection():
|
||||
"""获取数据库连接"""
|
||||
"""
|
||||
获取配置好的SQLite数据库连接
|
||||
|
||||
返回一个配置了Row工厂的数据库连接,支持:
|
||||
- 字典式访问查询结果(row['column_name'])
|
||||
- 自动类型转换
|
||||
- 标准的SQLite连接功能
|
||||
|
||||
Returns:
|
||||
sqlite3.Connection: 配置好的数据库连接对象
|
||||
|
||||
使用示例:
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM config_groups")
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
print(row['name']) # 字典式访问
|
||||
conn.close()
|
||||
"""
|
||||
conn = sqlite3.connect(DATABASE_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
@@ -1,6 +1,35 @@
|
||||
"""
|
||||
数据查询模块
|
||||
负责Cassandra数据的查询执行,支持单表、分表和多主键查询
|
||||
数据查询引擎模块
|
||||
================
|
||||
|
||||
本模块是BigDataTool的核心查询引擎,负责Cassandra数据库的高级查询功能。
|
||||
|
||||
核心功能:
|
||||
1. 单表查询:标准的Cassandra CQL查询执行
|
||||
2. 分表查询:基于TWCS策略的时间分表查询
|
||||
3. 多主键查询:支持复合主键的复杂查询条件
|
||||
4. 混合查询:生产环境分表+测试环境单表的组合查询
|
||||
|
||||
查询类型支持:
|
||||
- 单主键查询:WHERE key IN (val1, val2, val3)
|
||||
- 复合主键查询:WHERE (key1='val1' AND key2='val2') OR (key1='val3' AND key2='val4')
|
||||
- 分表查询:自动计算分表名称并并行查询多张表
|
||||
- 字段过滤:支持指定查询字段和排除字段
|
||||
|
||||
分表查询特性:
|
||||
- 时间戳提取:从Key中智能提取时间戳信息
|
||||
- 分表计算:基于TWCS策略计算目标分表
|
||||
- 并行查询:同时查询多张分表以提高性能
|
||||
- 错误容错:单个分表查询失败不影响整体结果
|
||||
|
||||
性能优化:
|
||||
- 查询时间监控:记录每个查询的执行时间
|
||||
- 批量处理:支持大批量Key的高效查询
|
||||
- 连接复用:优化数据库连接的使用
|
||||
- 内存管理:大结果集的内存友好处理
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import time
|
||||
@@ -10,7 +39,39 @@ from .sharding import ShardingCalculator
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def execute_query(session, table, keys, fields, values, exclude_fields=None):
|
||||
"""执行查询,支持单主键和复合主键"""
|
||||
"""
|
||||
执行Cassandra数据库查询,支持单主键和复合主键查询
|
||||
|
||||
本函数是查询引擎的核心,能够智能处理不同类型的主键查询:
|
||||
- 单主键:生成 WHERE key IN (val1, val2, val3) 查询
|
||||
- 复合主键:生成 WHERE (key1='val1' AND key2='val2') OR ... 查询
|
||||
|
||||
Args:
|
||||
session: Cassandra数据库会话对象
|
||||
table (str): 目标表名
|
||||
keys (list): 主键字段名列表,如 ['id'] 或 ['docid', 'id']
|
||||
fields (list): 要查询的字段列表,空列表表示查询所有字段
|
||||
values (list): 查询值列表,复合主键值用逗号分隔
|
||||
exclude_fields (list, optional): 要排除的字段列表
|
||||
|
||||
Returns:
|
||||
list: 查询结果列表,每个元素是一个Row对象
|
||||
|
||||
查询示例:
|
||||
# 单主键查询
|
||||
execute_query(session, 'users', ['id'], ['name', 'email'], ['1', '2', '3'])
|
||||
# 生成SQL: SELECT name, email FROM users WHERE id IN ('1', '2', '3')
|
||||
|
||||
# 复合主键查询
|
||||
execute_query(session, 'orders', ['user_id', 'order_id'], ['*'], ['1,100', '2,200'])
|
||||
# 生成SQL: SELECT * FROM orders WHERE (user_id='1' AND order_id='100') OR (user_id='2' AND order_id='200')
|
||||
|
||||
错误处理:
|
||||
- 参数验证:检查keys和values是否为空
|
||||
- SQL注入防护:对查询值进行适当转义
|
||||
- 异常捕获:数据库错误时返回空列表
|
||||
- 日志记录:记录查询SQL和执行统计
|
||||
"""
|
||||
try:
|
||||
# 参数验证
|
||||
if not keys or len(keys) == 0:
|
||||
|
@@ -1,6 +1,38 @@
|
||||
"""
|
||||
查询日志管理模块
|
||||
负责查询日志的收集、存储和检索
|
||||
================
|
||||
|
||||
本模块提供BigDataTool的完整查询日志管理功能,支持实时日志收集和历史日志分析。
|
||||
|
||||
核心功能:
|
||||
1. 实时日志收集:自动收集所有查询操作的详细日志
|
||||
2. 批次管理:按查询批次组织日志,便于追踪完整的查询流程
|
||||
3. 双重存储:内存缓存 + SQLite持久化存储
|
||||
4. 历史关联:将日志与查询历史记录关联,支持完整的操作回溯
|
||||
5. 性能监控:记录查询时间、记录数等性能指标
|
||||
|
||||
日志收集特性:
|
||||
- 多级日志:支持INFO、WARNING、ERROR等日志级别
|
||||
- 批次追踪:每个查询批次分配唯一ID,便于日志分组
|
||||
- 时间戳:精确到毫秒的时间戳记录
|
||||
- 查询类型:区分单表、分表、Redis等不同查询类型
|
||||
- 历史关联:支持日志与查询历史记录的双向关联
|
||||
|
||||
存储策略:
|
||||
- 内存缓存:最近的日志保存在内存中,支持快速访问
|
||||
- 数据库持久化:所有日志自动保存到SQLite数据库
|
||||
- 容量控制:内存缓存有容量限制,自动清理旧日志
|
||||
- 事务安全:数据库写入失败不影响程序运行
|
||||
|
||||
查询和分析:
|
||||
- 按批次查询:支持按查询批次获取相关日志
|
||||
- 按历史记录查询:支持按历史记录ID获取相关日志
|
||||
- 分页支持:大量日志的分页显示
|
||||
- 时间范围:支持按时间范围筛选日志
|
||||
- 日志清理:支持按时间清理旧日志
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
|
@@ -1,6 +1,36 @@
|
||||
"""
|
||||
Redis连接管理模块
|
||||
负责Redis集群的连接、错误处理和性能追踪
|
||||
===================
|
||||
|
||||
本模块提供Redis集群的连接管理和基础操作功能,支持单节点和集群模式。
|
||||
|
||||
核心功能:
|
||||
1. 智能连接管理:自动检测单节点和集群模式
|
||||
2. 连接池优化:高效的连接复用和资源管理
|
||||
3. 错误处理:完善的连接失败诊断和重试机制
|
||||
4. 性能监控:连接时间和操作性能的实时监控
|
||||
5. 类型检测:自动识别Redis数据类型
|
||||
|
||||
连接特性:
|
||||
- 自适应模式:根据节点数量自动选择连接方式
|
||||
- 连接池管理:每个节点独立的连接池配置
|
||||
- 超时控制:可配置的连接和操作超时时间
|
||||
- 密码认证:支持Redis AUTH认证
|
||||
- 健康检查:连接状态的实时监控
|
||||
|
||||
支持的Redis版本:
|
||||
- Redis 5.0+:完整功能支持
|
||||
- Redis Cluster:集群模式支持
|
||||
- Redis Sentinel:哨兵模式支持(通过配置)
|
||||
|
||||
错误诊断:
|
||||
- 连接超时:网络延迟和服务器负载分析
|
||||
- 认证失败:密码验证和权限检查
|
||||
- 集群错误:节点状态和集群配置验证
|
||||
- 数据类型错误:类型检测和转换建议
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import time
|
||||
|
@@ -1,6 +1,43 @@
|
||||
"""
|
||||
Redis查询和数据比较模块
|
||||
负责Redis数据的查询、随机key获取和数据比较
|
||||
Redis查询引擎模块
|
||||
=================
|
||||
|
||||
本模块是Redis数据比对的核心引擎,提供高级的Redis数据查询和比较功能。
|
||||
|
||||
核心功能:
|
||||
1. 多模式查询:随机采样和指定Key两种查询模式
|
||||
2. 全类型支持:支持所有Redis数据类型的查询和比较
|
||||
3. 智能比较:针对不同数据类型的专门比较算法
|
||||
4. 性能监控:详细的查询时间和性能统计
|
||||
5. 错误容错:单个Key查询失败不影响整体结果
|
||||
|
||||
查询模式:
|
||||
- 随机采样:从源集群随机获取指定数量的Key进行比对
|
||||
- 指定Key:对用户提供的Key列表进行精确比对
|
||||
- 模式匹配:支持通配符模式的Key筛选
|
||||
|
||||
支持的数据类型:
|
||||
- String:字符串类型,自动检测JSON格式
|
||||
- Hash:哈希表,字段级别的深度比较
|
||||
- List:列表,保持元素顺序的精确比较
|
||||
- Set:集合,自动排序后的内容比较
|
||||
- ZSet:有序集合,包含分数的完整比较
|
||||
- Stream:消息流,消息级别的详细比较
|
||||
|
||||
比较算法:
|
||||
- JSON智能比较:自动检测和比较JSON格式数据
|
||||
- 类型一致性检查:确保两个集群中数据类型一致
|
||||
- 内容深度比较:递归比较复杂数据结构
|
||||
- 性能优化:大数据集的高效比较算法
|
||||
|
||||
统计分析:
|
||||
- 一致性统计:相同、不同、缺失Key的详细统计
|
||||
- 类型分布:各种数据类型的分布统计
|
||||
- 性能指标:查询时间、连接时间等性能数据
|
||||
- 错误分析:查询失败的详细错误统计
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import time
|
||||
|
@@ -1,6 +1,29 @@
|
||||
"""
|
||||
Redis数据类型支持增强模块
|
||||
支持string、hash、list、set、zset、json等数据类型的比较
|
||||
================================
|
||||
|
||||
本模块提供对Redis所有主要数据类型的完整支持,包括:
|
||||
- String类型(包括JSON字符串的智能检测和格式化)
|
||||
- Hash类型(键值对映射)
|
||||
- List类型(有序列表)
|
||||
- Set类型(无序集合)
|
||||
- ZSet类型(有序集合,带分数)
|
||||
- Stream类型(消息流,完整支持消息解析和比较)
|
||||
|
||||
主要功能:
|
||||
1. get_redis_value_with_type() - 获取任意类型的Redis键值
|
||||
2. compare_redis_values() - 智能比较不同数据类型的值
|
||||
3. batch_get_redis_values_with_type() - 批量获取键值信息
|
||||
|
||||
设计特点:
|
||||
- 类型安全:自动检测并处理每种Redis数据类型
|
||||
- 编码处理:完善的UTF-8解码和二进制数据处理
|
||||
- JSON支持:智能识别和格式化JSON字符串
|
||||
- Stream支持:完整的Stream消息结构解析和比较
|
||||
- 错误处理:优雅处理连接错误和数据异常
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -11,19 +34,41 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
def get_redis_value_with_type(redis_client, key):
|
||||
"""
|
||||
获取Redis键值及其数据类型
|
||||
获取Redis键值及其数据类型的完整信息
|
||||
|
||||
这是本模块的核心函数,支持所有Redis数据类型的获取和解析。
|
||||
它会自动检测键的类型,然后使用相应的Redis命令获取数据,
|
||||
并进行适当的格式化处理。
|
||||
|
||||
Args:
|
||||
redis_client: Redis客户端
|
||||
key: Redis键名
|
||||
redis_client: Redis客户端连接对象
|
||||
key (str): 要查询的Redis键名
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
'type': 数据类型,
|
||||
'value': 值,
|
||||
'display_value': 用于显示的格式化值,
|
||||
'exists': 是否存在
|
||||
}
|
||||
dict: 包含以下字段的字典
|
||||
- 'type' (str): Redis数据类型 ('string', 'hash', 'list', 'set', 'zset', 'stream')
|
||||
- 'value': 解析后的原始值(Python对象)
|
||||
- 'display_value' (str): 格式化后用于显示的字符串
|
||||
- 'exists' (bool): 键是否存在
|
||||
|
||||
支持的数据类型处理:
|
||||
- String: 自动检测JSON格式,支持二进制数据
|
||||
- Hash: 完整的字段映射,UTF-8解码
|
||||
- List: 有序列表,保持原始顺序
|
||||
- Set: 无序集合,自动排序便于比较
|
||||
- ZSet: 有序集合,包含成员和分数
|
||||
- Stream: 完整的消息流解析,包含元数据和消息内容
|
||||
|
||||
异常处理:
|
||||
- 连接异常:返回错误状态
|
||||
- 编码异常:标记为二进制数据
|
||||
- 数据异常:记录警告并提供基本信息
|
||||
|
||||
示例:
|
||||
>>> result = get_redis_value_with_type(client, "user:1001")
|
||||
>>> print(result['type']) # 'string'
|
||||
>>> print(result['value']) # 'John Doe'
|
||||
>>> print(result['exists']) # True
|
||||
"""
|
||||
try:
|
||||
# 检查key是否存在
|
||||
@@ -43,27 +88,29 @@ def get_redis_value_with_type(redis_client, key):
|
||||
}
|
||||
|
||||
if key_type == 'string':
|
||||
# 字符串类型
|
||||
# String类型处理 - 支持普通字符串和JSON字符串的智能识别
|
||||
value = redis_client.get(key)
|
||||
if value:
|
||||
try:
|
||||
# 尝试解码为字符串
|
||||
# 尝试UTF-8解码
|
||||
str_value = value.decode('utf-8')
|
||||
result['value'] = str_value
|
||||
|
||||
# 尝试解析为JSON
|
||||
# 智能检测JSON格式并格式化显示
|
||||
try:
|
||||
json_value = json.loads(str_value)
|
||||
result['display_value'] = json.dumps(json_value, indent=2, ensure_ascii=False)
|
||||
result['type'] = 'json_string' # 标记为JSON字符串
|
||||
except:
|
||||
result['type'] = 'json_string' # 标记为JSON字符串类型
|
||||
except json.JSONDecodeError:
|
||||
# 不是JSON格式,直接显示字符串内容
|
||||
result['display_value'] = str_value
|
||||
|
||||
except UnicodeDecodeError:
|
||||
# 二进制数据
|
||||
# 处理二进制数据 - 无法UTF-8解码的数据
|
||||
result['value'] = value
|
||||
result['display_value'] = f"<binary data: {len(value)} bytes>"
|
||||
else:
|
||||
# 空字符串处理
|
||||
result['value'] = ""
|
||||
result['display_value'] = ""
|
||||
|
||||
@@ -130,6 +177,80 @@ def get_redis_value_with_type(redis_client, key):
|
||||
result['value'] = decoded_zset
|
||||
result['display_value'] = json.dumps(decoded_zset, indent=2, ensure_ascii=False)
|
||||
|
||||
elif key_type == 'stream':
|
||||
# Stream类型
|
||||
try:
|
||||
# 获取Stream信息
|
||||
stream_info = redis_client.xinfo_stream(key)
|
||||
|
||||
# 获取Stream中的消息(最多获取100条最新消息)
|
||||
stream_messages = redis_client.xrange(key, count=100)
|
||||
|
||||
# 解析Stream数据
|
||||
decoded_stream = {
|
||||
'info': {
|
||||
'length': stream_info.get('length', 0),
|
||||
'radix_tree_keys': stream_info.get('radix-tree-keys', 0),
|
||||
'radix_tree_nodes': stream_info.get('radix-tree-nodes', 0),
|
||||
'last_generated_id': stream_info.get('last-generated-id', '').decode('utf-8') if stream_info.get('last-generated-id') else '',
|
||||
'first_entry': None,
|
||||
'last_entry': None
|
||||
},
|
||||
'messages': []
|
||||
}
|
||||
|
||||
# 处理first-entry和last-entry
|
||||
if stream_info.get('first-entry'):
|
||||
first_entry = stream_info['first-entry']
|
||||
decoded_stream['info']['first_entry'] = {
|
||||
'id': first_entry[0].decode('utf-8'),
|
||||
'fields': {first_entry[1][i].decode('utf-8'): first_entry[1][i+1].decode('utf-8')
|
||||
for i in range(0, len(first_entry[1]), 2)}
|
||||
}
|
||||
|
||||
if stream_info.get('last-entry'):
|
||||
last_entry = stream_info['last-entry']
|
||||
decoded_stream['info']['last_entry'] = {
|
||||
'id': last_entry[0].decode('utf-8'),
|
||||
'fields': {last_entry[1][i].decode('utf-8'): last_entry[1][i+1].decode('utf-8')
|
||||
for i in range(0, len(last_entry[1]), 2)}
|
||||
}
|
||||
|
||||
# 处理消息列表
|
||||
for message in stream_messages:
|
||||
message_id = message[0].decode('utf-8')
|
||||
message_fields = message[1]
|
||||
|
||||
decoded_message = {
|
||||
'id': message_id,
|
||||
'fields': {}
|
||||
}
|
||||
|
||||
# 解析消息字段
|
||||
for i in range(0, len(message_fields), 2):
|
||||
try:
|
||||
field_name = message_fields[i].decode('utf-8')
|
||||
field_value = message_fields[i+1].decode('utf-8')
|
||||
decoded_message['fields'][field_name] = field_value
|
||||
except (IndexError, UnicodeDecodeError):
|
||||
continue
|
||||
|
||||
decoded_stream['messages'].append(decoded_message)
|
||||
|
||||
result['value'] = decoded_stream
|
||||
result['display_value'] = json.dumps(decoded_stream, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as stream_error:
|
||||
logger.warning(f"获取Stream详细信息失败 {key}: {stream_error}")
|
||||
# 如果详细获取失败,至少获取基本信息
|
||||
try:
|
||||
stream_length = redis_client.xlen(key)
|
||||
result['value'] = {'length': stream_length, 'messages': []}
|
||||
result['display_value'] = f"Stream (length: {stream_length} messages)"
|
||||
except:
|
||||
result['value'] = "Stream data (unable to read details)"
|
||||
result['display_value'] = "Stream data (unable to read details)"
|
||||
|
||||
else:
|
||||
# 未知类型
|
||||
result['value'] = f"<unsupported type: {key_type}>"
|
||||
@@ -238,6 +359,58 @@ def compare_redis_values(value1_info, value2_info):
|
||||
else:
|
||||
return {'status': 'different', 'message': f'有序集合不同,大小: {len(value1)} vs {len(value2)}'}
|
||||
|
||||
elif type1 == 'stream':
|
||||
# Stream比较
|
||||
if value1 == value2:
|
||||
return {'status': 'identical', 'message': 'Stream完全相同'}
|
||||
else:
|
||||
# 详细比较Stream
|
||||
if isinstance(value1, dict) and isinstance(value2, dict):
|
||||
# 比较Stream基本信息
|
||||
info1 = value1.get('info', {})
|
||||
info2 = value2.get('info', {})
|
||||
|
||||
if info1.get('length', 0) != info2.get('length', 0):
|
||||
return {
|
||||
'status': 'different',
|
||||
'message': f'Stream长度不同: {info1.get("length", 0)} vs {info2.get("length", 0)}'
|
||||
}
|
||||
|
||||
# 比较最后生成的ID
|
||||
if info1.get('last_generated_id') != info2.get('last_generated_id'):
|
||||
return {
|
||||
'status': 'different',
|
||||
'message': f'Stream最后ID不同: {info1.get("last_generated_id")} vs {info2.get("last_generated_id")}'
|
||||
}
|
||||
|
||||
# 比较消息内容
|
||||
messages1 = value1.get('messages', [])
|
||||
messages2 = value2.get('messages', [])
|
||||
|
||||
if len(messages1) != len(messages2):
|
||||
return {
|
||||
'status': 'different',
|
||||
'message': f'Stream消息数量不同: {len(messages1)} vs {len(messages2)}'
|
||||
}
|
||||
|
||||
# 比较具体消息
|
||||
for i, (msg1, msg2) in enumerate(zip(messages1, messages2)):
|
||||
if msg1.get('id') != msg2.get('id'):
|
||||
return {
|
||||
'status': 'different',
|
||||
'message': f'Stream消息ID不同 (第{i+1}条): {msg1.get("id")} vs {msg2.get("id")}'
|
||||
}
|
||||
|
||||
if msg1.get('fields') != msg2.get('fields'):
|
||||
return {
|
||||
'status': 'different',
|
||||
'message': f'Stream消息内容不同 (第{i+1}条消息)'
|
||||
}
|
||||
|
||||
return {'status': 'identical', 'message': 'Stream数据相同'}
|
||||
else:
|
||||
return {'status': 'different', 'message': 'Stream数据格式不同'}
|
||||
|
||||
else:
|
||||
# 其他类型的通用比较
|
||||
if value1 == value2:
|
||||
|
@@ -1,6 +1,41 @@
|
||||
"""
|
||||
分表计算模块
|
||||
负责TWCS时间分表的计算和映射
|
||||
TWCS分表计算引擎模块
|
||||
===================
|
||||
|
||||
本模块实现基于TWCS(Time Window Compaction Strategy)策略的时间分表计算功能。
|
||||
|
||||
核心功能:
|
||||
1. 时间戳提取:从Key中智能提取时间戳信息
|
||||
2. 分表索引计算:基于时间窗口计算目标分表索引
|
||||
3. 分表映射:将大批量Key映射到对应的分表
|
||||
4. 统计分析:提供分表计算的详细统计信息
|
||||
|
||||
TWCS分表策略:
|
||||
- 时间窗口:可配置的时间间隔(默认7天)
|
||||
- 分表数量:可配置的分表总数(默认14张)
|
||||
- 计算公式:timestamp // interval_seconds % table_count
|
||||
- 表命名:base_table_name + "_" + shard_index
|
||||
|
||||
时间戳提取算法:
|
||||
- 优先规则:提取Key中最后一个下划线后的数字
|
||||
- 备用规则:提取Key中最长的数字序列
|
||||
- 容错处理:无法提取时记录到失败列表
|
||||
- 格式支持:支持各种Key格式的时间戳提取
|
||||
|
||||
应用场景:
|
||||
- 大数据表的时间分片:按时间窗口将数据分散到多张表
|
||||
- 查询性能优化:减少单表数据量,提高查询效率
|
||||
- 数据生命周期管理:支持按时间窗口的数据清理
|
||||
- 负载均衡:将查询负载分散到多张表
|
||||
|
||||
性能特点:
|
||||
- 批量计算:支持大批量Key的高效分表计算
|
||||
- 内存友好:使用生成器和迭代器优化内存使用
|
||||
- 统计完整:提供详细的计算成功率和分布统计
|
||||
- 错误容错:单个Key计算失败不影响整体处理
|
||||
|
||||
作者:BigDataTool项目组
|
||||
更新时间:2024年8月
|
||||
"""
|
||||
|
||||
import re
|
||||
@@ -9,7 +44,24 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ShardingCalculator:
|
||||
"""分表计算器,基于TWCS策略"""
|
||||
"""
|
||||
TWCS分表计算器
|
||||
|
||||
基于Time Window Compaction Strategy实现的智能分表计算器,
|
||||
用于将时间相关的Key映射到对应的时间窗口分表。
|
||||
|
||||
主要特性:
|
||||
- 时间窗口分片:按配置的时间间隔进行分表
|
||||
- 智能时间戳提取:支持多种Key格式的时间戳解析
|
||||
- 负载均衡:通过取模运算实现分表间的负载均衡
|
||||
- 批量处理:高效处理大批量Key的分表映射
|
||||
|
||||
适用场景:
|
||||
- 时序数据的分表存储
|
||||
- 大数据表的性能优化
|
||||
- 数据生命周期管理
|
||||
- 查询负载分散
|
||||
"""
|
||||
|
||||
def __init__(self, interval_seconds=604800, table_count=14):
|
||||
"""
|
||||
|
@@ -1,696 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Redis集群比对工具</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.config-section {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.result-section {
|
||||
margin-top: 30px;
|
||||
}
|
||||
.difference-item {
|
||||
border-left: 4px solid #dc3545;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 15px;
|
||||
background-color: #fff5f5;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.identical-item {
|
||||
border-left: 4px solid #28a745;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 15px;
|
||||
background-color: #f8fff8;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.missing-item {
|
||||
border-left: 4px solid #ffc107;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 15px;
|
||||
background-color: #fffbf0;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.stat-card {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.loading {
|
||||
display: none;
|
||||
}
|
||||
.query-keys {
|
||||
min-height: 120px;
|
||||
}
|
||||
.redis-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
background-color: #f8f9fa !important;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
.node-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.node-input input {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.performance-section {
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.log-viewer {
|
||||
background-color: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.navbar {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||
}
|
||||
.breadcrumb {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
.breadcrumb-item a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn-redis {
|
||||
background: linear-gradient(135deg, #dc143c 0%, #b91c1c 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
}
|
||||
.btn-redis:hover {
|
||||
background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.redis-logo {
|
||||
color: #dc143c;
|
||||
}
|
||||
.cluster-config {
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.cluster-config.active {
|
||||
border-color: #dc143c;
|
||||
background-color: #fff8f8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">
|
||||
<i class="fas fa-database me-2"></i>
|
||||
大数据工具集合
|
||||
</a>
|
||||
<div class="navbar-nav ms-auto">
|
||||
<a class="nav-link" href="/">
|
||||
<i class="fas fa-home"></i> 首页
|
||||
</a>
|
||||
<a class="nav-link" href="/db-compare">
|
||||
<i class="fas fa-exchange-alt"></i> 数据库比对
|
||||
</a>
|
||||
<a class="nav-link active" href="/redis-compare">
|
||||
<i class="fab fa-redis"></i> Redis比对
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<!-- 面包屑导航 -->
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/">首页</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Redis集群比对工具</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fab fa-redis redis-logo fs-1 me-3"></i>
|
||||
<div>
|
||||
<h1 class="mb-1">Redis集群比对工具</h1>
|
||||
<p class="text-muted mb-0">专业的Redis集群数据比对工具,支持随机采样和指定Key查询</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 配置区域 -->
|
||||
<div class="row">
|
||||
<!-- 配置管理 -->
|
||||
<div class="col-lg-12">
|
||||
<div class="config-section mb-4">
|
||||
<h4><i class="fas fa-cogs me-2"></i>配置管理</h4>
|
||||
|
||||
<div class="row">
|
||||
<!-- 配置组管理 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6><i class="fas fa-layer-group me-2"></i>配置组管理</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-8">
|
||||
<select class="form-select form-select-sm" id="redisConfigGroupSelect">
|
||||
<option value="">选择Redis配置组...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<button class="btn btn-primary btn-sm w-100" onclick="loadSelectedRedisConfigGroup()">
|
||||
<i class="fas fa-download"></i> 加载
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button class="btn btn-success btn-sm w-100" onclick="showSaveRedisConfigDialog()">
|
||||
<i class="fas fa-save"></i> 保存配置
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button class="btn btn-info btn-sm w-100" onclick="showManageRedisConfigDialog()">
|
||||
<i class="fas fa-cog"></i> 管理配置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 查询历史 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6><i class="fas fa-history me-2"></i>查询历史</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-8">
|
||||
<select class="form-select form-select-sm" id="redisHistorySelect">
|
||||
<option value="">选择历史记录...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<button class="btn btn-warning btn-sm w-100" onclick="loadSelectedRedisHistory()">
|
||||
<i class="fas fa-history"></i> 加载
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button class="btn btn-secondary btn-sm w-100" onclick="showSaveRedisHistoryDialog()">
|
||||
<i class="fas fa-bookmark"></i> 保存历史
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button class="btn btn-info btn-sm w-100" onclick="showManageRedisHistoryDialog()">
|
||||
<i class="fas fa-list"></i> 管理历史
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 一键导入区域 -->
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6><i class="fas fa-download me-2"></i>配置导入</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<button class="btn btn-outline-primary btn-sm me-2" onclick="showImportRedisConfigDialog('cluster1')">
|
||||
<i class="fas fa-download"></i> 导入集群1配置
|
||||
</button>
|
||||
<button class="btn btn-outline-primary btn-sm" onclick="showImportRedisConfigDialog('cluster2')">
|
||||
<i class="fas fa-download"></i> 导入集群2配置
|
||||
</button>
|
||||
<small class="form-text text-muted d-block mt-2">
|
||||
支持YAML格式配置导入,如:clusterName、clusterAddress、clusterPassword等
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6><i class="fas fa-file-alt me-2"></i>查询日志</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<button class="btn btn-outline-info btn-sm" onclick="showRedisQueryLogsDialog()">
|
||||
<i class="fas fa-eye"></i> 查看查询日志
|
||||
</button>
|
||||
<small class="form-text text-muted d-block mt-2">
|
||||
查看Redis比较操作的详细执行日志
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis集群配置 -->
|
||||
<div class="col-lg-12">
|
||||
<div class="config-section">
|
||||
<h4><i class="fas fa-server me-2"></i>Redis集群配置</h4>
|
||||
|
||||
<div class="row">
|
||||
<!-- 第一个集群配置 -->
|
||||
<div class="col-md-6">
|
||||
<div class="cluster-config" id="cluster1-config">
|
||||
<h5><i class="fas fa-database me-2"></i>集群1 (生产环境)</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">集群名称</label>
|
||||
<input type="text" class="form-control" id="cluster1-name" value="生产集群" placeholder="输入集群名称">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">节点列表</label>
|
||||
<div id="cluster1-nodes">
|
||||
<div class="node-input">
|
||||
<input type="text" class="form-control node-host" placeholder="主机地址" value="127.0.0.1">
|
||||
<input type="number" class="form-control node-port" placeholder="端口" value="7000" style="width: 100px;">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-node">
|
||||
<i class="fas fa-minus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addNode('cluster1')">
|
||||
<i class="fas fa-plus"></i> 添加节点
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">密码 (可选)</label>
|
||||
<input type="password" class="form-control" id="cluster1-password" placeholder="Redis密码">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label class="form-label">连接超时 (秒)</label>
|
||||
<input type="number" class="form-control" id="cluster1-timeout" value="3" min="1" max="30">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label">最大连接数</label>
|
||||
<input type="number" class="form-control" id="cluster1-max-conn" value="16" min="1" max="100">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="button" class="btn btn-outline-success btn-sm" onclick="testConnection('cluster1')">
|
||||
<i class="fas fa-plug"></i> 测试连接
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二个集群配置 -->
|
||||
<div class="col-md-6">
|
||||
<div class="cluster-config" id="cluster2-config">
|
||||
<h5><i class="fas fa-database me-2"></i>集群2 (测试环境)</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">集群名称</label>
|
||||
<input type="text" class="form-control" id="cluster2-name" value="测试集群" placeholder="输入集群名称">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">节点列表</label>
|
||||
<div id="cluster2-nodes">
|
||||
<div class="node-input">
|
||||
<input type="text" class="form-control node-host" placeholder="主机地址" value="127.0.0.1">
|
||||
<input type="number" class="form-control node-port" placeholder="端口" value="7001" style="width: 100px;">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-node">
|
||||
<i class="fas fa-minus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addNode('cluster2')">
|
||||
<i class="fas fa-plus"></i> 添加节点
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">密码 (可选)</label>
|
||||
<input type="password" class="form-control" id="cluster2-password" placeholder="Redis密码">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label class="form-label">连接超时 (秒)</label>
|
||||
<input type="number" class="form-control" id="cluster2-timeout" value="3" min="1" max="30">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label">最大连接数</label>
|
||||
<input type="number" class="form-control" id="cluster2-max-conn" value="16" min="1" max="100">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="button" class="btn btn-outline-success btn-sm" onclick="testConnection('cluster2')">
|
||||
<i class="fas fa-plug"></i> 测试连接
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 查询选项 -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="config-section">
|
||||
<h4><i class="fas fa-search me-2"></i>查询选项</h4>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">查询模式</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="queryMode" id="randomMode" value="random" checked>
|
||||
<label class="form-check-label" for="randomMode">
|
||||
<i class="fas fa-random me-1"></i>随机采样查询
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="queryMode" id="specifiedMode" value="specified">
|
||||
<label class="form-check-label" for="specifiedMode">
|
||||
<i class="fas fa-list me-1"></i>指定Key查询
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 随机采样选项 -->
|
||||
<div id="randomOptions">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">采样数量</label>
|
||||
<input type="number" class="form-control" id="sampleCount" value="100" min="1" max="10000">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Key匹配模式</label>
|
||||
<input type="text" class="form-control" id="keyPattern" value="*" placeholder="例如: user:*">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">数据源集群</label>
|
||||
<select class="form-select" id="sourceCluster">
|
||||
<option value="cluster1">集群1 (生产环境)</option>
|
||||
<option value="cluster2" selected>集群2 (测试环境)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 指定Key选项 -->
|
||||
<div id="specifiedOptions" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">指定Key列表 (每行一个)</label>
|
||||
<textarea class="form-control query-keys" id="specifiedKeys"
|
||||
placeholder="请输入要查询的Key,每行一个 例如: user:1001 user:1002 session:abc123"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="text-center">
|
||||
<button type="button" class="btn btn-redis btn-lg me-3" onclick="executeRedisComparison()">
|
||||
<i class="fas fa-play me-2"></i>开始比较
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-lg" onclick="clearResults()">
|
||||
<i class="fas fa-eraser me-2"></i>清空结果
|
||||
</button>
|
||||
|
||||
<div class="loading mt-3">
|
||||
<div class="spinner-border text-danger me-2" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<span>正在执行Redis数据比较,请稍候...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 结果展示区域 -->
|
||||
<div class="result-section" id="results" style="display: none;">
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row" id="statsCards">
|
||||
<!-- 统计卡片将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
|
||||
<!-- 详细结果 -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<ul class="nav nav-tabs" id="resultTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="differences-tab" data-bs-toggle="tab" data-bs-target="#differences" type="button" role="tab">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i>差异数据 (<span id="diff-count">0</span>)
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="identical-tab" data-bs-toggle="tab" data-bs-target="#identical" type="button" role="tab">
|
||||
<i class="fas fa-check-circle me-1"></i>相同数据 (<span id="identical-count">0</span>)
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="missing-tab" data-bs-toggle="tab" data-bs-target="#missing" type="button" role="tab">
|
||||
<i class="fas fa-question-circle me-1"></i>缺失数据 (<span id="missing-count">0</span>)
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="performance-tab" data-bs-toggle="tab" data-bs-target="#performance" type="button" role="tab">
|
||||
<i class="fas fa-chart-line me-1"></i>性能报告
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="resultTabContent">
|
||||
<!-- 差异数据 -->
|
||||
<div class="tab-pane fade show active" id="differences" role="tabpanel">
|
||||
<div class="mt-3" id="differenceResults">
|
||||
<!-- 差异结果将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 相同数据 -->
|
||||
<div class="tab-pane fade" id="identical" role="tabpanel">
|
||||
<div class="mt-3" id="identicalResults">
|
||||
<!-- 相同结果将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 缺失数据 -->
|
||||
<div class="tab-pane fade" id="missing" role="tabpanel">
|
||||
<div class="mt-3" id="missingResults">
|
||||
<!-- 缺失结果将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 性能报告 -->
|
||||
<div class="tab-pane fade" id="performance" role="tabpanel">
|
||||
<div class="performance-section mt-3" id="performanceReport">
|
||||
<!-- 性能报告将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis配置组保存模态框 -->
|
||||
<div class="modal fade" id="saveRedisConfigModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-save me-2"></i>保存Redis配置组</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">配置组名称 *</label>
|
||||
<input type="text" class="form-control" id="redisConfigGroupName" placeholder="输入配置组名称">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">描述</label>
|
||||
<textarea class="form-control" id="redisConfigGroupDescription" placeholder="输入配置组描述(可选)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-success" onclick="saveRedisConfigGroup()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis配置管理模态框 -->
|
||||
<div class="modal fade" id="manageRedisConfigModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-cog me-2"></i>Redis配置组管理</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="redisConfigGroupList">
|
||||
<!-- 配置组列表将在这里动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis配置导入模态框 -->
|
||||
<div class="modal fade" id="importRedisConfigModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-download me-2"></i>导入Redis配置</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info">
|
||||
<strong>支持的配置格式:</strong><br>
|
||||
clusterName: "redis-test"<br>
|
||||
clusterAddress: "10.20.2.109:6470"<br>
|
||||
clusterPassword: ""<br>
|
||||
cachePrefix: message.status.Writer.<br>
|
||||
cacheTtl: 2000<br>
|
||||
async: true
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">配置内容</label>
|
||||
<textarea class="form-control" id="redisConfigImportText" rows="8"
|
||||
placeholder="请粘贴Redis配置内容..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" onclick="importRedisConfig()">导入配置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis查询历史保存模态框 -->
|
||||
<div class="modal fade" id="saveRedisHistoryModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-bookmark me-2"></i>保存Redis查询历史</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">历史记录名称 *</label>
|
||||
<input type="text" class="form-control" id="redisHistoryName" placeholder="输入历史记录名称">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">描述</label>
|
||||
<textarea class="form-control" id="redisHistoryDescription" placeholder="输入历史记录描述(可选)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-success" onclick="saveRedisQueryHistory()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis查询历史管理模态框 -->
|
||||
<div class="modal fade" id="manageRedisHistoryModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-list me-2"></i>Redis查询历史管理</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="redisHistoryList">
|
||||
<!-- 查询历史列表将在这里动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redis查询日志模态框 -->
|
||||
<div class="modal fade" id="redisQueryLogsModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-file-alt me-2"></i>Redis查询日志</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="mb-0">Redis查询执行日志</h6>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-primary me-2" onclick="refreshRedisQueryLogs()">
|
||||
<i class="fas fa-sync-alt"></i> 刷新
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="clearRedisQueryLogs()">
|
||||
<i class="fas fa-trash"></i> 清空
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="redisQueryLogs" class="log-viewer">
|
||||
<!-- 查询日志将在这里动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引入必要的JavaScript -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/js/redis_compare.js"></script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user