AI时代的程序员修养:数据库不是存 JSON 的地方
《AI时代的程序员修养》上一篇讲接口契约。接口是服务对外的承诺数据库则是系统对自己的承诺。字段、索引、唯一约束、事务、锁和迁移脚本决定了数据能不能保持一致系统能不能撑住增长故障后还能不能解释。AI 很容易把数据库当成“存一下对象”的地方。需求里有个订单它就建一张订单表字段不确定它就塞进 JSON查询慢了它就建议加索引并发出问题了它再补锁。这种顺序在 demo 里能跑在线上会留下很硬的技术债。数据库不是存 JSON 的地方。它是系统里最不容易被糊弄的部分。表结构就是业务规则很多人把表结构看成代码的附属品。先写对象再让 ORM 生成表。这样很快但容易把业务规则藏在代码里数据库只剩下几个宽松字段。比如一个任务表AI 可能会生成create table jobs ( id uuid primary key, user_id uuid not null, status text not null, payload jsonb not null, result jsonb, created_at timestamptz not null default now() );这张表能用但它几乎没有表达规则status可以写任何字符串。同一个业务任务能不能重复创建看不出来。任务是否可以被 worker 抢占看不出来。重试次数、下次执行时间、错误信息都在不在payload里没人知道。查询 pending 任务时需要什么索引也没表达。更像业务系统的表至少应该把关键状态和访问路径露出来create table jobs ( id uuid primary key, user_id uuid not null, job_type text not null, dedupe_key text not null, status text not null check ( status in (pending, running, success, failed, cancelled) ), priority integer not null default 0, run_at timestamptz not null default now(), locked_by text, locked_at timestamptz, attempt integer not null default 0, max_attempts integer not null default 3, payload jsonb not null, result jsonb, last_error text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (user_id, job_type, dedupe_key) );这张表仍然用了 JSON但 JSON 不再承担所有职责。payload可以放变化较快、查询较少的输入参数状态、调度、重试、抢占和去重这些核心规则必须变成明确字段。这里的unique (user_id, job_type, dedupe_key)是业务规则不是性能优化。它说明同一个用户、同一种任务、同一个去重键只能有一条任务。没有这条约束代码里写多少“先查一下有没有”都挡不住并发插入。让 AI 设计表时可以先问请不要直接给 ORM model。 先列出这个实体的业务不变量 - 哪些字段必须唯一 - 哪些状态是枚举 - 哪些字段参与查询、排序、抢占、过期和重试 - 哪些内容可以放 JSON哪些必须独立成列 - 哪些规则必须由数据库约束保证而不是只靠代码判断数据库约束不是束缚开发速度而是把最重要的规则钉在系统底座上。JSON 字段要有边界JSON 字段很好用。它适合放不稳定的扩展信息、第三方原始返回、低频查看的配置、事件 payload 和审计快照。但它不适合承载所有业务逻辑。一个常见坏味道是create table tool_calls ( id uuid primary key, user_id uuid not null, data jsonb not null, created_at timestamptz not null );data里可能包含tool_id、provider_id、status、cost、search_id、latency_ms、error_code。短期看省事长期看会有几个问题查询要写data-tool_id索引复杂且容易漏。字段类型不稳定数字可能被写成字符串。数据质量无法靠约束保证。统计和排障都依赖 JSON 解析。重构时不知道谁在用哪个 JSON path。如果这些字段是核心查询条件或统计维度就应该独立成列create table tool_calls ( id uuid primary key, user_id uuid not null, tool_id text not null, provider_id text not null, status text not null check (status in (success, failed)), latency_ms integer, result_count integer, search_id uuid, error_code text, request_payload jsonb not null, response_summary jsonb, created_at timestamptz not null );JSON 可以继续存在但它退回到“扩展载荷”和“原始快照”的位置。判断一个字段要不要独立成列可以看五个问题问题如果答案是是会经常作为 where 条件吗独立成列会经常排序或分页吗独立成列会参与 group by 统计吗独立成列需要唯一约束或外键约束吗独立成列类型错误会造成业务事故吗独立成列让 AI 设计 JSON 字段时可以明确请把字段分成三类 1. 必须独立成列的字段。 2. 可以放入 jsonb 的扩展字段。 3. 不应该保存或需要脱敏的字段。 并解释每个独立列对应的查询、约束或统计需求。只要把这一步做了表结构就不会变成一个万能杂物箱。索引要从查询模式倒推AI 很喜欢在慢查询后说“加索引”。问题是加什么索引按什么字段顺序是否覆盖排序是否会拖慢写入都需要从查询模式倒推。比如工具调用历史页需要查某个用户、某个工具、最近一天的调用select id, tool_id, status, latency_ms, created_at from tool_calls where user_id :user_id and tool_id :tool_id and created_at :start order by created_at desc limit 100;比较合理的索引是create index idx_tool_calls_user_tool_time on tool_calls (user_id, tool_id, created_at desc);为什么是这个顺序user_id和tool_id是等值过滤created_at负责范围和排序。数据库可以先定位用户和工具再按时间倒序扫出 100 条。如果另一个查询是按 provider 汇总昨天调用量select provider_id, count(*) from tool_calls where created_at :start and created_at :end group by provider_id;上面的索引就不一定有用。它以user_id开头而这个查询没有用户条件。你可能需要create index idx_tool_calls_time_provider on tool_calls (created_at, provider_id);或者更实际一点做每日汇总表create table provider_daily_stats ( stat_date date not null, provider_id text not null, call_count bigint not null, success_count bigint not null, primary key (stat_date, provider_id) );索引不是越多越好。每个索引都会增加写入成本、占用磁盘、增加维护开销。低选择性字段单独建索引也不一定有用比如status只有几个值单列索引经常效果有限。让 AI 推荐索引时不能只让它看表结构要让它看查询请基于以下查询模式设计索引 - 每个查询的 where 条件、order by、limit。 - 预估数据量和选择性。 - 每个索引服务哪些查询。 - 哪些查询不适合靠索引解决需要汇总表或分区。 - 写入成本和磁盘成本。能解释字段顺序的索引才是真正想过的索引。事务边界不要跨慢操作事务用来保证一组数据库操作要么一起成功要么一起失败。它很重要也很容易被用坏。一个危险写法async with transaction(): order await create_order() await reserve_inventory(order) await call_payment_api(order) await send_notification(order)这段代码把外部支付和通知放进数据库事务里。外部调用慢事务就一直开着事务开着锁可能一直持有连接也一直占用。下游抖动会直接拖住数据库。更合理的边界是async with transaction(): order await create_order(statuspending_payment) await reserve_inventory(order) await outbox.add(payment.requested, {order_id: order.id})事务只包住必须一致的数据库写入。支付请求通过 outbox 交给后台任务。支付成功后再用另一个短事务更新订单状态。事务边界的原则很朴素事务里只放必须保持一致的数据库操作。不在事务里做 HTTP/RPC 调用。不在事务里做大文件读写。不在事务里做长时间 CPU 计算。不在事务里等待用户输入或外部任务。事务尽量短锁尽量少。这和前面执行模型那篇是连着的。慢操作要离开请求链路也要离开数据库事务。让 AI 写涉及事务的代码时可以要求请标出每个数据库事务的开始和结束。 事务内禁止外部 HTTP/RPC、文件 I/O、长时间计算。 说明事务保护的业务不变量是什么。 如果需要调用外部系统请使用 outbox 或任务表。如果 AI 不能说明“这个事务保护什么”那这个事务多半只是为了让代码看起来安全。锁不是补丁是协议并发更新同一份数据时必须考虑锁或版本控制。锁不是出了 bug 才补的东西而是多个执行者之间的协议。比如扣库存。危险写法inventory get_inventory(sku) if inventory.available quantity: raise OutOfStock() inventory.available - quantity save(inventory)两个请求同时读到库存 10各扣 8最后可能超卖。一种做法是单条原子更新update inventories set available available - :quantity where sku :sku and available :quantity;检查受影响行数。如果是 1扣减成功如果是 0库存不足。另一种做法是显式行锁select * from inventories where sku :sku for update;拿到锁后再判断和更新。行锁适合需要读取多字段、做复杂判断的场景但锁持有时间要短。还有乐观锁update accounts set balance balance :delta, version version 1 where id :account_id and version :expected_version;更新失败说明版本变了需要重读或重试。几种方式的取舍方式适合场景风险原子 update简单计数、库存扣减、额度扣减业务逻辑不能太复杂select for update需要锁住一行做复杂判断锁等待、死锁、长事务乐观锁 version冲突不高、读多写少高冲突下重试变多唯一约束防重复创建、幂等记录需要处理冲突错误advisory lock跨多行或业务 key 加锁使用不当会变成隐式全局锁让 AI 写并发更新时可以直接要求不能用“先查再改”的非原子方式。 请给出并发安全方案原子 update、行锁、乐观锁或唯一约束。 说明锁粒度、锁持有时间、死锁风险和重试策略。 请写两个并发请求同时扣减的测试。并发正确性不能靠“概率上不碰上”。慢查询是设计问题不只是 SQL 问题慢查询常常不是因为 SQL 写得丑而是访问模式一开始没有设计好。比如一个后台页面要展示用户列表每行显示最近一次登录、最近一次购买、订单总数、未读消息数。如果 AI 直接写很可能是users get_users(pagepage) for user in users: user.last_login get_last_login(user.id) user.order_count count_orders(user.id) user.unread_count count_unread_messages(user.id)这就是典型 N1 查询。页面 50 个用户后面可能跟着 150 次查询。更合理的方式是批量查询或预聚合select u.id, u.name, s.last_login_at, s.order_count, s.unread_count from users u left join user_summary s on s.user_id u.id order by u.created_at desc limit 50;user_summary可以由异步任务维护也可以由数据库 job 定时刷新。重点是别让列表页临时聚合一堆大表。慢查询排查要看是否返回太多列。是否缺索引或索引字段顺序不对。是否排序需要额外 sort。是否 join 了高基数字段但没有合适索引。是否 N1 查询。是否把聚合放在高频请求里。是否 where 条件用了函数导致索引用不上。是否需要分区、归档或汇总表。让 AI 优化慢查询时可以要求它不要只改 SQL请先分析这个页面的访问模式 - 每次请求需要展示哪些字段。 - 每个字段来自哪张表。 - 哪些字段可以异步预聚合。 - 哪些查询必须实时。 - 是否存在 N1 查询。 - 给出 explain 预期、索引建议和数据量增长后的风险。很多性能问题要靠数据模型解决不是靠把 SQL 写得更花。迁移脚本也要按线上系统写AI 生成 migration 很快但线上迁移不是简单alter table add column。新增 nullable 字段通常比较安全alter table users add column timezone text;但新增not null字段、加默认值、改字段类型、创建大索引、回填历史数据都可能锁表、拖慢写入或造成长事务。一个更稳的新增必填字段流程新增 nullable 字段。新代码开始写入新字段。后台分批回填老数据。验证没有空值。再加not null约束。例如alter table users add column timezone text;分批回填不要一口气更新全表update users set timezone Asia/Shanghai where timezone is null and id :last_id order by id limit 1000;不同数据库对limitin update 支持不一样实际要按数据库方言写。关键是分批、小事务、可暂停、可重跑。创建大索引也要谨慎。PostgreSQL 通常使用create index concurrently idx_tool_calls_user_time on tool_calls (user_id, created_at desc);concurrently可以降低锁表风险但不能放在普通事务块里。迁移工具需要支持这种特殊语义。让 AI 写 migration 时可以要求这是线上大表迁移不能长时间锁表。 请给出 expand - backfill - contract 三阶段迁移方案。 回填必须分批、可重试、可观测。 创建索引用并发方式并说明数据库方言限制。 给出回滚策略和验证 SQL。迁移脚本不是一次性杂活。它是生产系统变更的一部分。ORM 不能替你理解数据库ORM 很方便AI 也很喜欢用 ORM因为对象关系清楚代码生成顺手。但 ORM 会遮住很多数据库事实一行代码可能触发多次查询。访问一个属性可能 lazy load。批量更新可能变成逐行更新。事务范围可能比想象中更大。默认隔离级别可能不符合业务。JSON 字段和复杂索引可能表达不完整。迁移脚本可能和线上数据库方言不匹配。比如orders session.query(Order).limit(100).all() for order in orders: print(order.user.name)如果user是 lazy load这可能触发 101 次查询。AI 很容易写这种代码因为它看起来像普通对象访问。要求 AI 写 ORM 代码时要把查询行为说清楚请避免 N1 查询。 需要预加载 user但只取 id/name 两个字段。 请说明生成的 SQL 大致是什么。 批量更新不能逐行 commit。 事务必须在 service 层显式控制。ORM 是工具不是数据库知识的替代品。越依赖 AI 生成 ORM 代码越要让它解释背后的 SQL。一份给 AI 的数据库设计提示词写数据层之前可以先用这段 prompt先不要写 ORM model 或 migration。 请为这个功能做数据库设计 1. 列出核心实体、业务不变量和状态机。 2. 说明哪些字段必须独立成列哪些可以放 jsonb哪些不能保存。 3. 给出表结构、主键、唯一约束、check 约束、外键取舍。 4. 列出主要查询模式where、order by、limit、group by、数据量预估。 5. 为每个查询设计索引并解释字段顺序和写入成本。 6. 标出事务边界说明每个事务保护的业务不变量。 7. 分析并发更新场景选择原子 update、行锁、乐观锁或唯一约束。 8. 说明慢查询风险、N1 风险、是否需要汇总表或分区。 9. 给出线上迁移方案expand、backfill、contract、验证和回滚。 10. 最后再写 ORM model、repository 和测试。这段 prompt 很长但数据库值得这么长。因为数据结构错了后面所有代码都会围着它打补丁。写表前先问几句话动手写数据层之前至少问自己这个字段以后会不会作为查询条件这个状态能不能用 check 约束限制这个唯一性靠代码查还是靠数据库约束这个 JSON 字段里有没有核心统计维度这个查询一年后数据量是多少这个索引服务哪几个查询会拖慢哪些写入这个事务里有没有慢操作两个请求同时更新同一行时会怎样这个 migration 会不会锁大表ORM 生成的 SQL 我看过没有AI 可以快速写出表、索引、ORM 和 migration。程序员要做的是判断这些东西是否真的表达了业务规则是否能在并发、增长和线上变更里站得住。数据库不会因为代码是 AI 写的就变宽容。它只相信约束、事务、索引和真实的数据量。

