-- 钱包开卡表抢卡死锁治理索引。 -- -- 适用接口: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;