直接结论:
会加锁,但扩大 VARCHAR 长度持锁时间极短(毫秒级),不会卡住业务。
为什么扩大长度不重写表?
加锁(AccessExclusiveLock) → 改系统表元数据 → 释放锁
整个过程毫秒级完成
VARCHAR(n) 的长度限制只是系统表里的一个数字 ,底层存储和 TEXT 完全一样。
PostgreSQL 只修改系统表 pg_attribute 中的 atttypmod 元数据,不碰实际数据行,上亿数据也几乎是秒级完成。
不同操作对比
| 操作 | 重写表 | 耗时 |
|---|---|---|
| VARCHAR 扩大长度 | ❌ 不重写 | 毫秒 ✅ |
| VARCHAR 缩小长度 | ✅ 重写 | 很慢 ❌ |
| 改为其他类型(INT等) | ✅ 重写 | 很慢 ❌ |
| 改为 TEXT | ❌ 不重写 | 毫秒 ✅ |
什么情况会"卡住"?
不是 DDL 本身慢,而是等锁排队:
- 表上有未提交的长事务 → ALTER 一直等,后续所有查询全部堆积
- 字段有函数索引 → 触发索引重建
- 有 CHECK 约束 → 全表扫描重新验证
生产环境必做
sql
-- 加锁超时保护,超时自动退出,不影响业务
SET lock_timeout = '3s';
ALTER TABLE big_table ALTER COLUMN your_col TYPE VARCHAR(500);
-- ✅ 3秒内完成 → 安全
-- ❌ 报 timeout → 有长事务占锁,换低峰期重试
最稳方案(AI 推荐,我不推荐)
sql
-- 第一步:改为 TEXT,毫秒完成,不重写表
ALTER TABLE big_table ALTER COLUMN your_col TYPE TEXT;
-- 第二步:加长度约束,NOT VALID 跳过存量数据验证
ALTER TABLE big_table
ADD CONSTRAINT chk_col_len
CHECK (char_length(your_col) <= 500) NOT VALID;
-- 第三步:低峰期验证存量数据,不阻塞读写
ALTER TABLE big_table VALIDATE CONSTRAINT chk_col_len;
一句话总结:
扩大 VARCHAR 长度本身不重写表,毫秒级完成; 危险在于等锁期间请求堆积雪崩,生产操作必须加 lock_timeout 保护
最后推荐操作: 谁阻塞干谁
sql
-- 第一步:找出阻塞方 查看谁在阻塞我的 ALTER
SELECT
blocking.pid AS 阻塞方PID,
blocking.query AS 阻塞方SQL,
blocking.state AS 阻塞方状态,
blocked.pid AS 被阻塞PID,
blocked.query AS 被阻塞SQL,
now() - blocking.query_start AS 阻塞持续时长
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.wait_event_type = 'Lock';
-- 第二步:干掉阻塞方PID
SELECT pg_terminate_backend(阻塞方PID);
-- 第二步:重新执行
ALTER TABLE your_table ALTER COLUMN your_col TYPE VARCHAR(500);