相关新闻

OpenHarmony开发板ADC按键控制LED实现详解

OpenHarmony开发板ADC按键控制LED实现详解

1. 开鸿智谷NiobeU4开发板简介开鸿智谷NiobeU4是一款基于OpenHarmony系统的开发板,主打物联网和嵌入式开发场景。这块板子最吸引我的地方在于它完美支持鸿蒙生态,同时硬件配置也相当给力——搭载了高性能处理器、丰富的外设接口,以及我最看重…

2026/7/16 11:22:52阅读更多 →
靠谱的佛山MS环保胶厂家怎么挑

靠谱的佛山MS环保胶厂家怎么挑

在建筑与工业领域,MS环保胶凭借优异的粘接性、耐候性和环保特性,最近一两年,越来越多装修工程都指定用它,正在逐步取代传统胶粘剂。面对市场上五花八门的选择,不少采购方都会问:佛山MS环保胶哪家靠谱&#…

2026/7/16 11:22:52阅读更多 →
我与 IT 这三十年:2001,Linux 光盘里的黄金年代

我与 IT 这三十年:2001,Linux 光盘里的黄金年代

2000 年,互联网泡沫破了。 很多故事忽然冷下来。门户网站不再只讲增长,创业公司开始算现金流,资本市场从兴奋变得挑剔。那些关于“只要上网就会改变一切”的句子,变得没有前一年那么好用。 但有些东西没有冷。 Linux 就是其中之…

