以为是存储引擎的锅,结果是两天前我亲手敲的
chown。
先说结论
如果你在 Docker 部署的 MySQL 里遇到:
- 查询正常,建表报错
1030 (168) - 磁盘空间充足,SQL 语法没问题,没有残留文件
第一反应别查 MySQL,先查目录权限。
大概率是你在宿主机上 chown 了挂载目录,导致宿主机 UID 和容器内 UID 不匹配,容器里的 MySQL 用户"能读不能写"。
问题现场
环境:Ubuntu + Docker 部署 MySQL,数据挂载在宿主机 /opt/resumeai/docker/resumeai/mysql/data。
执行建表 SQL,报错:
css
[HY000][1030] Got error 168 - 'Unknown (generic) error from engine' from storage engine
诡异的是:SELECT 正常,已有表的数据都能查,就是不能建新表。
排查过程
第一步:磁盘满了?
bash
df -h # 使用率 30%,空间充足
df -i # inode 使用率 9%,也没问题
排除。
第二步:SQL 语法有问题?
同样的 SQL 在本地环境跑得通,表名也无特殊字符。
排除。
第三步:有残留的物理文件冲突?
bash
ls /opt/resumeai/docker/resumeai/mysql/data/<库名>/ | grep release_note
# 无输出
没有残留的 .ibd 文件,排除。
第四步:看容器日志
bash
docker logs --tail 50 <容器名>
日志里出现了 Permission denied 相关的报错。方向变了------这不是 MySQL 的问题,是操作系统权限的问题。
关键转折:为什么"能读不能写"?
把两个操作拆开看:
| 操作 | 本质上在做什么 | 需要什么权限 |
|---|---|---|
SELECT 查询 |
读取已有的 .ibd 文件 |
文件读权限(r) |
CREATE TABLE 建表 |
在目录下创建新的 .ibd 文件 |
目录写权限(w) |
当时目录和文件的权限是这样的:
- 文件:
644(-rw-r--r--)→ 其他人有读权限,所以SELECT能跑 - 目录:
755(drwxr-xr-x)→ 只有所有者能写,其他人没写权限,所以CREATE TABLE失败
那谁是"其他人"?这就是根因。
根因确认
回忆了一下最近做过什么操作------为了省去每次敲 sudo 的麻烦,我执行了:
bash
sudo chown -R ubuntu:ubuntu /opt/resumeai
这一行命令,把整个数据目录的所有者改成了 ubuntu 用户(UID 1000)。
但 Docker 容器里 MySQL 官方镜像的 mysql 用户 UID 是 999。
Linux 判断权限不看用户名,看的是数字 UID。
容器内的 MySQL 进程(UID 999)到了宿主机目录(所有者 UID 1000)面前,被当成了一个陌生的"其他人"------有读权限,没有写权限。
这就是"能查不能建"的真正原因。
解决方案
bash
# 1. 先确认容器内 mysql 用户的 UID
docker exec -it <容器名> id -u mysql
# 输出 999
# 2. 把数据目录改回容器内 MySQL 的 UID
sudo chown -R 999:999 /opt/resumeai/docker/resumeai/mysql/data
# 3. 重启容器
docker restart <容器名>
重新执行建表 SQL,通过。
如果你的 MySQL 镜像不是官方的(比如 MariaDB 或定制镜像),UID 可能不是 999,先查再改:
bash
DOCKER_UID=$(docker exec <容器名> id -u mysql)
sudo chown -R ${DOCKER_UID}:${DOCKER_UID} /path/to/data
这件事教会我的
Linux 权限的 3 个核心认知
1. Linux 认的是数字 UID,不是用户名。
宿主机上的 ubuntu(UID 1000)和容器里的 mysql(UID 999),在系统眼里就是两个完全不同的人。就算你把容器里的用户也改成叫 ubuntu,UID 不同照样没权限。
2. 目录权限 ≠ 文件权限。
文件权限控制的是"能不能读/写这个文件的内容";目录权限控制的是"能不能在这个目录下创建、删除、重命名文件"。两者是独立的。
3. Docker 挂载卷的权限,由宿主机文件的 UID 决定。
容器内进程看到的权限,不是容器自己定义的,而是宿主机上那个文件/目录的 UID/GID 决定的。这是 Docker 数据卷最容易踩的坑。
以后怎么避免
不想每次输 sudo?正确做法是配免密,不是动目录权限:
ini
sudo visudo
# 添加:
ubuntu ALL=(ALL) NOPASSWD: /usr/bin/docker
这样既能免密执行 docker 命令,又不会破坏数据目录的权限体系。
Docker 数据卷权限的黄金法则:
容器内进程的 UID = 宿主机数据目录所有者的 UID
记住这一条,90% 的 Docker 权限问题都不会遇到。
总结
| 维度 | 内容 |
|---|---|
| 问题 | MySQL 建表报 1030,但查询正常 |
| 根因 | chown 改了目录 UID,与容器内 MySQL UID 不匹配 |
| 解法 | 把目录所有者改回容器内 UID(通常 999) |
| 教训 | 不要随意 chown Docker 挂载目录;用 sudo 免密替代 |
一个小时的排查,根源就是半年前我亲手敲的一行 chown。越是"我当时觉得没问题"的操作,越可能是后来问题的根源。
希望这篇帮你少走一次弯路。
如果这篇对你有帮助,点个赞再走呗 🙏 有 Docker 权限相关的踩坑经历,评论区聊聊?