9 秒,生产库没了
最近鸭鸭刷到一个挺吓人的 AI 事故。
不是 AI 写错了一个函数,也不是代码 review 漏了一个边界。
而是据多家媒体报道,一家海外创业公司的生产数据库,被 AI Agent 在 9 秒内删掉了。
更离谱的是,连备份也一起没了。

公开报道里大概是这么个过程:
公司创始人在测试环境里让 Cursor Agent 处理一个常规运维任务。Agent 遇到凭据不匹配,没有停下来问人,而是自己去代码库里翻 token。
它翻到了一个 Railway API token。
然后直接发了删除卷的 GraphQL mutation。
9 秒后,生产数据库没了。
创始人再去找备份,发现卷级备份和源数据在同一个卷里。卷删了,备份也跟着没了。最近能用的恢复点,是 3 个月前。
说实话,鸭鸭看到这里第一反应不是“AI 好可怕”。
是“这套权限设计也太敢了”。
……
评论区分成两派。
一派会说:看吧,AI 不能信,生产环境绝对不能让 Agent 碰。
另一派会说:这锅不该全甩 AI,token 权限太大、备份没隔离、删除没二次确认,这些才是根因。
鸭鸭更偏第二派。
这件事最值得程序员警惕的,不是 AI 会不会发疯。
而是我们正在把一个会猜、会搜索、会执行命令的东西,接进以前只有高级工程师才敢碰的生产权限里。
以前新人实习生要删库,至少得有人把账号给他、把命令教他、把生产环境入口打开。
现在不一样了。
Agent 能自己翻代码、自己找 token、自己拼 API、自己执行。
它不需要恶意。它只要“以为自己理解了”,就够吓人了。
所以鸭鸭觉得,这件事真正的独占视角是:
AI Agent 不是新同事,它更像一个执行速度极快、但没有责任感的运维账号。
你不能只问它聪不聪明。
你得问它能碰什么、不能碰什么、碰危险东西时谁来按确认键。
但这次删库事故补上了另一半:
AI 写代码只是第一阶段。
AI 开始执行生产操作,才是真正考验工程体系的时候。
以前我们担心的是:
“AI 写的代码有没有 bug?”
以后我们更该担心的是:
“AI 拿着生产权限的时候,会不会把 bug 直接变成事故?”
这中间差的不是模型能力,是工程护栏。
鸭鸭觉得,后面公司用 AI Agent,至少要有几个硬规矩:
- 生产权限必须最小化:管理域名的 token,就不应该有删数据库卷的能力。
- 破坏性操作必须人工确认:删库、删卷、清缓存、重建索引,这种动作不能靠提示词自觉。
- 备份必须物理隔离:备份和源数据放在同一个可删除对象里,本质上就不叫备份。
- Agent 操作必须可审计:谁授权的、它执行了什么、为什么执行,事后要能追。
这里面每一条,都不是 AI 时代才出现的新知识。
权限隔离、二次确认、灾备演练、操作审计,这些都是老掉牙的工程常识。
只是 AI Agent 把这些常识的重要性放大了。
因为人类工程师可能会犹豫,会问一句“这个真的是生产吗?”
Agent 不一定会。
它可能 9 秒就把你三个月的数据带走。
所以如果面试官问:
“AI 都能帮你运维了,后端工程师还剩什么价值?”
鸭鸭会这么答:
“写命令的价值变低了,决定哪些命令永远不能被随便执行的价值变高了。”
这句话听着有点绕,但特别现实。
未来真正值钱的程序员,不是会不会让 AI 多写几行代码,而是能不能给 AI 设计边界。
让它快,但不能让它乱来。
让它能干活,但不能让它拿 root 权限赌运气。
大家怎么看?你敢让公司的 AI Agent 碰生产环境吗?
……
今天鸭鸭和大家分享一道后端场景题面试题。
【在 Java 中,不使用锁如何实现一个线程安全的单例? 】
回答重点
不用锁指的是不能用 synchronized、Lock 这些显式加锁的手段,但可以利用 JVM 自身的机制或者 CAS 原子操作来保证线程安全。
常见的实现方式有四种:
- 静态内部类:利用类加载机制,延迟加载且线程安全
- 枚举单例:JVM 保证线程安全,天然防反射破坏
- 饿汉式:类加载时直接初始化,简单粗暴
- CAS 无锁:用原子操作 AtomicReference 实现

