
“随便造点数据”这个标题一看就是踩过坑的人才会写出来的。真到了联调、压测、做Demo、跑报表的阶段你会发现最缺的往往不是代码也不是服务器而是数据。开发环境里连个正经用户都没有登录功能怎么验订单列表空空荡荡分页怎么调没有任何一笔支付记录对账逻辑怎么跑于是我们开始“随便造点数据”。但这两个字往往是最贵的——数据造得不够真业务逻辑里的坑根本炸不出来造得太随意上线前又得花几倍时间返工。这篇文章记录的是我在造数据这件事上攒下来的一些经验包括工具选型、字段设计、批量生成、边界值处理以及几个让我印象深刻的翻车现场。适合正在为测试环境、Demo演示或者数据脱敏发愁的人哪怕你完全没写过造数脚本照着思路也能拼出一个能用的。1. 为什么“随便造点数据”这件事值得认真对待1.1 没有数据开发进度卡在最后一公里很多项目在开发阶段是“逻辑先行”的。接口写好了、页面调通了结果一点进详情页就是空指针或者空列表。不是代码有问题而是数据库里根本没有对应数据。你花半小时造了一条数据点过去发现列表能显示了但详情页的字段没配全又得回头补齐。这种反复在开发期特别费时间尤其是前后端并行的时候前端拿着假接口联调后端测着自己的逻辑两边用的根本不是同一份数据最后对不上又是一轮扯皮。所以后来我的态度是造数据不是测试阶段的活而是开发期就该同步做的事。越早把一份“长得像生产环境”的数据放进开发库很多接口取值、空值判断、格式化问题就能提前暴露。这个成本比你部署到测试环境再发现要低得多。1.2 生产数据不是你想用就能用有的人会说直接复制一份生产数据到测试库不就行了听起来简单但问题一堆。首先是隐私合规压力电话、身份证、地址、支付记录这些敏感信息在测试环境里裸奔出了事很难解释。其次是数据量太大生产库几百G测试库根本不需要那么多数据导过来还会拖慢开发机的性能。第三是数据特征不同生产环境里有复杂的状态流转、异常订单、历史遗留数据直接复制确实会带来一些“惊喜”但也会带来一堆和需求无关的脏数据。与其从生产库里费劲清洗、脱敏不如专门造一份“高度仿真但完全虚构”的数据。这个方案可控性强想要多少量就给多少量想包含什么边界值就加什么值对开发、测试、前端联调、Demo演示都是最舒服的。1.3 造数据不是“乱填”而是“定向模拟”“随便造”这三个字我理解的是不拘泥于严格业务数据但绝不是完全乱来。比如你造一个用户表用户名乱填成“张三”“李四”没问题但手机号也得能过正则校验不然登录模块永远测不出来。订单金额乱填成负数可以但你的对账逻辑到底该不该拦截负数是不是你想测的点造数据前最好先想一遍这条数据会被哪些逻辑消费它需要满足什么约束它要触发哪条分支想清楚了造数据就是从“填空”变成“建模”造出来的数据才能真正帮你发现问题而不是只让页面不报错。这也是整篇文章最核心的一句话。2. 造数据的第一层把基础字段“感觉到位”2.1 字符串、整数、日期最基础的三种随机很多人第一次写造数脚本就是从随机数开始。Python里的random模块足够应付基础场景但有几个细节容易出错。比如random.randint(0, 999999)生成订单号看着没问题但订单号如果是字符串类型前端展示可能缺位补零如果订单号需要唯一随机碰撞的概率在数据量大的时候就不能忽略。我通常会根据字段语义拆分字符串类型从一组预定义前缀加随机数字生成类似ORDER-20250101-001这种带日期和中序号的结构可读性和唯一性都比纯随机好。整数类型区分业务含义。年龄一般在1到100之间金额要考虑小数位和精度库存要考虑最小值不能为负。日期和时间用datetime模块做偏移比如注册时间取过去365天内的随机日期最后登录时间不能早于注册时间。基础字段的“感觉对”体现在类型、范围、格式三个维度上都说得通。先写一个小工具函数把这些规则收敛起来后面再造数就很顺手了。2.2 手机号、身份证、邮箱这类“有校验规则”的字段这里是最容易翻车的地方。手机号不是纯随机11位数字就行的它得符合号段规则否则一入你的项目里做了运营商号段校验这堆数据就全部失效。身份证更复杂前6位对应地区中间8位是出生日期第17位有性别含义最后一位可能是X而且整体还要满足校验码规则。邮箱要保证格式合法最好能按域名分布比如一部分用qq.com一部分用163.com一部分用公司自建域名。我的建议是不要去手动拼这些复杂字段直接用Faker这类成熟库它已经内置了各个国家地区的手机号、身份证、地址、公司名、邮箱等生成规则。用库的好处不只是省时间更重要的是它生成的规则是经过大量使用者验证的不容易出现低级格式问题。2.3 第一步先搞出一版“有感觉”的数据把基础字段规则整理好之后不要急着追求数据量先造100条出来肉眼扫一遍。看看姓名和性别有没有明显不搭配订单日期是不是有未来的日期金额有没有出现超过正常范围的极端值。这步很像做饭时候的试味先小批量验证规则确认没问题再上量。我自己的习惯是先把生成逻辑写成一个函数输入是数量输出是List[Dict]每一步都可以打印到终端看看。这样后续不管接CSV、Excel、MySQL还是PostgreSQL都只是把结果写出去的事和生成逻辑本身解耦。3. 造数据的第二层字段之间要有“生活常识”3.1 数据之间的关联关系才是“以假乱真”的关键基础字段只是及格线。真正让人觉得“这套数据没毛病”的是字段之间的逻辑关系。举个最常见的例子用户表里有注册时间和最后登录时间如果最后登录时间随随便便随机很容易出现用户还没注册就已经登录了的鬼故事。订单表里的下单时间和支付时间必然不能早于下单时间发货时间不能早于支付时间。用户表和订单表通过用户ID关联那同一个用户的订单金额分布应该符合这个用户的消费水平。这些关联关系看着简单但很多造数脚本就是死在这上面。原因也容易理解写造数脚本时人的注意力全在单字段的随机范围上很少去画一张字段依赖图。等数据一多各种前后矛盾就出来了。3.2 用状态机思路来造“过程型”数据如果业务里有状态流转比如订单从“待支付”到“已支付”再到“已发货”“已完成”造数就需要状态机思路。一个订单在时间轴上是有生命周期的不能一个状态为“已完成”的订单支付时间比下单时间还早。我习惯先定义状态之间的合法转换路径然后照着路径去生成时间戳和状态字段。比如想生成一批已完成订单就按“下单-支付-发货-完成”四个节点依次生成时间每个后续时间在前一个时间上增加随机间隔。间隔也要符合业务常识支付一般在下单后几分钟到一天内发货在支付后几小时内到两天内完成在发货后几天内。用这种思路造出来的数据拿来画漏斗、看时长分布都是成立的。3.3 数据分布真实业务不是均匀大乱炖纯随机生成的分布和真实业务分布差得非常远。比如用户活跃度真实场景肯定是少数用户很活跃、大量用户偶尔访问一次也就是长尾分布。订单金额往往在低价区扎堆高价订单很少。如果你尝试用random.uniform(100, 10000)生成金额做出来的客单价分布会跟实际业务对不上后续做数据分析展示时就特别假。这里有两个常用手段。一个是按比例分层抽样先设定好业务比例比如订单金额20%是0到10050%是100到100025%是1000到50005%是5000以上再在每层里随机。另一个是用正态分布或者幂律分布来模拟自然分布取决于你的业务形态。前者更简单直观适合大多数内部测试场景后者更贴近真实生态适合做数据产品Demo或者算法验证。4. 造数据的第三层批量生成、写入性能与数据量级4.1 十万条数据跑得动吗先想清楚瓶颈在哪数据量上到十万、百万级别你很快会发现生成数据本身一点也不慢慢的是两件事内存占用和数据库写入。Python里一次性生成一百万个字典对象放进列表内存轻松吃掉好几个G机器差点就直接卡死了。解决思路也简单不要一次性生成全量改成生成一批、写一批、清一批。比如每5000条刷一次数据库循环反复内存占用就能打得很低。还有一个常被忽略的点数据库批量插入要用批量SQL不要一条一条insert。一条条插入每次都有网络往返和事务提交开销十万条能跑死人。批量insert每5000条提交一次时间能从几分钟降到几秒差异非常明显。4.2 生成唯一键的时候别踩并发和碰撞的坑造数脚本一般单线程跑碰撞问题不明显但只要你用到唯一键还是建议留个心眼。直接用random生成然后去重数据量一大就可能反复碰撞。更好的是采用“前缀递增序号”的方式或者用UUID。用UUID唯一性没问题就是索引空间大如果业务主键是字符串类型能接受如果是整型主键UUID就不合适了。我自己经常用“业务前缀日期循环序号”的结构比如user_20250110_0001。这样做既保证可读性又天然唯一而且对后续按前缀模糊查询也友好。注意这个方案只适合造数据场景生产环境的ID策略另说。4.3 不同数据量级造数策略完全不同一千条、一万条、一百万条策略绝对不是简单的参数放大的关系。千条级别直接循环生成写到Excel或者SQL文件都行。万条级别注意批量插入脚本本身保持简单即可。百万条级别就要考虑生成器模式、批量刷写、关闭数据库自动提交、尽量用本地文件先行生成再导入数据库。如果目标是压测那就更讲究了。压测数据往往需要特定分布、特定关联、特定字段宽度而且可能需要大量数据在同一个库里共存。这时候我一般会先把基础维度表造好再在此基础上并行生成事实表数据否则百万条数据全在一个脚本里跑又慢又不稳定。5. 真正难造的是“假但合理”的数据三个翻车现场5.1 翻车现场一手机号全是“空号”有一次给一个短信验证码功能造测试数据我用随机9位数字拼在138后面生成了一堆手机号。看着挺正常结果联调的时候短信服务商真去调了号码可携数据库发现这批号码全是空号。虽然测试环境一般不会真的发短信但那一瞬间我意识到造数据不光是格式对还要考虑你的下游系统会拿这些数据做什么。后来我在造手机号的时候就直接调用Faker的手机号生成器并且尽量选用官方文档里的测试号段或者刻意使用像19999999999这样不会打通的号段。你要清楚造数据的目的不是真的能用这些手机号收短信而是让你的业务逻辑跑得通同时不要误伤真实用户。5.2 翻车现场二订单金额和明细对不上这是个特别经典的问题。订单主表有一个总金额订单明细表里有商品单价和数量两者之间应该满足“总金额明细行金额之和”的约束。当时我图省事先造主表随机总金额再造明细随机单价和数量结果对账一跑全是不平账。这种数据拿去做功能测试不仅测不出问题还会把正常代码判定为异常。从此之后遇到这种主子表结构我的造数顺序一定是先造明细再汇总主表金额或者所有金额都从同一个根值派生出来。这种“从底向上”的造数顺序能保证所有逻辑约束天然成立。5.3 翻车现场三用户画像自相矛盾某次做一个用户画像页面需要生成性别、年龄段、兴趣标签和消费类目。我图方便独立随机每个字段于是出现了“60岁女性兴趣标签是电竞外设消费类目全是什么潮牌男装”这种离谱画像。页面展示之后产品经理一眼就看出数据是假的问我为什么不做点合理的数据。这给我提了个醒造数据要定义“合理域”。比如根据年龄段限制兴趣标签和消费类目或者只从一组“预设人设”里随机抽取。先设计二十个典型用户画像再按画像去生成对应的属性数据这样整体合理性就会好很多。演示效果也好测试覆盖率也不差。6. 造数据的进阶玩法生产数据脱敏与“半造半真”混合6.1 脱敏和造数其实是一回事后来我逐渐发现生产数据脱敏本质上就是一种造数。你要把真实的姓名、电话、地址替换成看起来合法的假数据同时保留原本的统计特征和关联关系。比如把“张三”改成“张伟”把手机号换成同一号段的随机号码把身份证里的出生日期保留但把地址码和顺序码替换成虚拟值。这样既能测试真实业务逻辑又不会导致敏感信息泄露。如果团队已经有生产数据可以用我的方案经常是“脱敏补造”。从生产库抽取一小部分数据做字段替换和偏移比如日期整体往前后平移一段时间、金额加一个随机扰动、ID重新映射这样既保留了数据分布的真实性又切断了和真实用户之间的关联。6.2 静态脱敏和动态脱敏怎么选静态脱敏就是把数据从生产库导出来后清洗一遍再导入测试库。这种方式适合手工测试、数据开发、分析报表这类离线场景。动态脱敏则是在请求数据的时候实时做脱敏处理相当于中间加了一个拦截层生产库不动测试环境看到的是脱敏后的数据。这种方式对权限控制比较友好但架构成本也更高。如果你只是一个人维护测试库我的建议是先做静态脱敏简单直接跑完就完事。如果你们有完整的测试环境治理要求再考虑动态方案。脱敏工具方面开源社区有很多选择但在小团队里往往不需要上重型组件用Python脚本或者SQL替换也能覆盖大部分需求。6.3 造数据工具的取舍我最后留下了什么Faker确实是最常用的造数库几乎所有基础字段都能覆盖。但是如果你的业务非常垂直比如物流行业、医疗行业、金融行业Faker提供的通用数据其实不够用。这时候我的做法是在Faker之上封装一层“业务字段工厂”把物流单号、医保目录编码、理财产品代码这些自定义规则都塞进去。核心思路就是通用抽象用现成库特性业务自己写规则。还有一类场景需要“延续性数据”比如一套数据要长期供多个环境使用。那你就不能每次运行脚本生成完全不同的数据而要让生成结果可复现。方法是固定随机种子或者在脚本里记录生成批次号。保持同批次数据在多个库之间的一致很多联调和对比工作会轻松不少。7. 关于“随便”两个字我最后的看法造数据这件事越做到后面越觉得它不是“随便”就能做好的但也没必要做得像建设生产系统一样重。关键是让每一个字段都经得起逻辑推敲让每一条数据都能为你下一步的开发验证提供有效支撑。从最开始写一个几十行的随机脚本到后来封装出一套带状态机、带分布逻辑、带脱敏能力的造数模块我最大的体会是数据生成规则的投入产出比极高。花一下午时间把规则写清楚之后每次联调和测试都能省出好几天。尤其是那些被人反复吐槽的“真花时间”的环节比如表单校验、报表核对、可视化看板一套合理的数据能让你直接跳过大部分无效调试。如果你现在正准备造数据我的建议是四个字先想后造。列一下业务有哪些表、哪些字段之间有依赖关系、哪些状态流转是合法的、哪些分布是符合常识的再动手写代码。这个顺序省下来的时间比你找任何库都值钱。