SQL 注入
攻击原理
用户在表单或 URL 输入恶意的 SQL 片段,例如:
用户名: abcd' OR 1=1;--
密码: 1234
拼出的 SQL 变成:
SELECT * FROM t_student
WHERE NAME = 'abcd' OR 1=1;-- ' AND PASSWORD = '1234';
-- 之后都被注释,OR 1=1 让条件恒为真,绕过密码验证。
防护:PreparedStatement(参数化查询)
原理:SQL 语句不再”先拼接再编译”,而是先发到 MySQL 预编译,再回传参数。用户提交的内容被解析为”值”,不会参与 SQL 拼接。
// JDBC 正确写法
String sql = "SELECT * FROM user WHERE name = ? AND pwd = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
MyBatis 中的两个语法
<!-- #{} 使用 PreparedStatement(推荐,防注入) -->
<select id="findByName" resultType="User">
SELECT * FROM user WHERE name = #{name}
</select>
<!-- ${} 直接拼接 SQL(不防注入,仅用于动态表名/列名) -->
<select id="findByTable" resultType="User">
SELECT * FROM ${tableName} ORDER BY ${column}
</select>
| 写法 | 防注入 | 使用场景 |
|---|---|---|
#{xxx} | ✓ | 99% 场景,参数值 |
${xxx} | ✗ | 仅动态表名/列名(FROM 后、ORDER BY 后) |
⚠️
${}在拼接用户输入的字段/表名时必须做白名单校验,否则就是 SQL 注入漏洞。
MySQL 8.0 新特性
服务器功能
- 元数据数据字典化:所有表的元数据信息不再存在
.frm文件,统一存到新数据字典 - 系统表全部使用 InnoDB + 独立表空间
- 资源管理组:可限制特定 SQL 占用的 CPU 资源
- 不可见索引 / 降序索引:语法层增强,直方图优化让优化器估算更准
- 窗口函数:终于支持
ROW_NUMBER() OVER (PARTITION BY ...)、LAG()、LEAD()等 - 在线修改全局参数持久化:
SET PERSIST xxx直接写配置文件
用户和安全
- 默认认证插件改为
caching_sha2_password(比mysql_native_password更安全) - 角色(Role)支持:可将权限授予角色,再把角色授予用户
- 密码历史记录:限制重复使用最近 N 次密码
InnoDB
- DDL 原子性:InnoDB 的 DDL 不再因 MySQL 崩溃而部分执行
- 在线修改 UNDO 表空间
- 监控视图:
INNODB_CACHED_INDEXES等视图监控表状态(锁、内存使用) innodb_dedicated_server:在只跑 MySQL 的服务器上开启,自动调优 buffer pool、log 文件等参数
三大发行版对比
| 维度 | MySQL 官方版 | Percona MySQL | MariaDB |
|---|---|---|---|
| 引擎 | InnoDB | XtraDB(XtraDB 完全兼容 InnoDB) | XtraDB(5.5 起) |
| 兼容性 | 官方基线 | 与 MySQL 完全兼容 | 与 MySQL 大部分兼容 |
| 监控工具 | 社区版不提供(仅企业版有) | Percona Monitor(免费) | Monyog |
| 主从复制 | 基于日志点 + GTID + MGR | 基于日志点 + GTID + PXC | 基于日志点 + GTID + Galera Cluster |
| 高可用集群 | MGR(多主) | MGR + PXC | Galera Cluster(PXC 风格) |
| 最新版本 | 跟进最快 | 略落后 | 较激进 |
| 来源 | Oracle 维护 | Percona 公司维护 | MySQL 创始人在 Oracle 收购后另起炉灶 |
选型建议
- 追求生态稳定 → 官方 MySQL
- 需要免费监控 + 备份工具 → Percona MySQL(推荐生产环境用)
- 想要新特性激进 + 可替代 Oracle MySQL → MariaDB
⚠️ 坑点:MariaDB 的 GTID 日志格式与 MySQL 不兼容,不能作为 MySQL 的从库(反之可以)。
版本升级方法论
Step 1:分析升级收益
- 是否能解决业务痛点(性能、并发、新特性需求)?
- 是否能解决运维痛点(监控、安全、备份)?
Step 2:分析升级风险
- 驱动版本兼容:项目里 MySQL Connector/J 版本是否支持新版本服务端
- 默认值变化:新版本某些字段默认值可能不同(如
sql_mode调整) - 字符集变化:8.0 默认
utf8mb4,老库可能是utf8 - 保留字变化:升级前需检查新版本保留字
Step 3:制定方案
1. 评估受影响的业务系统
2. 制定详细的升级步骤
3. 备份数据库(mysqldump + 物理备份)
4. 升级 Slave → 主从切换 → 升级原主
5. 制定回滚方案(出问题能切回)
Step 4:执行(推荐流程)
1. 升级从库 1 → 验证无问题
2. 升级从库 2 → 验证无问题
3. 手动主从切换(让从库 1 变主)
4. 升级原主
5. 持续观察 24~48h
数据类型选型
字符串
CHAR(10) -- 定长,最多 255 字符
VARCHAR(255) -- 变长,除内容外多 1~2 字节存长度
| 场景 | 推荐 |
|---|---|
| 长度固定(身份证号、MD5) | CHAR |
| 长度差异大(昵称、地址) | VARCHAR |
时间
| 类型 | 存储 | 范围 | 时区 | 字节 |
|---|---|---|---|---|
DATETIME | 与时区无关 | 1000-01-01 ~ 9999-12-31 | 无 | 8 |
TIMESTAMP | 时间戳(1970 起秒数) | 1970-01-01 ~ 2038-01-19 | 依赖时区 | 4 |
⚠️ TIMESTAMP 的 2038 问题:4 字节秒数将在 2038-01-19 溢出,建议新业务统一用 DATETIME。
全文索引
ALTER TABLE t_article ADD FULLTEXT INDEX ft_title (title);
SELECT * FROM t_article WHERE MATCH(title) AGAINST('小米');
为什么生产环境不推荐用 MySQL 全文索引
- 中文分词差:词结果太多且不准确,占用大量存储
- 不实时更新:字段内容改了,全文索引不更新,需定期重建
- 集群中维护成本高:主从同步全文索引很麻烦
- 准确率低:不如专业搜索引擎
替代方案
- Elasticsearch(推荐,分布式 + 中文分词 + 实时)
- HanLP(中文分词库,配合 MySQL 自己做倒排索引)
- Sphinx / Manticore Search
更优秀的数据库
如果业务场景允许,可以考虑更现代的数据库:
| 数据库 | 优势 | 集群方案 |
|---|---|---|
| PostgreSQL | 开源、稳定、功能强大(JSONB、GIS、CTE 完整) | Postgres-XL(OLTP)、GreenPlum(OLAP) |
PostgreSQL 近年发展极快,在 SQL 标准兼容性、复杂查询优化上普遍优于 MySQL。但 MySQL 的生态(运维工具、ORM 支持、问题排查资料)仍占优势。
写在最后
整个 MySQL 知识系列从架构→索引→锁→日志→事务→实战,覆盖了 DBA 和后端开发日常需要的核心知识。再看一组真实生产环境的 QPS/TPS 监控截图(单机 64 核 512G 内存,峰值 QPS 37 万、TPS 5 万):

能否扛住这种量级,依赖的正是这一系列文章所讲的:
- 架构:可扩展的存储引擎 + 合理的连接池
- 索引:覆盖索引 + 避免全表扫描
- 锁:最小化锁粒度 + 避免间隙锁滥用
- 日志:合理刷盘策略 + 主从延迟监控
- 事务:隔离级别选型 + 分布式事务方案
- 实战:慢查询治理 + 版本升级方法论
希望对你有所帮助。