2026/7/16 11:22:52阅读更多 →
5分钟重塑音乐体验:foobox-cn让foobar2000焕发新生

5分钟重塑音乐体验:foobox-cn让foobar2000焕发新生

5分钟重塑音乐体验:foobox-cn让foobar2000焕发新生 【免费下载链接】foobox-cn DUI 配置 for foobar2000 项目地址: https://gitcode.com/GitHub_Trending/fo/foobox-cn 你是否曾经觉得专业音乐播放器就注定要牺牲美观?是否在寻找一款既强大又赏心…

2026/7/16 12:38:08阅读更多 →
Dify生产部署为何必须用Docker Compose而非单容器或手动安装

Dify生产部署为何必须用Docker Compose而非单容器或手动安装

1. 项目概述:为什么Dify必须用Docker Compose部署,而不是单容器或手动安装 Dify 是当前最主流的开源大模型应用开发平台,它不是简单调用API的玩具工具,而是一个具备完整知识库管理、工作流编排、多智能体协同、RAG增强、可视化调试…

2026/7/16 12:38:07阅读更多 →
终极指南:如何让10美元鼠标在macOS上比苹果触控板更好用

终极指南:如何让10美元鼠标在macOS上比苹果触控板更好用

终极指南:如何让10美元鼠标在macOS上比苹果触控板更好用 【免费下载链接】mac-mouse-fix Mac Mouse Fix - Make Your $10 Mouse Better Than an Apple Trackpad! 项目地址: https://gitcode.com/GitHub_Trending/ma/mac-mouse-fix 你是否曾经为macOS上第三方…

