From c0b24bef6981b8e851b1391c8970d104355b1e79 Mon Sep 17 00:00:00 2001 From: tangwang Date: Wed, 22 Oct 2025 16:40:14 +0800 Subject: [PATCH] fix redis data --- offline_tasks/doc/Redis_Key格式问题修复方案.md | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ offline_tasks/doc/详细设计文档.md | 2 -- offline_tasks/run.sh | 9 +++++++++ offline_tasks/scripts/fix_redis_keys.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ offline_tasks/scripts/load_index_to_redis.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 419 insertions(+), 4 deletions(-) create mode 100644 offline_tasks/doc/Redis_Key格式问题修复方案.md create mode 100644 offline_tasks/scripts/fix_redis_keys.py diff --git a/offline_tasks/doc/Redis_Key格式问题修复方案.md b/offline_tasks/doc/Redis_Key格式问题修复方案.md new file mode 100644 index 0000000..e0be962 --- /dev/null +++ b/offline_tasks/doc/Redis_Key格式问题修复方案.md @@ -0,0 +1,286 @@ +# Redis Key格式问题修复方案 + +## 🔍 问题诊断 + +### 发现的问题 + +经过排查,发现了以下三个关键问题: + +1. **Redis Key 格式错误** ❌ + - **实际存储**: `i2i:content_name:*`, `i2i:deepwalk:*`, `i2i:session_w2v:*` + - **文档定义**: `item:similar:content_name:*`, `item:similar:deepwalk:*`, `item:similar:session_w2v:*` + - **影响**: 查询 `KEYS item:similar:*` 返回空,因为key前缀不匹配 + +2. **C++ Swing 未被加载** ❌ + - `collaboration/output/swing_similar.txt` 存在(15,177条记录,6.9MB) + - 但加载脚本没有处理这个文件 + - 应该存储为: `item:similar:swing_cpp:*` + +3. **Python Swing 文件缺失** ⚠️ + - `i2i_swing_20251022.txt` 不存在 + - Python Swing算法可能执行失败 + +### 数据统计 + +``` +Redis总Key数: 229,710 + ├─ i2i:* (错误格式): 176,150 + ├─ item:similar:* (正确格式): 0 + └─ interest:* (正确格式): 53,560 ✓ + +C++ Swing输出: 15,177条记录 (未加载) +Interest数据: 53,560条 (已正确加载) ✓ +``` + +**结论**: `interest:*` 有数据是因为它使用了正确的key格式,但 `item:similar:*` 为空是因为数据被错误地存储到了 `i2i:*` 格式。 + +--- + +## 🔧 根本原因 + +### 代码问题定位 + +**文件**: `scripts/load_index_to_redis.py` + +**错误代码** (第100行): +```python +count = load_index_file( + file_path, + redis_client, + f"i2i:{i2i_type}", # ❌ 错误! + expire_seconds +) +``` + +**应该是**: +```python +count = load_index_file( + file_path, + redis_client, + f"item:similar:{i2i_type}", # ✓ 正确! + expire_seconds +) +``` + +**缺失功能**: 没有加载 C++ Swing 的函数 + +--- + +## ✅ 修复方案 + +### 已完成的修复 + +✅ **1. 修复了 `load_index_to_redis.py`** + - 修正了key前缀: `i2i:` → `item:similar:` + - 添加了 `load_cpp_swing_index()` 函数 + - 添加了 `i2i_item_behavior` 到加载列表 + +✅ **2. 创建了清理脚本 `fix_redis_keys.py`** + - 清理错误格式的 `i2i:*` key + - 提供统计信息和确认提示 + +### 执行步骤 + +#### 步骤1: 清理错误的Redis Key + +```bash +cd /home/tw/recommendation/offline_tasks + +# 运行清理脚本 +python3 scripts/fix_redis_keys.py +# 输入 'yes' 确认删除错误格式的key +``` + +#### 步骤2: 重新加载数据(使用output_1022的数据) + +```bash +# 方式1: 从output_1022目录加载指定日期的数据 +python3 scripts/load_index_to_redis.py \ + --date 20251022 \ + --redis-host localhost \ + --redis-port 6379 \ + --expire-days 7 + +# 注意:确保output目录指向output_1022,或者临时修改OUTPUT_DIR +``` + +**或者,更简单的方式**: + +```bash +# 创建软链接指向output_1022 +cd /home/tw/recommendation/offline_tasks +rm -rf output # 如果当前output是目录 +ln -s output_1022/output output + +# 然后正常加载 +python3 scripts/load_index_to_redis.py \ + --redis-host localhost \ + --redis-port 6379 \ + --date 20251022 +``` + +#### 步骤3: 验证修复结果 + +```bash +redis-cli + +# 检查key数量 +DBSIZE + +# 查看正确格式的key +KEYS item:similar:* + +# 查看C++ Swing的key(应该有15177个) +KEYS item:similar:swing_cpp:* + +# 查看其他i2i算法的key +KEYS item:similar:deepwalk:* +KEYS item:similar:content_name:* +KEYS item:similar:session_w2v:* +KEYS item:similar:item_behavior:* + +# 验证interest数据仍然存在 +KEYS interest:* + +# 测试查询具体的key +GET item:similar:swing_cpp:3600052 +GET interest:hot:platform:essabuy +``` + +**预期结果**: +``` +KEYS item:similar:* 返回数量: ~176,000+ (包括C++ Swing的15,177条) +KEYS item:similar:swing_cpp:* 返回数量: 15,177 +KEYS item:similar:deepwalk:* 返回数量: ~48,376 +KEYS item:similar:content_name:* 返回数量: ~127,720 +KEYS interest:* 返回数量: 53,560 (保持不变) +``` + +--- + +## 📊 修复后的数据结构 + +### 正确的Redis Key格式 + +``` +# i2i相似度索引 +item:similar:swing_cpp:{item_id} → C++ Swing结果 +item:similar:swing:{item_id} → Python Swing结果 +item:similar:session_w2v:{item_id} → Session W2V结果 +item:similar:deepwalk:{item_id} → DeepWalk结果 +item:similar:content_name:{item_id} → 内容相似度(名称) +item:similar:content_pic:{item_id} → 内容相似度(图片) +item:similar:item_behavior:{item_id} → Item行为相似度 + +# 兴趣点聚合索引 +interest:hot:{dimension_key} → 热门商品 +interest:cart:{dimension_key} → 加购商品 +interest:new:{dimension_key} → 新品 +interest:global:{dimension_key} → 全局热门 +``` + +### 查询示例 + +```python +import redis +import json + +r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) + +# 查询C++ Swing相似商品 +similar_items = json.loads(r.get('item:similar:swing_cpp:3600052')) +# 返回: [[2704531, 0.00431593], [2503886, 0.00431593], ...] + +# 查询平台热门商品 +hot_items = r.get('interest:hot:platform:essabuy') +# 返回: "3090653:70.0000,3509349:70.0000,..." +``` + +--- + +## 🚨 Python Swing 问题 + +### 问题描述 +`i2i_swing_20251022.txt` 文件不存在,说明Python Swing算法没有成功执行。 + +### 可能原因 +1. 算法执行失败(内存不足、超时等) +2. Session数据问题 +3. 参数配置问题 + +### 排查步骤 + +```bash +# 查看Python Swing的日志 +cat /home/tw/recommendation/offline_tasks/output_1022/logs/debug/i2i_swing*.log + +# 检查session文件 +ls -lh /home/tw/recommendation/offline_tasks/output_1022/output/session.txt.* + +# 手动运行Python Swing测试 +cd /home/tw/recommendation/offline_tasks +python3 scripts/i2i_swing.py --lookback_days 30 --top_n 10 --debug +``` + +### 临时解决方案 +由于C++ Swing已经生成且性能更好,可以暂时只使用C++ Swing的结果: +- C++ Swing: `item:similar:swing_cpp:*` (15,177条记录) +- 文档推荐在生产环境使用C++ Swing + +--- + +## 📝 后续改进建议 + +### 1. 代码改进 +- ✅ 已修复key格式问题 +- ✅ 已添加C++ Swing加载功能 +- 建议: 添加单元测试验证key格式 + +### 2. 文档更新 +- 更新 `Redis数据规范.md` 中的加载示例代码 +- 添加故障排查指南 +- 标注C++ Swing为生产推荐 + +### 3. 监控告警 +- 添加Redis key格式验证 +- 监控各类索引的数量 +- Python Swing执行失败时告警 + +### 4. 自动化测试 +```python +# 建议添加测试用例 +def test_redis_key_format(): + """测试Redis key格式是否正确""" + keys = redis_client.keys('item:similar:*') + assert len(keys) > 0, "item:similar:* should not be empty" + + # 确保没有错误格式的key + wrong_keys = redis_client.keys('i2i:*') + assert len(wrong_keys) == 0, f"Found wrong format keys: {wrong_keys[:5]}" +``` + +--- + +## 🎯 总结 + +### 问题根源 +加载脚本使用了错误的Redis key前缀 `i2i:*` 而不是文档定义的 `item:similar:*` + +### 解决方案 +1. ✅ 修复了 `load_index_to_redis.py` 的key前缀 +2. ✅ 添加了C++ Swing加载功能 +3. ✅ 创建了清理脚本清理错误的key +4. ⏳ 需要重新加载数据 + +### 后续行动 +1. 执行 `fix_redis_keys.py` 清理错误的key +2. 重新运行 `load_index_to_redis.py` 加载数据 +3. 验证所有 `item:similar:*` key都能正常查询 +4. 排查Python Swing执行失败的原因 + +--- + +**创建时间**: 2025-10-22 +**版本**: v1.0 +**状态**: ✅ 修复方案已就绪,等待执行 + diff --git a/offline_tasks/doc/详细设计文档.md b/offline_tasks/doc/详细设计文档.md index fcbdc91..82ca230 100644 --- a/offline_tasks/doc/详细设计文档.md +++ b/offline_tasks/doc/详细设计文档.md @@ -710,5 +710,3 @@ crontab -e **文档版本**: v1.1 **最后更新**: 2024-10-17 -**维护者**: 推荐系统团队 - diff --git a/offline_tasks/run.sh b/offline_tasks/run.sh index 32521c7..84c0e5b 100755 --- a/offline_tasks/run.sh +++ b/offline_tasks/run.sh @@ -10,6 +10,15 @@ cd /home/tw/recommendation/offline_tasks # mkdir output # mkdir logs +# 检测并激活conda环境 +if [ "$CONDA_DEFAULT_ENV" != "tw" ]; then + echo "当前环境不是 tw,正在激活 tw 环境..." + source /home/tw/miniconda3/etc/profile.d/conda.sh + conda activate tw + echo "已激活 tw 环境" +else + echo "当前已经在 tw 环境中,无需重复激活" +fi # ============================================================================ # 配置区域 diff --git a/offline_tasks/scripts/fix_redis_keys.py b/offline_tasks/scripts/fix_redis_keys.py new file mode 100644 index 0000000..46c02a6 --- /dev/null +++ b/offline_tasks/scripts/fix_redis_keys.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +修复Redis中的key格式问题 +将错误的 i2i:* 格式迁移到正确的 item:similar:* 格式 +""" +import redis +import sys + +def fix_redis_keys(): + """修复Redis key格式""" + + # 连接Redis + redis_client = redis.Redis( + host='localhost', + port=6379, + db=0, + decode_responses=True + ) + + try: + redis_client.ping() + print("✓ Redis连接成功") + except Exception as e: + print(f"✗ Redis连接失败: {e}") + return 1 + + # 统计信息 + print("\n" + "="*80) + print("当前Redis数据统计:") + print("="*80) + + total_keys = redis_client.dbsize() + print(f"总Key数量: {total_keys}") + + # 统计各类型key + i2i_keys = redis_client.keys('i2i:*') + item_similar_keys = redis_client.keys('item:similar:*') + interest_keys = redis_client.keys('interest:*') + + print(f"错误格式 i2i:* 数量: {len(i2i_keys)}") + print(f"正确格式 item:similar:* 数量: {len(item_similar_keys)}") + print(f"interest:* 数量: {len(interest_keys)}") + + # 询问是否删除错误的key + print("\n" + "="*80) + print("⚠️ 警告:即将删除所有 i2i:* 格式的key(错误格式)") + print("="*80) + + if i2i_keys: + print(f"\n将删除 {len(i2i_keys)} 个错误格式的key") + print("示例:") + for key in i2i_keys[:5]: + print(f" - {key}") + + response = input("\n确认删除?(yes/no): ") + if response.lower() == 'yes': + # 使用pipeline批量删除 + pipe = redis_client.pipeline() + for key in i2i_keys: + pipe.delete(key) + pipe.execute() + print(f"✓ 已删除 {len(i2i_keys)} 个错误格式的key") + else: + print("取消删除操作") + else: + print("✓ 没有需要删除的错误格式key") + + # 最终统计 + print("\n" + "="*80) + print("清理后统计:") + print("="*80) + total_keys_after = redis_client.dbsize() + print(f"总Key数量: {total_keys_after}") + print(f"item:similar:* 数量: {len(redis_client.keys('item:similar:*'))}") + print(f"interest:* 数量: {len(redis_client.keys('interest:*'))}") + + print("\n✓ 修复完成!现在可以使用修复后的加载脚本重新加载数据。") + + return 0 + + +if __name__ == '__main__': + sys.exit(fix_redis_keys()) + diff --git a/offline_tasks/scripts/load_index_to_redis.py b/offline_tasks/scripts/load_index_to_redis.py index e7756c0..34b3810 100644 --- a/offline_tasks/scripts/load_index_to_redis.py +++ b/offline_tasks/scripts/load_index_to_redis.py @@ -69,6 +69,37 @@ def load_index_file(file_path, redis_client, key_prefix, expire_seconds=None): return count +def load_cpp_swing_index(redis_client, expire_days=7): + """ + 加载C++ Swing相似度索引 + + Args: + redis_client: Redis客户端 + expire_days: 过期天数 + + Returns: + 加载的记录数 + """ + # C++ Swing输出文件 + file_path = os.path.join(os.path.dirname(OUTPUT_DIR), 'collaboration', 'output', 'swing_similar.txt') + + if not os.path.exists(file_path): + logger.warning(f"C++ Swing file not found: {file_path}, skipping...") + return 0 + + expire_seconds = expire_days * 24 * 3600 if expire_days else None + + logger.info(f"Loading C++ Swing indices from {file_path}...") + count = load_index_file( + file_path, + redis_client, + "item:similar:swing_cpp", + expire_seconds + ) + logger.info(f"Loaded {count} C++ Swing indices") + return count + + def load_i2i_indices(redis_client, date_str=None, expire_days=7): """ 加载i2i相似度索引 @@ -84,7 +115,7 @@ def load_i2i_indices(redis_client, date_str=None, expire_days=7): expire_seconds = expire_days * 24 * 3600 if expire_days else None # i2i索引类型 - i2i_types = ['swing', 'session_w2v', 'deepwalk', 'content_name', 'content_pic'] + i2i_types = ['swing', 'session_w2v', 'deepwalk', 'content_name', 'content_pic', 'item_behavior'] for i2i_type in i2i_types: file_path = os.path.join(OUTPUT_DIR, f'i2i_{i2i_type}_{date_str}.txt') @@ -97,7 +128,7 @@ def load_i2i_indices(redis_client, date_str=None, expire_days=7): count = load_index_file( file_path, redis_client, - f"i2i:{i2i_type}", + f"item:similar:{i2i_type}", # 修复: 使用正确的key前缀 expire_seconds ) logger.info(f"Loaded {count} {i2i_type} indices") @@ -184,6 +215,13 @@ def main(): redis_client.flushdb() logger.info("Database flushed") + # 加载C++ Swing索引 + if args.load_i2i: + logger.info("\n" + "="*80) + logger.info("Loading C++ Swing indices") + logger.info("="*80) + load_cpp_swing_index(redis_client, args.expire_days) + # 加载i2i索引 if args.load_i2i: logger.info("\n" + "="*80) -- libgit2 0.21.2