-- ============================================ -- 压力测试数据生成脚本 -- 用于生成大量测试数据以进行性能测试 -- ============================================ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- 清理测试数据(可选,谨慎使用) -- DELETE FROM wallet_ledger WHERE user_id > 1000; -- DELETE FROM rental_orders WHERE id > 100; -- DELETE FROM rental_listings WHERE id > 100; -- DELETE FROM game_accounts WHERE id > 100; -- DELETE FROM users WHERE id > 1000; -- ============================================ -- 1. 批量生成用户数据(10000个用户) -- ============================================ DROP PROCEDURE IF EXISTS generate_users; DELIMITER $$ CREATE PROCEDURE generate_users(IN batch_size INT) BEGIN DECLARE i INT DEFAULT 1; DECLARE phone_num VARCHAR(32); DECLARE nick VARCHAR(64); WHILE i <= batch_size DO SET phone_num = CONCAT('138', LPAD(i, 8, '0')); SET nick = CONCAT('测试用户', i); INSERT INTO users ( phone, nickname, realname_status, risk_status, credit_score, status, created_at ) VALUES ( phone_num, nick, CASE WHEN i % 10 = 0 THEN 'unverified' ELSE 'verified' END, CASE WHEN i % 100 = 0 THEN 'frozen' ELSE 'normal' END, 80 + (i % 20), 'active', DATE_SUB(NOW(), INTERVAL (i % 365) DAY) ) ON DUPLICATE KEY UPDATE id=id; -- 为已实名用户创建实名记录 IF i % 10 != 0 THEN INSERT INTO user_realname ( user_id, provider, status, masked_name, masked_id_no, verified_at ) VALUES ( LAST_INSERT_ID(), 'mock', 'success', CONCAT('张*', CHAR(65 + (i % 26))), CONCAT('3301**********', LPAD(i % 10000, 4, '0')), DATE_SUB(NOW(), INTERVAL (i % 300) DAY) ) ON DUPLICATE KEY UPDATE user_id=user_id; END IF; -- 为用户创建钱包 INSERT INTO wallet_accounts (user_id, available_balance_cent, frozen_balance_cent, status) VALUES ( LAST_INSERT_ID(), (i % 10) * 10000, (i % 5) * 5000, 'active' ) ON DUPLICATE KEY UPDATE user_id=user_id; SET i = i + 1; -- 每1000条提交一次 IF i % 1000 = 0 THEN COMMIT; END IF; END WHILE; COMMIT; END$$ DELIMITER ; -- ============================================ -- 2. 批量生成游戏账号和商品(50000个商品) -- ============================================ DROP PROCEDURE IF EXISTS generate_listings; DELIMITER $$ CREATE PROCEDURE generate_listings(IN batch_size INT) BEGIN DECLARE i INT DEFAULT 1; DECLARE user_id_val BIGINT; DECLARE account_id_val BIGINT; DECLARE price_val BIGINT; DECLARE deposit_val BIGINT; DECLARE verified_user_min BIGINT; DECLARE verified_user_max BIGINT; DECLARE user_pick BIGINT; SELECT MIN(id), MAX(id) INTO verified_user_min, verified_user_max FROM users WHERE realname_status = 'verified'; IF verified_user_min IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '没有可用的已实名用户'; END IF; WHILE i <= batch_size DO -- 近似随机选择一个用户作为号主,避免 ORDER BY RAND() 全表排序。 SET user_id_val = NULL; SET user_pick = verified_user_min + FLOOR(RAND() * (verified_user_max - verified_user_min + 1)); SELECT id INTO user_id_val FROM users WHERE id >= user_pick AND realname_status = 'verified' ORDER BY id LIMIT 1; IF user_id_val IS NULL THEN SELECT id INTO user_id_val FROM users WHERE realname_status = 'verified' ORDER BY id LIMIT 1; END IF; -- 创建游戏账号 INSERT INTO game_accounts ( owner_id, game_name, server_region, login_platform, title, description, rank_level, haf_coin_amount, status ) VALUES ( user_id_val, 'delta_force', CASE (i % 4) WHEN 0 THEN '亚服' WHEN 1 THEN '美服' WHEN 2 THEN '欧服' ELSE '国服' END, CASE (i % 3) WHEN 0 THEN 'Steam' WHEN 1 THEN 'Epic' ELSE 'WeGame' END, CONCAT('账号', i, ' 高分段'), CONCAT('这是一个测试账号,编号', i), CASE (i % 5) WHEN 0 THEN '青铜' WHEN 1 THEN '白银' WHEN 2 THEN '黄金' WHEN 3 THEN '铂金' ELSE '钻石' END, (i % 100) * 10000, 'published' ); SET account_id_val = LAST_INSERT_ID(); -- 创建租号商品 SET price_val = 500 + (i % 20) * 50; SET deposit_val = 10000 + (i % 10) * 5000; INSERT INTO rental_listings ( listing_no, account_id, owner_id, price_cent, deposit_amount_cent, in_transaction, status, review_status, published_at ) VALUES ( CONCAT( DATE_FORMAT(DATE_SUB(NOW(), INTERVAL (i % 90) DAY), '%Y%m%d'), LPAD(FLOOR((i - 1) / 90) + 1, 4, '0') ), account_id_val, user_id_val, price_val, deposit_val, CASE WHEN i % 10 = 0 THEN 1 ELSE 0 END, CASE WHEN i % 20 = 0 THEN 'offline' WHEN i % 15 = 0 THEN 'draft' ELSE 'published' END, CASE WHEN i % 15 = 0 THEN 'pending' WHEN i % 30 = 0 THEN 'rejected' ELSE 'approved' END, DATE_SUB(NOW(), INTERVAL (i % 90) DAY) ); SET i = i + 1; IF i % 1000 = 0 THEN COMMIT; END IF; END WHILE; INSERT INTO listing_no_sequences (biz_date, next_seq) SELECT LEFT(listing_no, 8), MAX(CAST(SUBSTRING(listing_no, 9) AS UNSIGNED)) FROM rental_listings GROUP BY LEFT(listing_no, 8) ON DUPLICATE KEY UPDATE next_seq = GREATEST(next_seq, VALUES(next_seq)); COMMIT; END$$ DELIMITER ; -- ============================================ -- 3. 批量生成订单数据(30000个订单) -- ============================================ DROP PROCEDURE IF EXISTS generate_orders; DELIMITER $$ CREATE PROCEDURE generate_orders(IN batch_size INT) BEGIN DECLARE i INT DEFAULT 1; DECLARE listing_id_val BIGINT; DECLARE account_id_val BIGINT; DECLARE owner_id_val BIGINT; DECLARE renter_id_val BIGINT; DECLARE order_no_val VARCHAR(64); DECLARE rent_amount_val BIGINT; DECLARE deposit_val BIGINT; DECLARE listing_min BIGINT; DECLARE listing_max BIGINT; DECLARE listing_pick BIGINT; DECLARE verified_user_min BIGINT; DECLARE verified_user_max BIGINT; DECLARE user_pick BIGINT; SELECT MIN(id), MAX(id) INTO listing_min, listing_max FROM rental_listings WHERE status IN ('published', 'active') AND review_status = 'approved'; SELECT MIN(id), MAX(id) INTO verified_user_min, verified_user_max FROM users WHERE realname_status = 'verified'; IF listing_min IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '没有可用的已上架商品'; END IF; IF verified_user_min IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '没有可用的已实名用户'; END IF; WHILE i <= batch_size DO -- 近似随机选择一个上架商品,避免 ORDER BY RAND() 全表排序。 SET listing_id_val = NULL; SET listing_pick = listing_min + FLOOR(RAND() * (listing_max - listing_min + 1)); SELECT rl.id, rl.account_id, rl.owner_id, rl.price_cent, rl.deposit_amount_cent INTO listing_id_val, account_id_val, owner_id_val, rent_amount_val, deposit_val FROM rental_listings rl WHERE rl.id >= listing_pick AND rl.status IN ('published', 'active') AND rl.review_status = 'approved' ORDER BY rl.id LIMIT 1; IF listing_id_val IS NULL THEN SELECT rl.id, rl.account_id, rl.owner_id, rl.price_cent, rl.deposit_amount_cent INTO listing_id_val, account_id_val, owner_id_val, rent_amount_val, deposit_val FROM rental_listings rl WHERE rl.status IN ('published', 'active') AND rl.review_status = 'approved' ORDER BY rl.id LIMIT 1; END IF; -- 近似随机选择一个租客(不能是号主本人)。 SET renter_id_val = NULL; SET user_pick = verified_user_min + FLOOR(RAND() * (verified_user_max - verified_user_min + 1)); SELECT id INTO renter_id_val FROM users WHERE id >= user_pick AND id != owner_id_val AND realname_status = 'verified' ORDER BY id LIMIT 1; IF renter_id_val IS NULL THEN SELECT id INTO renter_id_val FROM users WHERE id != owner_id_val AND realname_status = 'verified' ORDER BY id LIMIT 1; END IF; IF renter_id_val IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '没有可用的租客用户'; END IF; SET order_no_val = CONCAT('ORD', DATE_FORMAT(NOW(), '%Y%m%d'), LPAD(i, 8, '0')); SET rent_amount_val = rent_amount_val * 24; -- 24小时租金 INSERT INTO rental_orders ( order_no, listing_id, account_id, owner_id, renter_id, estimated_duration_hours, rent_amount_cent, owner_rent_amount_cent, deposit_amount_cent, deposit_original_amount_cent, platform_fee_cent, status, handoff_status, settlement_status, rented_at, created_at ) VALUES ( order_no_val, listing_id_val, account_id_val, owner_id_val, renter_id_val, 24, rent_amount_val, ROUND(rent_amount_val * 0.95), -- 号主实得95% deposit_val, deposit_val, ROUND(rent_amount_val * 0.05), -- 平台5%手续费 CASE (i % 10) WHEN 0 THEN 'pending_payment' WHEN 1 THEN 'cancelled' WHEN 2 THEN 'closed' ELSE 'completed' END, CASE (i % 10) WHEN 0 THEN 'none' WHEN 1 THEN 'none' WHEN 2 THEN 'owner_delivered' ELSE 'owner_received' END, CASE (i % 10) WHEN 0 THEN 'unsettled' WHEN 1 THEN 'unsettled' ELSE 'settled' END, DATE_SUB(NOW(), INTERVAL (i % 60) DAY), DATE_SUB(NOW(), INTERVAL (i % 60) DAY) ); SET i = i + 1; IF i % 1000 = 0 THEN COMMIT; END IF; END WHILE; COMMIT; END$$ DELIMITER ; -- ============================================ -- 4. 批量生成钱包流水(100000条流水) -- ============================================ DROP PROCEDURE IF EXISTS generate_wallet_ledger; DELIMITER $$ CREATE PROCEDURE generate_wallet_ledger(IN batch_size INT) BEGIN DECLARE i INT DEFAULT 1; DECLARE user_id_val BIGINT; DECLARE order_id_val BIGINT; DECLARE ledger_no_val VARCHAR(64); DECLARE amount_val BIGINT; DECLARE user_min BIGINT; DECLARE user_max BIGINT; DECLARE order_min BIGINT; DECLARE order_max BIGINT; DECLARE user_pick BIGINT; DECLARE order_pick BIGINT; SELECT MIN(id), MAX(id) INTO user_min, user_max FROM users; SELECT MIN(id), MAX(id) INTO order_min, order_max FROM rental_orders; IF user_min IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '没有可用的用户'; END IF; WHILE i <= batch_size DO -- 近似随机选择用户,避免 ORDER BY RAND() 全表排序。 SET user_id_val = NULL; SET user_pick = user_min + FLOOR(RAND() * (user_max - user_min + 1)); SELECT id INTO user_id_val FROM users WHERE id >= user_pick ORDER BY id LIMIT 1; IF user_id_val IS NULL THEN SELECT id INTO user_id_val FROM users ORDER BY id LIMIT 1; END IF; -- 随机关联订单(50%概率) IF RAND() > 0.5 AND order_min IS NOT NULL THEN SET order_id_val = NULL; SET order_pick = order_min + FLOOR(RAND() * (order_max - order_min + 1)); SELECT id INTO order_id_val FROM rental_orders WHERE id >= order_pick ORDER BY id LIMIT 1; IF order_id_val IS NULL THEN SELECT id INTO order_id_val FROM rental_orders ORDER BY id LIMIT 1; END IF; ELSE SET order_id_val = NULL; END IF; SET ledger_no_val = CONCAT('LDG', DATE_FORMAT(NOW(), '%Y%m%d%H%i%s'), LPAD(i, 6, '0')); SET amount_val = ((i % 500) * 100) + FLOOR(RAND() * 10000); INSERT INTO wallet_ledger ( ledger_no, user_id, order_id, direction, amount_cent, balance_after_cent, balance_type, biz_type, biz_no, remark, created_at ) VALUES ( ledger_no_val, user_id_val, order_id_val, CASE WHEN i % 2 = 0 THEN 'in' ELSE 'out' END, amount_val, 100000 + (i % 1000) * 100, CASE WHEN i % 5 = 0 THEN 'frozen' ELSE 'available' END, CASE (i % 6) WHEN 0 THEN 'rent_payment' WHEN 1 THEN 'deposit_freeze' WHEN 2 THEN 'settlement' WHEN 3 THEN 'refund' WHEN 4 THEN 'recharge' ELSE 'withdraw' END, CONCAT('BIZ', LPAD(i, 10, '0')), CONCAT('测试流水', i), DATE_SUB(NOW(), INTERVAL (i % 180) DAY) ); SET i = i + 1; IF i % 1000 = 0 THEN COMMIT; END IF; END WHILE; COMMIT; END$$ DELIMITER ; -- ============================================ -- 5. 批量生成聊天消息(50000条消息) -- ============================================ DROP PROCEDURE IF EXISTS generate_chat_messages; DELIMITER $$ CREATE PROCEDURE generate_chat_messages(IN batch_size INT) BEGIN DECLARE i INT DEFAULT 1; DECLARE conv_id BIGINT; DECLARE user_id_val BIGINT; DECLARE conv_min BIGINT; DECLARE conv_max BIGINT; DECLARE user_min BIGINT; DECLARE user_max BIGINT; DECLARE conv_pick BIGINT; DECLARE user_pick BIGINT; SELECT MIN(id), MAX(id) INTO conv_min, conv_max FROM chat_conversations; SELECT MIN(id), MAX(id) INTO user_min, user_max FROM users; IF conv_min IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '没有可用的聊天会话'; END IF; IF user_min IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '没有可用的用户'; END IF; WHILE i <= batch_size DO -- 近似随机选择一个会话和发送者,避免 ORDER BY RAND() 全表排序。 SET conv_id = NULL; SET conv_pick = conv_min + FLOOR(RAND() * (conv_max - conv_min + 1)); SELECT id INTO conv_id FROM chat_conversations WHERE id >= conv_pick ORDER BY id LIMIT 1; IF conv_id IS NULL THEN SELECT id INTO conv_id FROM chat_conversations ORDER BY id LIMIT 1; END IF; IF conv_id IS NOT NULL THEN SET user_id_val = NULL; SET user_pick = user_min + FLOOR(RAND() * (user_max - user_min + 1)); SELECT id INTO user_id_val FROM users WHERE id >= user_pick ORDER BY id LIMIT 1; IF user_id_val IS NULL THEN SELECT id INTO user_id_val FROM users ORDER BY id LIMIT 1; END IF; INSERT INTO chat_messages ( conversation_id, sender_type, sender_id, sender_role, content_type, content, created_at ) VALUES ( conv_id, 'user', user_id_val, CASE WHEN i % 2 = 0 THEN 'owner' ELSE 'renter' END, 'text', CONCAT('这是测试消息', i, ',内容随机生成用于压力测试'), DATE_SUB(NOW(), INTERVAL (i % 30) DAY) ); END IF; SET i = i + 1; IF i % 1000 = 0 THEN COMMIT; END IF; END WHILE; COMMIT; END$$ DELIMITER ; -- ============================================ -- 本文件只创建存储过程,不直接生成数据。 -- 请使用 scripts/stress_test.sh data 按参数生成,避免意外重复造数。 -- ============================================ SET FOREIGN_KEY_CHECKS = 1;