perf(etl): 优化批量更新与批处理大小
- 将默认批处理大小 BATCH_SIZE 从 1 增大到 1000 提高效率 - 添加 BACKFILL_BATCH_SIZE 环境变量支持,默认为 5000 用于补数据批量控制 - 使用 SQL 的 VALUES 语法替换 executemany 实现批量更新 yinyan_song_records 平台字段 - 修改 runner 中补数据查询改用 BACKFILL_BATCH_SIZE 以提升性能 - 更新相关单元测试以适配新的批量更新 SQL 语句和参数格式
Showing
4 changed files
with
23 additions
and
15 deletions
| ... | @@ -43,4 +43,5 @@ PLATFORM_KUGOU = '2' | ... | @@ -43,4 +43,5 @@ PLATFORM_KUGOU = '2' |
| 43 | PLATFORM_NETEASE = '4' | 43 | PLATFORM_NETEASE = '4' |
| 44 | PLATFORMS = [PLATFORM_QQ, PLATFORM_KUGOU, PLATFORM_NETEASE] | 44 | PLATFORMS = [PLATFORM_QQ, PLATFORM_KUGOU, PLATFORM_NETEASE] |
| 45 | 45 | ||
| 46 | BATCH_SIZE = 1 | 46 | BATCH_SIZE = 1000 |
| 47 | BACKFILL_BATCH_SIZE = int(os.environ.get('BACKFILL_BATCH_SIZE', '5000')) | ... | ... |
| ... | @@ -3,7 +3,7 @@ import json | ... | @@ -3,7 +3,7 @@ import json |
| 3 | import logging | 3 | import logging |
| 4 | from tqdm import tqdm | 4 | from tqdm import tqdm |
| 5 | 5 | ||
| 6 | from .config import PLATFORM_QQ, PLATFORM_KUGOU, PLATFORM_NETEASE, BATCH_SIZE, OSS_CONFIG | 6 | from .config import PLATFORM_QQ, PLATFORM_KUGOU, PLATFORM_NETEASE, BATCH_SIZE, BACKFILL_BATCH_SIZE, OSS_CONFIG |
| 7 | from .connections import get_hk_songs_conn, get_source_conn, get_spider_conn, get_pg_conn, get_oss_bucket | 7 | from .connections import get_hk_songs_conn, get_source_conn, get_spider_conn, get_pg_conn, get_oss_bucket |
| 8 | from .reader import ( | 8 | from .reader import ( |
| 9 | iter_hk_songs_batches, | 9 | iter_hk_songs_batches, |
| ... | @@ -548,7 +548,7 @@ def backfill_yinyan_record_platforms(max_batches: int | None = None) -> None: | ... | @@ -548,7 +548,7 @@ def backfill_yinyan_record_platforms(max_batches: int | None = None) -> None: |
| 548 | pbar = tqdm(desc='backfill-yinyan-platforms') | 548 | pbar = tqdm(desc='backfill-yinyan-platforms') |
| 549 | while max_batches is None or batch_index < max_batches: | 549 | while max_batches is None or batch_index < max_batches: |
| 550 | with pg_conn.cursor() as pg_cur: | 550 | with pg_conn.cursor() as pg_cur: |
| 551 | rows = fetch_yinyan_records_missing_platform(pg_cur, BATCH_SIZE) | 551 | rows = fetch_yinyan_records_missing_platform(pg_cur, BACKFILL_BATCH_SIZE) |
| 552 | if not rows: | 552 | if not rows: |
| 553 | break | 553 | break |
| 554 | 554 | ... | ... |
| ... | @@ -56,15 +56,20 @@ def fetch_yinyan_records_missing_platform(cur, limit: int) -> list[dict]: | ... | @@ -56,15 +56,20 @@ def fetch_yinyan_records_missing_platform(cur, limit: int) -> list[dict]: |
| 56 | def update_yinyan_record_platforms(cur, records: list[dict]) -> None: | 56 | def update_yinyan_record_platforms(cur, records: list[dict]) -> None: |
| 57 | if not records: | 57 | if not records: |
| 58 | return | 58 | return |
| 59 | cur.executemany( | 59 | values_sql = ', '.join(['(%s::bigint, %s::bigint, %s::varchar)'] * len(records)) |
| 60 | """ | 60 | params = [] |
| 61 | UPDATE yinyan_song_records | 61 | for r in records: |
| 62 | SET platform = %s | 62 | params.extend([r['song_id'], r['record_id'], r['platform']]) |
| 63 | WHERE song_id = %s | 63 | cur.execute( |
| 64 | AND record_id = %s | 64 | f""" |
| 65 | AND platform IS NULL | 65 | UPDATE yinyan_song_records AS ysr |
| 66 | SET platform = v.platform | ||
| 67 | FROM (VALUES {values_sql}) AS v(song_id, record_id, platform) | ||
| 68 | WHERE ysr.song_id = v.song_id | ||
| 69 | AND ysr.record_id = v.record_id | ||
| 70 | AND ysr.platform IS NULL | ||
| 66 | """, | 71 | """, |
| 67 | [(r['platform'], r['song_id'], r['record_id']) for r in records], | 72 | tuple(params), |
| 68 | ) | 73 | ) |
| 69 | 74 | ||
| 70 | 75 | ... | ... |
| ... | @@ -97,10 +97,12 @@ def test_update_yinyan_record_platforms_fills_only_null_platform_rows(): | ... | @@ -97,10 +97,12 @@ def test_update_yinyan_record_platforms_fills_only_null_platform_rows(): |
| 97 | {'song_id': 11, 'record_id': 101, 'platform': '2'}, | 97 | {'song_id': 11, 'record_id': 101, 'platform': '2'}, |
| 98 | ]) | 98 | ]) |
| 99 | 99 | ||
| 100 | sql, rows = cur.executemany.call_args[0] | 100 | sql, params = cur.execute.call_args[0] |
| 101 | assert 'SET platform = %s' in sql | 101 | assert 'UPDATE yinyan_song_records AS ysr' in sql |
| 102 | assert 'AND platform IS NULL' in sql | 102 | assert 'FROM (VALUES (%s::bigint, %s::bigint, %s::varchar), (%s::bigint, %s::bigint, %s::varchar))' in sql |
| 103 | assert rows == [('1', 10, 100), ('2', 11, 101)] | 103 | assert 'SET platform = v.platform' in sql |
| 104 | assert 'AND ysr.platform IS NULL' in sql | ||
| 105 | assert params == (10, 100, '1', 11, 101, '2') | ||
| 104 | 106 | ||
| 105 | 107 | ||
| 106 | def test_upsert_netease_songs_writes_album_json_column(): | 108 | def test_upsert_netease_songs_writes_album_json_column(): | ... | ... |
-
Please register or sign in to post a comment