2026/7/16 12:38:07阅读更多 →
OpenCore Legacy Patcher终极指南:免费让老款Mac焕发新生,运行最新macOS系统

OpenCore Legacy Patcher终极指南:免费让老款Mac焕发新生,运行最新macOS系统

OpenCore Legacy Patcher终极指南:免费让老款Mac焕发新生,运行最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 你是否拥…

2026/7/16 12:38:07阅读更多 →
EeveeSpotify破解版:免费解锁Spotify Premium完整功能终极指南

EeveeSpotify破解版:免费解锁Spotify Premium完整功能终极指南

EeveeSpotify破解版:免费解锁Spotify Premium完整功能终极指南 【免费下载链接】EeveeSpotify A tweak to get Spotify Premium for free, just like Spotilife 项目地址: https://gitcode.com/GitHub_Trending/ee/EeveeSpotify 还在为Spotify Premium的高昂…

2026/7/16 12:38:07阅读更多 →
传导噪声分类与抑制:共模与差模噪声解析

传导噪声分类与抑制:共模与差模噪声解析

1. 传导噪声的基本概念与分类传导噪声(Conducted Noise)是指通过导体(如电源线、信号线等)传播的电磁干扰信号。这类噪声会沿着电路中的金属路径传导,影响电子设备的正常工作。根据噪声的传播路径和特性,传…

