资讯动态

数据库测试数据批量造数实战:SQL脚本、性能优化与常见坑

发布时间:2026/10/14 16:01:43 来源:尧图企业网站定制
1. 为什么造测试数据这件事值得认真对待先说个常见场景开发完一个分页查询接口本地库里只有几十条数据随便点两下看着挺正常。等部署到测试环境DBA导了一份生产脱敏数据进来接口直接超时SQL执行计划走了全表扫描。这时候你才发现原来当初建的那个索引根本没生效或者查询条件里字段类型对不上隐式转换把索引废了。问题根源不在于代码写错而在于你从来没有在“像样的数据量”上验证过。这就是批量制造测试模拟数据的价值。它解决的核心问题有三个第一让你的SQL、索引、查询计划在接近真实的数据分布下接受检验第二让压力测试、并发测试有足够的样本量而不是拿几十行数据自欺欺人第三让前端分页、导出、统计报表这些功能在数据量上来之后依然表现稳定。说白了造数据不是糊弄事而是把测试环境的数据基础打牢让问题提前暴露在测试阶段而不是等上线后被用户发现。我见过不少团队用三种方式解决这个问题一是写存储过程循环插入二是用Excel手工拉数据再导入三是找DBA要生产库脱敏快照。第一种在小数据量下能用一到百万级就慢得让人抓狂第二种只适合几十上百条的场景数据量一大根本不现实第三种效果最好但脱敏流程繁琐、审批链路长普通项目根本等不起。所以自己动手用SQL脚本批量造数据反而是最灵活、最快见效的方案。这篇文章面向的读者很明确后端开发、测试工程师、DBA以及所有需要自己准备测试数据的人。我会用PG、MySQL、SqlServer三种数据库分别演示一套完整的造数据方案从基础表结构到百万级数据量把脚本写法、性能调优、常见坑一次讲透。2. 造数之前必须想清楚的几件事2.1 别急着写脚本先理解你的表结构和约束很多人拿到建表语句就开始写INSERT结果插入到一半碰到外键约束报错或者唯一索引冲突直接中断。我建议先花十分钟做一件事把目标表的所有约束列出来分清哪些是NOT NULL、哪些是UNIQUE、哪些是外键、哪些有CHECK约束、哪些字段有默认值。这一步看起来简单实际上决定了你后面脚本怎么写。举个例子一张用户表有两个唯一约束一个是user_name一个是email。如果你用随机字符串去填充冲突概率其实不小尤其在数据量过十万之后碰撞几乎是必然的。这时候就需要在生成逻辑里做去重处理比如用序列号拼接固定前缀的方式保证唯一性而不是单纯依赖随机。另一个典型坑是外键关联订单表的user_id必须存在于用户表中如果你先插订单后插用户或者两个表的数据量对不上就会一直报错。正确的做法是先插主表再插从表并且从表的外键值要从主表中真实存在的ID范围里取。还有一个容易被忽略的细节是字段长度和字符集。varchar(20)的字段你塞进去一个32位字符串数据库不会报错但数据会被截断后续查询结果看起来就很怪。我建议在造数之前先跑一条DESCRIBE或者查看information_schema把所有字段类型、长度过一遍。尤其是中文场景下utf8mb4和utf8的字符占用不一样字段长度不够时中文直接变成问号这种问题排查起来特别费劲。2.2 数据分布比数据量更重要造数不是把随机值往表里塞就完事了更重要的是让数据分布接近真实场景。你想象一下真实业务里的订单表金额字段大概率的分布是大量小额、少量大额状态字段是大部分已完成、少量待支付和退款。如果你把订单金额做成纯随机均匀分布那查出来的数据特征就跟线上完全不一样测试结论也没有参考价值。所以我在造数时会刻意控制生成规则。最简单的做法是给数据加权重比如用CASE WHEN配合随机数实现按比例分布。拿订单状态举例我可以让70%的数据是“已完成”20%是“待支付”10%是“已取消”。这样后续你测试“待支付订单列表”这个功能时查出来的数据量和真实业务就是同数量级的不会出现全表都是待支付这种不合理的场景。同理时间字段也要注意别全部集中在同一秒可以用随机偏移把时间均匀打散在某个时间区间里方便测试按时间维度的统计报表。除了字段分布表之间的数量比例也要提前规划。比如一个用户平均产生多少条订单这个比例决定了联合查询时的返回行数。我建议先估算再动手比如你要造一万用户、五十万订单那平均每个用户50条订单生成订单数据时user_id就要从用户表的ID范围内随机取并根据正态分布来控制极端用户和普通用户的占比。3. PG、MySQL、SqlServer三种数据库的造数实操方案3.1 PostgreSQL的generate_series是造数神器先聊PG因为它是三种数据库里造数据最省力的。PG内置了一个叫generate_series的函数可以直接生成一个数字序列配合INSERT INTO ... SELECT语法一行语句就能插入几十万行数据不需要写循环。我随便举个例子。假设有一张users表字段是id、user_name、email、created_at我想插入50万条测试数据PG脚本可以这样写INSERT INTO users (user_name, email, created_at) SELECT user_ || gs, user_ || gs || example.com, now() - (random() * interval 365 days) FROM generate_series(1, 500000) AS gs;这里的关键点在于generate_series(1, 500000)生成50万个数字然后通过拼接字符串构造唯一的用户名和邮箱时间字段用random()函数做随机偏移。整个执行计划是一个集合操作效率比逐行循环高非常多。我实测过在一台普通配置的机器上插入50万行只需几十秒具体取决于磁盘性能。如果要造业务表比如订单表需要关联用户ID那就在SELECT里加个子查询从users表取ID范围再用随机数做偏移INSERT INTO orders (user_id, order_no, amount, status, created_at) SELECT u.id, ORD || lpad(gs::text, 8, 0), round((random() * 500 10)::numeric, 2), CASE WHEN random() 0.7 THEN completed WHEN random() 0.9 THEN pending ELSE canceled END, now() - (random() * interval 90 days) FROM generate_series(1, 200000) AS gs CROSS JOIN LATERAL (SELECT id FROM users ORDER BY random() LIMIT 1) AS u;这里用到了CROSS JOIN LATERAL简单理解就是每一行生成时都从用户表随机取一条ID。不过要提醒一句如果用户表特别大ORDER BY random()会导致全表排序性能很差。更稳妥的做法是先确定ID范围在应用层或SQL里用随机偏移量取ID。比如user_id在1到500000之间那就直接用1 floor(random() * 500000)来生成不需要真实关联用户表反正测试数据不需要严格外键一致性。3.2 MySQL的递归CTE和内存临时表方案MySQL的情况稍微复杂一点。MySQL 8.0及以上版本支持递归CTE也可以实现类似generate_series的效果。但在8.0之前的版本要么用存储过程循环要么用数字辅助表。我这边先说8.0的写法再补充老版本的替代方案。递归CTE生成50万行INSERT INTO users (user_name, email, created_at) WITH RECURSIVE seq AS ( SELECT 1 AS n UNION ALL SELECT n 1 FROM seq WHERE n 500000 ) SELECT CONCAT(user_, n), CONCAT(user_, n, example.com), DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY) FROM seq;这个写法跟PG的generate_series思路一致。但有一个关键区别递归CTE默认最大递归深度有限制如果一次生成百万级数据可能会报错或者性能奇差。我建议把单批插入数量控制在10万以内然后分循环执行。另一种更推荐的做法是先创建一张数字表比如dim_numbers表里面提前灌入从1到1000000的数字之后所有造数场景都从这张表里取数INSERT INTO users (user_name, email, created_at) SELECT CONCAT(user_, n), CONCAT(user_, n, example.com), DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY) FROM dim_numbers WHERE n 500000;这种数字辅助表方案在老版本MySQL、新版本MySQL之间通用能避免递归CTE的性能坑而且执行计划走的是最简单的扫描没有递归链路的开销。我在实际项目中更喜欢这种方式尤其是在数据量百万级以上时明显更稳。存储过程循环我不太推荐尤其是逐行INSERT的写法数据量上万就开始明显变慢。如果非要用存储过程也建议配合批量提交每凑满1000行做一次批量插入。不过说实话在MySQL里用数字辅助表配合INSERT INTO ... SELECT已经是性能和代码可读性的最佳平衡了。3.3 SqlServer的GO批处理与递归CTESqlServer的语法和MySQL有一些相似之处也支持递归CTE另外有一个很特别的批处理指令GO可以配合变量循环实现多次执行。SqlServer造用户的写法INSERT INTO users (user_name, email, created_at) WITH seq AS ( SELECT 1 AS n UNION ALL SELECT n 1 FROM seq WHERE n 500000 ) SELECT CONCAT(user_, n), CONCAT(user_, n, example.com), DATEADD(DAY, -FLOOR(RAND(CHECKSUM(NEWID())) * 365), GETDATE()) FROM seq OPTION (MAXRECURSION 0);这里要比MySQL多写一个OPTION (MAXRECURSION 0)因为SqlServer默认递归CTE上限是100层不关掉这个限制序列生成到100就走不下去了。RAND函数在SqlServer里每行调用时需要用CHECKSUM(NEWID())做种子否则同一批次内会返回相同的随机数这是新手特别容易踩的坑。另一种常用的SqlServer方案是GO循环。因为SqlServer里循环逐行插入效率同样很低我更喜欢利用GO指令让同一批INSERT重复执行次数通过变量控制DECLARE i INT 0; WHILE i 50 BEGIN INSERT INTO users (user_name, email, created_at) SELECT CONCAT(user_, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) i * 10000), CONCAT(user_, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) i * 10000, example.com), DATEADD(DAY, -FLOOR(RAND(CHECKSUM(NEWID())) * 365), GETDATE()) FROM master..spt_values CROSS JOIN master..spt_values AS s2 WHERE s1.number 100 AND s2.number 100; SET i i 1; END;master..spt_values是SqlServer系统表里面有几千行数字记录交叉连接后能产生足够多的行数来支撑INSERT。这种方式的好处是不依赖递归CTE老版本SqlServer也能用。不过说实话用WITH递归已经够用了不必为了炫技去写这种系统表交叉连接。三种数据库的写法各有特色我整理了一个对比表方便你选型数据库推荐方案单次推荐数据量适用场景PostgreSQLgenerate_series INSERT SELECT10万~100万中大规模数据语法简洁MySQL 8.0数字辅助表 INSERT SELECT5万~20万通用场景性能稳定MySQL 老版本存储过程批量循环1万~5万表结构简单仅做功能联调SqlServer递归CTE MAXRECURSION 05万~20万通用场景语法清晰3.4 造数脚本的流程化设计从一次执行到处处可用很多人写完一次脚本就扔了下次要造另一张表又从头写一遍效率很低。我自己的习惯是把造数脚本做成一个半自动化的模板核心思路是表名和字段规则参数化通过简单修改就能复用。具体做法是这样的。先用一个变量定义目标表名、起始ID、数据量、日期范围然后主体SQL尽量写成“基于数字序列生成行”的通用结构。拿MySQL举例我会固定一个套路-- 参数区 SET target_table orders; SET start_id 1; SET batch_size 50000; SET user_min_id 1; SET user_max_id 100000; SET start_date 2024-01-01; SET end_date 2024-12-31; -- 执行区 INSERT INTO orders (order_no, user_id, amount, status, created_at) SELECT CONCAT(ORD, LPAD(n, 8, 0)), user_min_id FLOOR(RAND() * (user_max_id - user_min_id 1)), ROUND(10 RAND() * 500, 2), CASE WHEN RAND() 0.7 THEN completed WHEN RAND() 0.9 THEN pending ELSE canceled END, DATE_ADD(start_date, INTERVAL FLOOR(RAND() * DATEDIFF(end_date, start_date)) DAY) FROM dim_numbers WHERE n BETWEEN start_id AND start_id batch_size - 1;这样做的好处很明显下次换一张表只需要改字段映射关系而生成逻辑、参数设置、批量控制这些骨架完全不用动。我在团队里还习惯把参数区单独放在SQL文件顶部注释写清楚每个参数的含义这样新人接手也能直接上手改。再进一步如果表特别多可以写一个简单的存储过程接收表名参数内部根据表名走不同的字段生成逻辑。不过坦白讲存储过程的维护成本比普通SQL高我更倾向于用外部脚本比如Shell或Python来循环调用预写好的SQL文件这样逻辑清晰、出问题也容易定位。3.5 造数性能优化别让数据库卡死在插入上百万级数据造起来时间花在哪里除了SQL本身的执行还有很大一部分在索引维护、日志写入、约束检查上。所以性能优化也要从这几个方向同时下手。先说批量大小。单条INSERT逐行提交性能最差因为每条都要走一次事务提交、一次日志刷盘。通用做法是把批量控制在5000到50000之间具体值取决于表结构复杂度和磁盘性能。我在MySQL里测过批量大小从1000提升到10000总耗时能缩短一半以上但再往上提升就不明显了甚至因为单条SQL太大反而变慢。再说索引。如果目标表已经有普通索引、唯一索引插入时每行都要更新索引数据量大了之后索引维护会成为瓶颈。如果测试数据不需要保留原有索引可以临时禁用或先删掉索引插完再重建。MySQL里可以用ALTER TABLE ... DISABLE KEYS配合ENABLE KEYS尤其对MyISAM表效果明显。InnoDB表不能直接禁用但可以先DROP INDEX再ADD INDEX。PG可以临时把索引设置为UNLOGGED或者造数期间关闭同步提交。SqlServer也类似可以把表切换到堆模式完成后再重建聚集索引。日志也是一个容易被忽视的因素。如果你只需要造测试数据不在乎崩溃恢复可以临时把数据库设置为不强制每次提交都刷盘。MySQL的innodb_flush_log_at_trx_commit设为0或2PG的synchronous_commit设为offSqlServer可以暂时用简单恢复模式。调整之前要确认这是独立测试环境生产库千万别动这个开关。我整理了一个优化清单按优先顺序排列使用INSERT INTO ... SELECT替代逐行插入数据量超过十万时删除或禁用目标表索引关闭外键检查MySQL的SET FOREIGN_KEY_CHECKS0临时降低日志刷盘强度仅限隔离的测试环境分批提交每批控制在5万行以内如果目标表有自增主键避免过小步长减少页分裂和锁竞争4. 造数过程中的常见问题与排查实录4.1 随机数据撞了唯一约束造数最常见的报错就是Duplicate entry。根本原因还是随机生成的字段值在大量样本下必然产生碰撞。你拿UUID或者随机字符串去填充唯一字段数据量到几十万之后碰撞概率一点都不低尤其是字段长度只有十几位的情况下。我的解决办法是唯一性字段一律使用数字序列拼接前缀来保证不要依赖随机。用户名就是user_00000001这种结构订单号就是ORD00000001这种结构。有些场景要求看起来像真实数据比如邮箱那至少保证本地部分是用序列号生成的后面的域名可以随机换几个后缀。这样既保证了唯一性又不会让数据看起来千篇一律。真要用随机就把随机部分长度拉到碰撞概率可忽略的程度比如32位十六进制而不只是8位字符串。4.2 时间字段全是同一个值这个问题特别阴险。我在一次造数据时用了RAND()函数生成日期偏移量结果日志显示5000行数据的created_at完全一样。查了半天才发现MySQL的RAND()在同一个执行批次里只初始化一次后续所有行拿到的都是同一个随机数。这其实不是RAND过期的问题而是优化器对查询做常量折叠把RAND()优化成了固定的值。解决办法要么用相对随机的表达式比如在每行关联一个序列数参与计算让RAND()受行号影响RAND(n 1)。要么在应用层生成数据。PG和SqlServer也有类似问题SqlServer的RAND()在单行函数里不加种子时每批只取一个随机值。踩过这个坑之后我养成了一个习惯每次造完数据先看一眼时间字段的DISTINCT计数如果远小于行数就得怀疑随机函数是不是被优化了。4.3 外键约束导致的插入顺序问题外键约束说白了就是主表和从表的插入顺序问题。我见过太多人先插子表再插主表结果外键校验直接报错。还有一种情况是两张表相互依赖比如用户表和用户详情表各有一个外键指向对方插入就卡住了。处理思路很直接先分析外键依赖关系按“被引用的表在前引用的表在后”的顺序插。如果有双向依赖就临时禁用外键检查插完再开启。MySQL可以用SET FOREIGN_KEY_CHECKS0PG可以用SET CONSTRAINTS ALL DEFERREDSqlServer就没这么方便了一般建议在造数阶段先把外键禁掉或者删掉完事再重建。这里需要提醒禁用外键检查只适用于隔离的测试库如果在共享的开发库上操作可能导致其他同事的数据被连带影响。4.4 造数速度越跑越慢怎么排查很多时候脚本一开始跑得很快越往后越慢直观感受就是数据库“累了”。背后原因通常有三个。第一是索引膨胀随着数据量增长B树的层级变深每次插入定位索引位置的开销增大。第二是表碎片重复的插入让物理存储不再连续扫描时间变长。第三是事务日志膨胀日志文件持续增大刷盘压力升高。排查方法也很直接。插入过程中监控几个指标磁盘IO等待时间、事务日志大小、目标表的索引统计信息。如果发现IO占用很高多半是日志刷盘太频繁可以增大批处理量或者调整刷盘参数。如果索引统计信息长期不更新可以手动ANALYZEPG/MySQL或UPDATE STATISTICSSqlServer让优化器重新认识数据分布。另外如果造数脚本要执行很久建议用后台任务方式跑同时定时输出进度别让终端卡死在那干等。常见问题速查表现象排查方向推荐处理唯一键冲突报错字段生成逻辑是否保证唯一改用序列号拼接前缀不依赖纯随机时间字段完全相同随机函数被常量折叠让随机表达式依赖行号或序列号外键约束报错从表先于主表插入按依赖顺序插入测试环境临时禁用外键插入速度断崖式下降索引膨胀、日志刷盘压力大分批插入删除索引调整日志参数内存占用过高单批数据量过大压缩单批行数控制在5万以内数据分布单一参数写死缺少权重设计用CASE WHEN为字段增加比例分布4.5 一个印象深刻的真实排查案例有一次帮一个项目组造测试数据表结构是典型的电商订单场景有orders、order_items、users三张核心表。用户表要造200万用户订单表要造500万订单明细表要造1500万行。一开始我用了最简单的方案挨个表执行循环插入结果跑了一个多小时只完成了一小半而且数据库CPU长期占满其他同事连不上这个测试库。我把脚本停下来换了个思路全程用INSERT INTO ... SELECT第一批先把索引全部删掉插入结束后统一重建。同时把随机数生成规则全部改为基于数字序列的确定性方案时间字段用日期偏移量加行号取模的方式打散。重新执行后用户表200万行大概用了两分钟订单表500万行用了不到十分钟。重建索引反而成了最耗时的环节但因为只做一次整体体验好了太多。这个案例让我总结出两条经验第一性能问题的根源往往不是单个SQL慢而是选择了低效的造数策略第二造数脚本更像一次ETL任务而不是简单的插入操作要用批处理思维去设计而不是逐行循环思维。如果你遇到类似问题建议先算一下总行数和总字段数如果确认是千万级就提前做好分批和索引重建的计划。5. 更真实一点的模拟数据如何让假数据更像真数据5.1 用规则的随机代替完全的随机如果目标只是“有数据”那随机字符串随便填。但如果目标是让前端页面看起来顺眼、让查询结果符合业务直觉那就要让字段值符合业务规则。比如手机号字段你不能生成一个以0开头的11位数字邮箱字段域名得有真实存在的后缀姓名、地址这种字段不能是user_123这种一看就是造出来的东西。我常用的做法是准备一份候选数据池。比如造用户表时姓氏从“赵钱孙李周吴郑王”等常见姓氏里取名字从一组常用汉字里随机组合或者更简单点准备50个常用名然后随机拼接。地址字段准备好城市列表、街道列表、小区列表按随机组合生成。手机号直接用13x、15x、18x的号段前缀加8位随机数字。这些候选数据池不需要很大每个维度几十个候选值就够了因为随机组合之后能产出的组合数已经非常多不会出现重复。这种“半规则随机”的做法还有一个好处生成的测试数据可以直接用于演示、截图、用户体验验证。你给业务方展示一个订单列表页面时数据里不会有乱码、不会出现超长字符串把布局撑坏观感好很多。5.2 业务口径的分布控制业务口径的分布控制比你想的重要得多。想象一个场景你要测试一个“近30天活跃用户”的报表接口。如果造数时没有控制登录时间字段所有用户的活跃时间随机分布在过去一年里那么“近30天活跃”这个筛选条件查出来的行数可能不到全表的3%前端图表看起来稀稀拉拉根本看不出趋势。解决办法就是给关键字段加分布控制参数。拿登录时间举例我先决定近30天内的用户要占总数量的60%然后这个区间之内再均匀打散。SQL里用CASE WHEN RAND() 0.6走“近30天”的分支其余走更早的分支。类似的做法也适用在订单状态、金额区间、渠道来源这类业务字段上。再拿订单金额来说真实业务通常是大量小额订单但你如果均匀分布平均值两岸的行数大致相等这跟真实数据差异极大。我常用的一种简单模型是把金额拆成几档50元以下占40%50到200元占30%200到1000元占20%1000元以上占10%。用CASE WHEN配合RAND()就能实现代码量不大但测试结论的参考价值高很多。5.3 为测试场景定制特殊数据有些测试场景需要特殊数据这时候通用方案就不够用了。比如你要测一个分页查询在深分页场景下的性能就需要造出几百万行历史数据且时间全部集中在前几个月保证当前页返回的结果很少。又比如你要测一个针对“已删除”状态的过滤逻辑就需要造一批statusdeleted的数据并且保证它们的deleted_at字段有值。这种情况下我会额外写入一批标记数据。最简单的方式是单独准备几个SQL文件专门造这种边缘数据在基础数据造完之后执行。也别忘了给这批数据设置明显的业务标识比如用户名以special_开头方便后续清理和识别。造数脚本的整体思路是基础数据批量生成特殊数据单独定制两者结合才能覆盖完整的测试场景。6. 整理一份可直接上手的执行清单6.1 造数前的准备清单结合我自己的实操经验每次造数前过一遍这个清单能省掉大部分返工时间确认目标表结构和约束列出主键、唯一键、外键、CHECK约束、非空字段、默认值、字段长度统计表间依赖关系明确插入顺序主表先插、从表后插规划数据量和数据分布确定总行数、关键字段的比例分布、时间区间范围准备候选数据池姓名、手机号、地址、邮箱等字段的候选值避免纯随机确定批量大小单批行数建议为5000到50000千万级数据提前规划分批次确认环境隔离测试库独立磁盘空间足够数据库账号具备DDL权限制定索引策略造数期间删除索引或重建完再插入大表先禁用外键检查6.2 造数脚本的推荐结构一份好的造数SQL文件应该能做到“参数化、可复用、易排查”。我推荐按下面的结构组织顶部参数区数据量、ID范围、日期范围、批量大小、状态分布比例环境准备禁用外键检查、删除目标表索引、修改日志刷盘参数主数据生成按表逐个执行INSERT INTO ... SELECT批量插入索引重建插完数据之后统一重建索引更新统计信息数据校验执行SELECT COUNT(*)对比预期行数抽查关键字段分布环境恢复恢复外键检查、恢复日志参数确认数据可被查询6.3 造数性能优化的参数速查整理了一份参数速查表你可能用得上数据库关键参数推荐值用途MySQLinnodb_flush_log_at_trx_commit0或2测试环境减少日志刷盘频率MySQLforeign_key_checks0插入期间跳过外键检查MySQLunique_checks0插入期间跳过唯一约束二次校验MySQLbulk_insert_buffer_size256M加速MyISAM批量插入PostgreSQLsynchronous_commitoff测试环境降低事务提交等待PostgreSQLmaintenance_work_mem512M~1G加速索引重建SqlServer恢复模式简单测试环境降低日志空间占用SqlServerMAXRECURSION0解除递归CTE深度限制以上参数都只建议在隔离的测试环境里调整。生产库或共享开发库动这些开关后果可能很严重轻则影响其他同事重则造成数据丢失风险。6.4 最后一步校验你造出来的数据是不是合格的数据造完别急着走。我会花几分钟跑几组查询确认数据质量达到预期。最基本的校验是行数统计SELECT COUNT(*)和目标值对比差一截就说明插入过程中有部分批次失败了。然后是字段分布校验拿订单状态举例分别统计completed、pending、canceled的行数占比看是否符合预设的70%、20%、10%比例。再就是抽样检查随机看几行的完整数据确认没有空值、没有超长字符串、没有乱码、日期字段没有超出预期范围。还有一个容易被忽略的点造完数据之后一定要更新统计信息。MySQL的ANALYZE TABLE、PG的ANALYZE、SqlServer的UPDATE STATISTICS各跑一遍。否则优化器手里还是旧统计信息执行计划可能基于错误判断走全表扫描然后你以为是自己SQL写错了其实是统计信息过期了。7. 写在最后的一点个人体会造数据这个东西看起来是个体力活但做久了你会发现它特别考验你对数据库的理解深度。同样一张表不同人写出来的造数脚本性能可能差几十倍同样的数据量不同字段生成规则对测试结论的影响可能完全不一样。我在多次实战之后最大的感受是先想清楚你要什么数据、怎么分布再动手写SQL。脚本本身只是执行工具真正决定数据质量的是你前期的规划和设计。如果你现在正在为测试数据不足发愁我建议你按这篇文章的思路先选一张核心表练手跑通一个完整的造数流程再慢慢扩展到其他表。大方向搞清楚了后面就是熟练度的问题。等你把批量化造数的流程固化下来再遇到需要造几百万条测试数据的任务就会觉得这其实是件很有把握的事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