修复redis识别

This commit is contained in:
2025-08-04 22:07:42 +08:00
parent 3272525c92
commit 701a9a552e
2 changed files with 87 additions and 84 deletions

View File

@@ -86,7 +86,7 @@ def get_random_keys_from_redis(redis_client, count=100, pattern="*", performance
def get_redis_values_by_keys(redis_client, keys, cluster_name="Redis集群", performance_tracker=None):
"""
批量查询Redis中指定keys的值自动适配单节点和集群模式
批量查询Redis中指定keys的值支持所有Redis数据类型String、Hash、List、Set、ZSet等
Args:
redis_client: Redis客户端
@@ -95,50 +95,39 @@ def get_redis_values_by_keys(redis_client, keys, cluster_name="Redis集群", per
performance_tracker: 性能追踪器
Returns:
list: 对应keys的值列表如果key不存在则为None
list: 对应keys的值信息字典列表,包含类型、值和显示格式
"""
start_time = time.time()
result = [None] * len(keys)
from .redis_types import get_redis_value_with_type
logger.info(f"开始从{cluster_name}批量查询 {len(keys)} 个keys")
query_log_collector.add_log('INFO', f"开始从{cluster_name}批量查询 {len(keys)} 个keys")
start_time = time.time()
result = []
logger.info(f"开始从{cluster_name}批量查询 {len(keys)} 个keys支持所有数据类型")
query_log_collector.add_log('INFO', f"开始从{cluster_name}批量查询 {len(keys)} 个keys支持所有数据类型")
try:
# 检查是否是集群模式
is_cluster = hasattr(redis_client, 'cluster_nodes')
if is_cluster:
# 集群模式按slot分组keys以优化查询性能
slot_groups = {}
for idx, key in enumerate(keys):
slot = key_slot(key)
slot_groups.setdefault(slot, []).append((idx, key))
logger.info(f"集群模式keys分布在 {len(slot_groups)} 个slot中")
query_log_collector.add_log('INFO', f"集群模式keys分布在 {len(slot_groups)} 个slot中")
# 分组批量查询
for group in slot_groups.values():
indices, slot_keys = zip(*group)
values = redis_client.mget(slot_keys)
for i, v in zip(indices, values):
result[i] = v
else:
# 单节点模式:直接批量查询
logger.info(f"单节点模式:直接批量查询")
query_log_collector.add_log('INFO', f"单节点模式:直接批量查询")
result = redis_client.mget(keys)
# 逐个查询每个key支持所有Redis数据类型
for key in keys:
key_info = get_redis_value_with_type(redis_client, key)
result.append(key_info)
end_time = time.time()
query_duration = end_time - start_time
if performance_tracker:
performance_tracker.record_query(f"{cluster_name}_batch_query", query_duration)
performance_tracker.record_query(f"{cluster_name}_typed_batch_query", query_duration)
# 统计成功获取的key数量
successful_count = sum(1 for v in result if v is not None)
logger.info(f"{cluster_name}查询完成,成功获取 {successful_count}/{len(keys)} 个值,耗时 {query_duration:.3f}")
query_log_collector.add_log('INFO', f"{cluster_name}查询完成,成功获取 {successful_count}/{len(keys)} 个值,耗时 {query_duration:.3f}")
# 统计成功获取的key数量和类型分布
successful_count = sum(1 for r in result if r['exists'])
type_stats = {}
for r in result:
if r['exists']:
key_type = r['type']
type_stats[key_type] = type_stats.get(key_type, 0) + 1
type_info = ", ".join([f"{t}: {c}" for t, c in type_stats.items()]) if type_stats else ""
logger.info(f"{cluster_name}查询完成,成功获取 {successful_count}/{len(keys)} 个值,数据类型分布: [{type_info}],耗时 {query_duration:.3f}")
query_log_collector.add_log('INFO', f"{cluster_name}查询完成,成功获取 {successful_count}/{len(keys)} 个值,数据类型分布: [{type_info}],耗时 {query_duration:.3f}")
return result
@@ -147,15 +136,16 @@ def get_redis_values_by_keys(redis_client, keys, cluster_name="Redis集群", per
query_duration = end_time - start_time
if performance_tracker:
performance_tracker.record_query(f"{cluster_name}_batch_query_error", query_duration)
performance_tracker.record_query(f"{cluster_name}_typed_batch_query_error", query_duration)
logger.error(f"{cluster_name}批量查询失败: {e},耗时 {query_duration:.3f}")
query_log_collector.add_log('ERROR', f"{cluster_name}批量查询失败: {e},耗时 {query_duration:.3f}")
return result
# 返回错误占位符
return [{'type': 'error', 'value': None, 'display_value': f'<error: {e}>', 'exists': False} for _ in keys]
def compare_redis_data(client1, client2, keys, cluster1_name="生产集群", cluster2_name="测试集群", performance_tracker=None):
"""
比较两个Redis集群中指定keys的数据
比较两个Redis集群中指定keys的数据支持所有Redis数据类型
Args:
client1: 第一个Redis客户端生产
@@ -168,18 +158,20 @@ def compare_redis_data(client1, client2, keys, cluster1_name="生产集群", clu
Returns:
dict: 比较结果,包含统计信息和差异详情
"""
from .redis_types import compare_redis_values
comparison_start_time = time.time()
logger.info(f"开始比较 {cluster1_name}{cluster2_name} 的数据")
query_log_collector.add_log('INFO', f"开始比较 {cluster1_name}{cluster2_name} 的数据")
logger.info(f"开始比较 {cluster1_name}{cluster2_name} 的数据支持所有Redis数据类型")
query_log_collector.add_log('INFO', f"开始比较 {cluster1_name}{cluster2_name} 的数据支持所有Redis数据类型")
# 获取两个集群的数据
values1 = get_redis_values_by_keys(client1, keys, cluster1_name, performance_tracker)
if values1 is None:
if not values1:
return {'error': f'{cluster1_name}获取数据失败'}
values2 = get_redis_values_by_keys(client2, keys, cluster2_name, performance_tracker)
if values2 is None:
if not values2:
return {'error': f'{cluster2_name}获取数据失败'}
# 开始数据比对
@@ -202,56 +194,56 @@ def compare_redis_data(client1, client2, keys, cluster1_name="生产集群", clu
# 逐个比较
for i, key in enumerate(keys):
val1 = values1[i]
val2 = values2[i]
key_str = key.decode('utf-8') if isinstance(key, bytes) else key
value1_info = values1[i]
value2_info = values2[i]
# 将bytes转换为字符串用于显示如果是bytes类型
display_val1 = val1.decode('utf-8') if isinstance(val1, bytes) else val1
display_val2 = val2.decode('utf-8') if isinstance(val2, bytes) else val2
# 使用redis_types模块的比较函数
comparison_result = compare_redis_values(value1_info, value2_info)
if val1 is None and val2 is None:
# 两个集群都没有这个key
if comparison_result['status'] == 'both_missing':
stats['both_missing'] += 1
missing_results.append({
'key': key.decode('utf-8') if isinstance(key, bytes) else key,
'key': key_str,
'status': 'both_missing',
'message': '两个集群都不存在该key'
'message': comparison_result['message']
})
elif val1 is None:
# 只有第一个集群没有
elif comparison_result['status'] == 'missing_in_cluster1':
stats['missing_in_cluster1'] += 1
missing_results.append({
'key': key.decode('utf-8') if isinstance(key, bytes) else key,
'key': key_str,
'status': 'missing_in_cluster1',
'cluster1_value': None,
'cluster2_value': display_val2,
'message': f'{cluster1_name}中不存在'
'cluster2_value': value2_info['display_value'],
'cluster2_type': value2_info['type'],
'message': comparison_result['message']
})
elif val2 is None:
# 只有第二个集群没有
elif comparison_result['status'] == 'missing_in_cluster2':
stats['missing_in_cluster2'] += 1
missing_results.append({
'key': key.decode('utf-8') if isinstance(key, bytes) else key,
'key': key_str,
'status': 'missing_in_cluster2',
'cluster1_value': display_val1,
'cluster1_value': value1_info['display_value'],
'cluster1_type': value1_info['type'],
'cluster2_value': None,
'message': f'{cluster2_name}中不存在'
'message': comparison_result['message']
})
elif val1 == val2:
# 值相同
elif comparison_result['status'] == 'identical':
stats['identical_count'] += 1
identical_results.append({
'key': key.decode('utf-8') if isinstance(key, bytes) else key,
'value': display_val1
'key': key_str,
'value': value1_info['display_value'],
'type': value1_info['type']
})
else:
# 值不同
else: # different
stats['different_count'] += 1
different_results.append({
'key': key.decode('utf-8') if isinstance(key, bytes) else key,
'cluster1_value': display_val1,
'cluster2_value': display_val2,
'message': '值不同'
'key': key_str,
'cluster1_value': value1_info['display_value'],
'cluster1_type': value1_info['type'],
'cluster2_value': value2_info['display_value'],
'cluster2_type': value2_info['type'],
'message': comparison_result['message']
})
compare_end = time.time()

View File

@@ -399,16 +399,19 @@ function displayDifferenceResults(differences) {
let html = '';
differences.forEach((diff, index) => {
const cluster1Type = diff.cluster1_type ? ` (${diff.cluster1_type})` : '';
const cluster2Type = diff.cluster2_type ? ` (${diff.cluster2_type})` : '';
html += `
<div class="difference-item">
<h6><i class="fas fa-key me-2"></i>Key: <code>${diff.key}</code></h6>
<div class="row">
<div class="col-md-6">
<strong>${currentResults.clusters.cluster1_name || '集群1'}:</strong>
<strong>${currentResults.clusters.cluster1_name || '集群1'}${cluster1Type}:</strong>
<pre class="redis-value mt-2">${formatRedisValue(diff.cluster1_value)}</pre>
</div>
<div class="col-md-6">
<strong>${currentResults.clusters.cluster2_name || '集群2'}:</strong>
<strong>${currentResults.clusters.cluster2_name || '集群2'}${cluster2Type}:</strong>
<pre class="redis-value mt-2">${formatRedisValue(diff.cluster2_value)}</pre>
</div>
</div>
@@ -439,11 +442,13 @@ function displayIdenticalResults(identical) {
let html = '';
identical.forEach((item, index) => {
const typeInfo = item.type ? ` (${item.type})` : '';
html += `
<div class="identical-item">
<h6><i class="fas fa-key me-2"></i>Key: <code>${item.key}</code></h6>
<div class="mt-2">
<strong>值:</strong>
<strong>值${typeInfo}:</strong>
<pre class="redis-value mt-2">${formatRedisValue(item.value)}</pre>
</div>
<div class="mt-2">
@@ -479,15 +484,15 @@ function displayMissingResults(missing) {
<div class="mt-2">
<span class="badge bg-warning">${item.message}</span>
</div>
${item.cluster1_value !== undefined ? `
${item.cluster1_value !== undefined && item.cluster1_value !== null ? `
<div class="mt-2">
<strong>${currentResults.clusters.cluster1_name || '集群1'}:</strong>
<strong>${currentResults.clusters.cluster1_name || '集群1'}${item.cluster1_type ? ` (${item.cluster1_type})` : ''}:</strong>
<pre class="redis-value mt-1">${formatRedisValue(item.cluster1_value)}</pre>
</div>
` : ''}
${item.cluster2_value !== undefined ? `
${item.cluster2_value !== undefined && item.cluster2_value !== null ? `
<div class="mt-2">
<strong>${currentResults.clusters.cluster2_name || '集群2'}:</strong>
<strong>${currentResults.clusters.cluster2_name || '集群2'}${item.cluster2_type ? ` (${item.cluster2_type})` : ''}:</strong>
<pre class="redis-value mt-1">${formatRedisValue(item.cluster2_value)}</pre>
</div>
` : ''}
@@ -1674,6 +1679,7 @@ function displayRawData(results) {
function collectClusterData(results, clusterType) {
const data = [];
const clusterField = clusterType === 'cluster1' ? 'cluster1_value' : 'cluster2_value';
const clusterTypeField = clusterType === 'cluster1' ? 'cluster1_type' : 'cluster2_type';
// 从相同结果中收集数据
if (results.identical_results) {
@@ -1682,7 +1688,8 @@ function collectClusterData(results, clusterType) {
data.push({
key: item.key,
value: item.value,
type: 'identical'
type: 'identical',
redis_type: item.type || 'unknown'
});
}
});
@@ -1695,7 +1702,8 @@ function collectClusterData(results, clusterType) {
data.push({
key: item.key,
value: item[clusterField],
type: 'different'
type: 'different',
redis_type: item[clusterTypeField] || 'unknown'
});
}
});
@@ -1704,11 +1712,12 @@ function collectClusterData(results, clusterType) {
// 从缺失结果中收集数据
if (results.missing_results) {
results.missing_results.forEach(item => {
if (item.key && item[clusterField] !== undefined) {
if (item.key && item[clusterField] !== undefined && item[clusterField] !== null) {
data.push({
key: item.key,
value: item[clusterField],
type: 'missing'
type: 'missing',
redis_type: item[clusterTypeField] || 'unknown'
});
}
});
@@ -1740,11 +1749,12 @@ function renderRawData(clusterId, data, viewType) {
data.forEach((item, index) => {
const typeClass = getTypeClass(item.type);
const formattedValue = formatRedisValue(item.value);
const redisTypeInfo = item.redis_type ? ` [${item.redis_type}]` : '';
html += `
<div class="raw-data-item mb-3 p-3 border rounded ${typeClass}">
<div class="d-flex justify-content-between align-items-center mb-2">
<strong class="text-primary">Key: ${escapeHtml(item.key)}</strong>
<strong class="text-primary">Key: ${escapeHtml(item.key)}${redisTypeInfo}</strong>
<span class="badge bg-secondary">${item.type}</span>
</div>
<div class="raw-data-value">
@@ -1760,7 +1770,8 @@ function renderRawData(clusterId, data, viewType) {
html += '<pre class="redis-value">';
data.forEach((item, index) => {
html += `Key: ${escapeHtml(item.key)}\n`;
html += `Type: ${item.type}\n`;
html += `Redis Type: ${item.redis_type || 'unknown'}\n`;
html += `Comparison Type: ${item.type}\n`;
html += `Value: ${escapeHtml(String(item.value))}\n`;
if (index < data.length - 1) {
html += '\n' + '='.repeat(50) + '\n\n';