2026/7/16 12:33:07阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/16 8:28:11阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/16 6:53:04阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/16 12:02:41阅读更多 →
A--10 Codex Review与GitHub PR工作流实战指南:从代码审查到安全合并

A--10 Codex Review与GitHub PR工作流实战指南:从代码审查到安全合并

摘要:本文系统讲解如何利用Codex App的Review功能与GitHub PR工作流,实现从代码修改到安全合并的完整流程。涵盖Review面板深度使用、/review命令实战、GitHub Connector配置、PR描述撰写技巧,以及常见问题排查方法。通过多个实战案例和流程图,帮助开发者建立高效的AI辅助代…

2026/7/16 0:00:38阅读更多 →
遗传算法解5皇后问题:从Hello World到工业优化的进化实验室

遗传算法解5皇后问题:从Hello World到工业优化的进化实验室

1. 项目概述:为什么用遗传算法解5皇后问题,而不是直接回溯?我带过十几届算法课,也给不少初创团队做过AI架构咨询。每次讲到组合优化问题,学生和工程师的第一反应永远是“写个回溯试试”。这没错——55棋盘上找所有合法…

2026/7/16 0:00:38阅读更多 →
5.1V稳压管输出为何只有4.7V?工作电流与负载影响分析

5.1V稳压管输出为何只有4.7V?工作电流与负载影响分析

前几天调试一个简单的电源模块,用到了5.1V稳压管。电路接好,上电测试,万用表一量——输出居然只有4.7V。第一反应是稳压管坏了,换了一个新的,结果还是4.7V。这让我想起很多初学者都会遇到的困惑:明明标称5.…

2026/7/16 0:00:38阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/15 15:50:47阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/16 8:58:42阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/15 14:06:23阅读更多 →