fix: 治理线上 MySQL CPU 波动的两个热点查询
- backfill-last7 的报表用户集合改从 wallet_accounts(7千行)取, 替代每10分钟对千万级 wallet_entries 做资产全量 DISTINCT 扫描 (线上等价性已核验:旧集合 4907 人 ⊆ 新集合 4908 人) - lucky_gift_outbox 补两个无 app_code 前缀的 claim 索引, 消除 worker 每秒空轮询的全索引扫描+filesort(线上已手工应用) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
68fc43d4b2
commit
959ce54750
@ -47,9 +47,32 @@ CREATE TABLE IF NOT EXISTS lucky_gift_outbox (
|
||||
KEY idx_lucky_gift_outbox_status_retry (app_code, status, next_retry_at_ms),
|
||||
KEY idx_lucky_gift_outbox_lucky_retry (app_code, event_type, status, next_retry_at_ms, created_at_ms, outbox_id),
|
||||
KEY idx_lucky_gift_outbox_lucky_lock (app_code, event_type, status, lock_until_ms, created_at_ms, outbox_id),
|
||||
KEY idx_lucky_gift_outbox_retention (app_code, status, updated_at_ms, outbox_id)
|
||||
KEY idx_lucky_gift_outbox_retention (app_code, status, updated_at_ms, outbox_id),
|
||||
KEY idx_lucky_gift_outbox_claim_retry (event_type, status, next_retry_at_ms, created_at_ms, outbox_id),
|
||||
KEY idx_lucky_gift_outbox_claim_lock (event_type, status, lock_until_ms, created_at_ms, outbox_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物 owner outbox 表';
|
||||
|
||||
-- Worker 的 outbox 抢占 SQL 不带 app_code(跨 App 统一补偿),老索引都以 app_code 开头无法命中,
|
||||
-- 每次轮询(默认 1 秒 × 空轮询最多 6 条查询)都会全索引扫描并 filesort 整表。
|
||||
-- 这两个无 app_code 前缀的索引让空轮询变成零行索引定位,是线上 MySQL 基线 CPU 的主要治理点。
|
||||
SET @ddl := IF(
|
||||
(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lucky_gift_outbox' AND INDEX_NAME = 'idx_lucky_gift_outbox_claim_retry') = 0,
|
||||
'ALTER TABLE lucky_gift_outbox ADD INDEX idx_lucky_gift_outbox_claim_retry (event_type, status, next_retry_at_ms, created_at_ms, outbox_id), ALGORITHM=INPLACE, LOCK=NONE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl := IF(
|
||||
(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lucky_gift_outbox' AND INDEX_NAME = 'idx_lucky_gift_outbox_claim_lock') = 0,
|
||||
'ALTER TABLE lucky_gift_outbox ADD INDEX idx_lucky_gift_outbox_claim_lock (event_type, status, lock_until_ms, created_at_ms, outbox_id), ALGORITHM=INPLACE, LOCK=NONE',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lucky_gift_rule_versions (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID',
|
||||
|
||||
@ -352,12 +352,15 @@ func collectReportMetricUserIDs(ctx context.Context, walletDB *sql.DB, userDB *s
|
||||
}{
|
||||
{
|
||||
db: walletDB,
|
||||
// 历史持有人集合从 wallet_accounts 取:账户行在首笔流水时创建且从不删除,
|
||||
// 与「wallet_entries 中出现过该资产」等价(提前开户的账户是无害超集,只影响维度预取)。
|
||||
// 千万级 wallet_entries 上按资产全量 DISTINCT 扫描曾是线上 MySQL 每 10 分钟的 CPU 尖峰主因。
|
||||
query: `
|
||||
SELECT DISTINCT e.user_id
|
||||
FROM wallet_entries e
|
||||
WHERE e.app_code = ? AND e.asset_type IN ('COIN', 'COIN_SELLER_COIN', 'HOST_SALARY_USD', 'AGENCY_SALARY_USD') AND e.created_at_ms < ?
|
||||
SELECT user_id
|
||||
FROM wallet_accounts
|
||||
WHERE app_code = ? AND asset_type IN ('COIN', 'COIN_SELLER_COIN', 'HOST_SALARY_USD', 'AGENCY_SALARY_USD') AND created_at_ms < ?
|
||||
UNION
|
||||
SELECT DISTINCT e.counterparty_user_id
|
||||
SELECT e.counterparty_user_id
|
||||
FROM wallet_entries e
|
||||
JOIN wallet_transactions wt ON wt.app_code = e.app_code AND wt.transaction_id = e.transaction_id
|
||||
WHERE e.app_code = ? AND wt.biz_type = 'salary_transfer_to_coin_seller'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user