推荐优先用静态内部类,既有延迟加载又不需要显式加锁,性能也好。枚举单例适合需要防御反射攻击的场景,比如配置中心的单例类。
扩展知识
静态内部类
这是工业界最常用的方式,核心原理是利用 JVM 的类加载机制。JVM 保证类的初始化过程是线程安全的,只会执行一次。
内部类 Holder 只有在第一次调用 getInstance() 时才会被加载,所以实现了延迟加载,不会像饿汉式那样类一加载就创建实例。
执行流程:
- 第一次调用 getInstance() 时,触发 Holder 类加载
- JVM 执行 Holder 的 方法,初始化 INSTANCE
- JVM 保证 只执行一次且线程安全
- 后续调用直接返回已初始化的实例

代码实现:
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE; // 首次调用时加载 Holder 类并初始化实例
}
}
优点是既有延迟加载,又不需要显式加锁,性能开销几乎为零。唯一的缺点是无法防止反射破坏,如果有人通过反射调用私有构造器,还是能创建多个实例。
枚举单例
JVM 在加载枚举类时,会保证每个枚举常量只实例化一次,实例化过程由虚拟机内部控制,天然线程安全。更重要的是,枚举单例可以防止反射和序列化破坏,因为 JVM 层面就禁止了对枚举构造器的反射调用。
代码实现:
public enum Singleton {
INSTANCE; // 唯一实例
public void doSomething() {
// 业务逻辑
}
}
这是《Effective Java》里推荐的最佳实践。枚举单例不仅线程安全,还能防止反序列化时创建新对象,因为 JVM 在反序列化枚举时会直接返回已有的枚举常量,不会调用构造器。
缺点是不支持延迟加载,类一加载就会创建实例。如果单例对象很大或者初始化很耗时,可能会拖慢应用启动速度。
饿汉式
最简单的方式,类加载时直接初始化实例,依赖 JVM 类加载机制保证线程安全。
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
优点是实现简单,没有任何并发问题。缺点是不支持延迟加载,类一加载就创建实例,如果这个单例一直没被用到,就白白浪费了内存。适合小项目或者单例对象很轻量的场景。
CAS 无锁
通过原子操作 AtomicReference 实现无锁竞争,多个线程同时调用 getInstance() 时,只有一个线程能成功 CAS 设置实例,其他线程会继续循环重试。
代码实现:
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
private Singleton() {}
public static Singleton getInstance() {
while (true) {
Singleton instance = INSTANCE.get();
if (instance != null) {
return instance;
}
instance = new Singleton();
if (INSTANCE.compareAndSet(null, instance)) {
return instance;
}
}
}
}

这种方式的问题是 while(true) 会导致 CPU 空转,在高并发场景下可能有多个线程同时创建实例,虽然最终只有一个实例会被设置成功,但其他线程创建的实例会被丢弃,浪费了内存和 CPU。
实际项目中几乎不用这种方式,因为静态内部类已经足够好了。CAS 单例只在一些对性能要求极高、不能接受任何锁开销、且资源充足的场景下才会考虑。
为什么静态内部类是首选
静态内部类结合了饿汉式的线程安全和懒汉式的延迟加载,是目前工业界最常用的方案。原因是:
1)利用 JVM 类加载机制保证线程安全,不需要显式加锁,性能开销几乎为零。
2)Holder 类只有在第一次调用 getInstance() 时才会被加载,实现了延迟加载,避免了饿汉式的资源浪费。
3)实现简单,代码清晰,不容易出错。
4)没有 CAS 方式的 CPU 空转和内存浪费问题。
唯一需要注意的是,如果单例类需要防止反射破坏,就得用枚举单例。比如配置中心的单例类,如果被反射创建了多个实例,可能导致配置不一致。但大多数场景下,反射破坏是一个理论上的问题,实际开发中很少有人会故意用反射去破坏单例。
如何防止反射破坏
除了枚举单例天然防反射,其他方式都可以通过在构造器里加标志位来防御:
public class Singleton {
private static volatile boolean initialized = false;
private Singleton() {
synchronized (Singleton.class) {
if (initialized) {
throw new RuntimeException("单例已被创建,禁止反射调用");
}
initialized = true;
}
}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
这样如果有人通过反射调用构造器,第二次调用时会抛出异常,阻止创建多个实例。不过这种防御手段也不是绝对安全的,如果攻击者先通过反射修改 initialized 标志位,还是能绕过检查。所以需要真正安全的单例,还是得用枚举。
篇幅有限,更多 后端场景 相关面试题可以进入面试鸭(mianshiya.com)进行查阅。