41 lines
2.3 KiB
SQL
41 lines
2.3 KiB
SQL
-- 钱包开卡表抢卡死锁治理索引。
|
||
--
|
||
-- 适用接口:GET /wallet-manage/create-wallet(用户批量导入触发的并发创建钱包+自动绑卡)等 createAndActiveCard 路径。
|
||
-- 背景:抢卡 SQL 按 merchant_id = ? + user_id IS NULL 过滤并 ORDER BY card_sort LIMIT 1 FOR UPDATE;
|
||
-- 原表仅有主键和 (user_id) 单列索引,锁定读需经索引扫描全部空闲卡再 filesort,
|
||
-- 实际锁范围覆盖全表空闲卡直到事务提交,并与自动补卡的批量 INSERT 插入意向锁互相等待,
|
||
-- 大批量用户导入并发建钱包时产生集中死锁。
|
||
-- 索引效果:范围收敛为单商户空闲卡区间且天然按 card_sort 有序,FOR UPDATE 免排序、取满 LIMIT 1 即停止,
|
||
-- 每笔事务仅锁定队首一张卡;同时加速绑卡前的空闲卡余量 COUNT。
|
||
-- 执行影响:仅新增普通索引,不修改字段、约束和历史数据;建索引期间消耗 IO,建议业务低峰期执行。
|
||
-- 执行前建议:先备份数据库,并用文末查询核对生产库 wallet_active 实际已有索引。
|
||
|
||
SET @index_ddl = IF(
|
||
EXISTS (
|
||
SELECT 1
|
||
FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'wallet_active'
|
||
AND index_name = 'idx_wallet_active_merchant_user_sort'
|
||
),
|
||
'SELECT ''idx_wallet_active_merchant_user_sort already exists'' AS message',
|
||
'ALTER TABLE `wallet_active` ADD INDEX `idx_wallet_active_merchant_user_sort` (`merchant_id`, `user_id`, `card_sort`)'
|
||
);
|
||
PREPARE index_stmt FROM @index_ddl;
|
||
EXECUTE index_stmt;
|
||
DEALLOCATE PREPARE index_stmt;
|
||
|
||
-- 执行后核对新增索引;确认与既有 idx_dept_id(user_id) 无同前缀冗余。
|
||
SELECT table_name, index_name, seq_in_index, column_name
|
||
FROM information_schema.statistics
|
||
WHERE table_schema = DATABASE()
|
||
AND table_name = 'wallet_active'
|
||
ORDER BY table_name, index_name, seq_in_index;
|
||
|
||
-- 建议执行后更新统计信息,并用真实商户参数验证抢卡 SQL 计划不再 filesort:
|
||
-- ANALYZE TABLE wallet_active;
|
||
-- EXPLAIN SELECT id, ... FROM wallet_active
|
||
-- WHERE del_flag = '0' AND user_id IS NULL AND wallet_id IS NULL
|
||
-- AND merchant_id = ? AND is_active_hardware = '0' AND tenant_id = ?
|
||
-- ORDER BY card_sort ASC LIMIT 1 FOR UPDATE;
|