MySQL 数据库运维学习笔记(Docker 实操 + Flask 迁移)
MySQL 数据库运维学习笔记(Docker 实操 + Flask 迁移)
日期:2026-07-06
学习方式:Docker 容器实操 + Note Center 从 SQLite 迁移到 MySQL
第一部分:Docker 基础
什么是 Docker?
Docker 是一个容器化平台,把软件连同它的运行环境打包成一个"镜像"(Image),一条命令就能启动。运行起来的镜像叫"容器"(Container)。
类比: 镜像 ≈ ISO 安装盘,容器 ≈ 装好系统的虚拟机。
安装 Docker(OpenCloudOS 9.4)
# 添加 Docker 官方源
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 安装 Docker 引擎和命令行工具
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 启动 Docker
systemctl start docker
# 验证
docker --version
docker info
Docker 核心命令
docker pull 镜像名 # 下载镜像
docker run -d --name 容器名 镜像名 # 后台运行容器
docker ps -a # 查看所有容器
docker exec -i 容器名 命令 # 在容器内执行命令
docker images # 查看本地镜像列表
端口映射
-p 宿主机端口:容器端口 — 把容器内的端口暴露到宿主机。
例如:-p 3306:3306 让宿主机能通过 127.0.0.1:3306 访问容器里的 MySQL。
第二部分:MySQL 安装与部署(Docker)
# 拉取 MySQL 8.0 镜像
docker pull mysql:8.0
# 启动 MySQL 容器
docker run -d \
--name mysql-demo \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=root123 \
mysql:8.0
参数说明
| 参数 | 含义 |
|---|---|
-d |
后台运行 |
--name mysql-demo |
容器名字 |
-p 3306:3306 |
端口映射 |
-e MYSQL_ROOT_PASSWORD=root123 |
设置 root 密码 |
验证连接
# 查看容器状态
docker ps -a --filter name=mysql-demo(过滤条件) --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"(自定义输出格式。--format 用 Go 模板语法,table 开头表示带表头:\t 是制表符,用来对齐列)
# 在容器内执行 MySQL 命令
docker exec -i mysql-demo mysql -uroot -proot123 -e "SHOW DATABASES;"
docker exec 全链路解析
docker exec -i mysql-demo mysql -uroot -proot123 -e "SHOW DATABASES;"
| 分段 | 作用 |
|---|---|
docker exec |
钻进容器执行命令 |
-i |
保持交互 |
mysql-demo |
目标容器名 |
mysql |
容器里的 MySQL 客户端程序 |
-uroot -proot123 |
用 root 登录,密码 root123 |
-e "SQL语句" |
执行一次 SQL 后就退出 |
整条命令的流程: 宿主机 → Docker 引擎 → 容器 → mysql 客户端 → 连接到容器内的 mysqld → 执行 SQL → 返回结果。
用户疑问:为什么不是用 SELECT * FROM ops_db?
因为 SELECT 后面只能跟表名,不能跟数据库名。数据库相当于文件夹,表才是文件。正确写法是 SELECT * FROM ops_db.servers(库名.表名)或先 USE ops_db; 再 SELECT * FROM servers;。
用户疑问:输出结果只有列名没有内容?
SHOW DATABASES; 的结果中,information_schema、mysql、performance_schema、sys 这些就是内容——它们是 MySQL 自带的系统数据库的名字。列名叫 Database,下面每一行都是一个数据库的实际名称。
用户疑问:ops_db 是什么时候创建的?
在第三课实操中通过 CREATE DATABASE IF NOT EXISTS ops_db 创建的。它是一个练习用的"沙盒"数据库,后续的建表、插数据、权限管理、备份恢复都在里面操作。
第三部分:MySQL 基础概念
MySQL 是什么?
MySQL 是一个数据库管理软件(服务程序叫 mysqld),一直后台运行监听 3306 端口,接收 SQL 命令来读写数据。
对比 SQLite vs MySQL
| SQLite | MySQL | |
|---|---|---|
| 运行方式 | 嵌入在程序里,直接读写文件 | 独立服务程序 |
| 如何连接 | 直接打开 .db 文件 | mysql -u用户 -p密码 -h地址 |
| 用户权限 | 无 | 有 |
| 多人同时用 | 不行(写锁) | 支持 |
| 适用场景 | 开发环境、小工具 | 生产环境 |
| 端口 | 无 | 3306 |
层级结构
MySQL 服务器
└── 数据库(Database) ← 像 Excel 文件
└── 表(Table) ← 像 Excel 里的 Sheet
├── 列(Column)← 像 A/B/C 列(字段名)
└── 行(Row) ← 一条数据记录
数据类型
| 类型 | 说明 | 示例 |
|---|---|---|
| INT | 整数 | cpu_cores INT |
| VARCHAR(N) | 变长字符串,最长 N 字符 | hostname VARCHAR(64) |
| DECIMAL(M,D) | 精确小数,M 总位 D 小数位 | memory_gb DECIMAL(5,2) |
| ENUM('a','b') | 枚举,只能选列出的值 | status ENUM('running','stopped') |
| DATETIME | 日期时间 | created_at DATETIME |
常用约束
| 约束 | 作用 |
|---|---|
| NOT NULL | 必填,不能为空 |
| DEFAULT 值 | 没填时自动补这个值 |
| PRIMARY KEY | 主键,唯一标识每一行 |
| AUTO_INCREMENT | 自动递增 |
| COMMENT '说明' | 字段注释 |
CRUD 四大操作
-- 查
SELECT * FROM 库名.表名;
SELECT 列1, 列2 FROM 表名 WHERE 条件;
-- 增
INSERT INTO 表名 (列1, 列2) VALUES (值1, 值2);
-- 改(一定要写 WHERE!)
UPDATE 表名 SET 列=新值 WHERE 条件;
-- 删(一定要写 WHERE!)
DELETE FROM 表名 WHERE 条件;
运维金句: 没有 WHERE 的 UPDATE 和 DELETE 就是删库跑路。
第四部分:用户权限管理
为什么不能都用 root?
生产环境需要最小权限原则——每个用户只给够用的权限。
用户由两部分组成
'用户名'@'主机'
| 写法 | 含义 |
|---|---|
| 'u'@'localhost' | 只能从本机连 |
| 'u'@'%' | 任何地方都能连 |
| 'u'@'192.168.1.%' | 只允许内网段 |
常用命令
CREATE USER '用户名'@'主机' IDENTIFIED BY '密码';
GRANT 权限 ON 库.表 TO '用户'@'主机';
SHOW GRANTS FOR '用户'@'主机';
实操:创建只读用户
CREATE USER 'dev_zhou'@'localhost' IDENTIFIED BY 'Monitor@2024';
GRANT SELECT ON ops_db.* TO 'monitor'@'localhost';
-- 验证:可以 SELECT,但 DELETE 被拒绝
加权限
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'dev_zhou'@'localhost';
第五部分:mysqldump 备份与恢复
备份
docker exec mysql-demo mysqldump -uroot -proot123 ops_db > backup.sql
备份文件的内容
备份文件包含 CREATE TABLE(建表结构)+ INSERT INTO(插数据)的 SQL 命令。
恢复
docker exec -i mysql-demo mysql -uroot -proot123 ops_db < backup.sql
备份 vs 恢复对比
| >(备份) | <(恢复) | |
|---|---|---|
| 方向 | 数据库 → 文件 | 文件 → 数据库 |
| 程序 | mysqldump | mysql 客户端 |
第六部分:慢查询排查与索引优化
为什么 SQL 会慢?
根本原因是 MySQL 需要扫描太多数据才能找到你要的结果。
索引是什么?
索引就像字典的拼音目录——排好序的数据结构,让 MySQL 能用二分查找快速定位,而不是逐行扫描全表。
EXPLAIN 只看三个指标
docker exec mysql-demo mysql -uroot -proot123 -e "USE ops_db; DESC test_slow; SHOW INDEX FROM test_slow;" 2>&1
| 指标 | 好的信号 | 坏的信号 |
|------|---------|---------|
| type | ref(索引查找) | ALL(全表扫描) |
| rows | 个位数 | 几万以上行数 |
| key | 有索引名 | NULL(没用索引) |
加索引
CREATE INDEX idx_name ON 表名 (列名);
-- 只给经常出现在 WHERE 条件里的列加索引
EXPLAIN SELECT * FROM test_slow WHERE username = 'user_100';
慢查询日志
docker exec mysql-demo mysql -uroot -proot123 -e "SHOW VARIABLES LIKE 'slow_query_log_file'; SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time';" 2>&1
docker exec mysql-demo cat /var/lib/mysql/ff129f65a5fa-slow.log | tail -50(MySQL 跑在 Docker 容器里,宿主机 /var/lib/mysql 下没有文件)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
第七部分:Flask + MySQL 迁移
整体流程
① 在 MySQL 里创建 note_center 库和用户 nc_app
② pip install pymysql(Python 连 MySQL 的驱动)
③ 改 config.py(加 MySQL 连接配置)
④ 改 database.py(pymysql 替换 sqlite3,建表语句适配 MySQL 语法)
⑤ 改 models.py(? → %s,FTS5 → FULLTEXT,NULLS LAST 处理)
⑥ 改 monitor.py + routes.py + app.py
⑦ 启动测试
核心语法差异
| SQLite | MySQL |
|---|---|
| INTEGER PRIMARY KEY AUTOINCREMENT | INT AUTO_INCREMENT PRIMARY KEY |
| REAL | DOUBLE |
| TEXT NOT NULL DEFAULT '' | 不支持,改为 VARCHAR 或无 DEFAULT |
| INSERT OR IGNORE | INSERT IGNORE |
| ORDER BY col ASC NULLS LAST | ORDER BY (col IS NULL) ASC, col ASC |
| ? 参数占位符 | %s 参数占位符 |
迁移前后
改之前:Note Center → SQLite 文件 (data/notes.db)
改之后:Note Center → MySQL 8.0 容器 (mysql-demo:3306)
启动顺序(机器重启后)
docker start mysql-demo # 先启动 MySQL
python3 app.py # 再启动 Note Center
学习收获总结
| 领域 | 掌握内容 |
|---|---|
| Docker | 安装、镜像/容器概念、核心命令、端口映射 |
| MySQL 基础 | 数据库结构、数据类型、约束、CRUD |
| MySQL 权限 | 创建用户、授权、最小权限原则 |
| MySQL 备份 | mysqldump 备份与恢复 |
| 索引优化 | EXPLAIN 分析、CREATE INDEX、慢查询日志 |
| 项目迁移 | SQLite → MySQL 全流程、语法差异处理 |