ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

数据库核心知识:从存储原理到SQL优化与安全防御

数据库核心知识:从存储原理到SQL优化与安全防御 数据库这名字听着唬人但拆开看其实特别朴素——它就是个存数据的地方。真正让数据库和 Excel 拉开差距的是它背后那一整套关于“怎么存、怎么取、怎么保证不错不乱”的规则。我这些年带过不少新人发现很多人一上来就背 SQL 语法SELECT、INSERT 用得飞起但真要问他“为什么这张表建了索引查询反而变慢”“为什么 count(*) 和 count(1) 结果不一样”就开始含糊了。这篇文章我想从最底层的数据存储逻辑讲起一路打通 SQL 分类、索引原理、慢查询优化再到注入防御和面试题基本覆盖你从入行到独立干活需要搞清楚的那点事儿。适合刚学完基础语法、想系统梳理数据库知识体系的读者也适合准备面试前回头补课的朋友。1. 先建立直觉数据库到底在解决什么问题1.1 关系型数据库的底层逻辑你完全可以想象数据库是一排排带编号的文件柜每个柜子是一张表柜子里的每个抽屉是一行记录每个抽屉里分了几格分别放姓名、年龄、手机号。关系型数据库RDBMS的核心就是用“二维表”这种结构来描述现实世界里的实体和实体之间的关系。你有一个用户表一个订单表订单表里存了 user_id通过这个字段就能把“谁买了什么东西”串起来这就是“关系”两个字的本意。那为什么这种结构能统治数据库世界几十年因为它天生契合业务场景。绝大多数企业的核心数据——订单、库存、账目、会员——都是强结构化的每条记录字段固定、类型明确、依赖关系清晰用表格存就是最自然的选择。MySQL、PostgreSQL、SQL Server、Oracle还有国产的人大金仓、达梦都属于这个阵营。它们共享同一套理论根基表、行、列、主键、外键、索引、事务、ACID。这里有个关键认知要建立数据库的价值不在“存”而在“查”。一张表存一万条数据和存一万个 Excel 文件如果只考虑离线保存差别其实不大。真正的差异是——当你要从几百万行里找出“上个月下单超过三次的用户”时关系型数据库通过索引、查询优化器、执行计划这套机制能把这个操作从分钟级压缩到毫秒级。这就是它存在的意义。1.2 数据库的世界里不只有关系模型不过要只是关系型数据库一家独大今天这篇文章也不用写这么长。这几年你肯定听过 NoSQL、向量数据库、时序数据库这些词。它们不是来取代关系型数据库的而是来解决关系型数据库不擅长的问题。比如 TDengine做物联网和工业时序数据监控的每秒要写入几十万条设备上报数据按时间戳顺序追加写入查询也基本是“最近五分钟的平均温度”这种范型。你硬要用 MySQL 去扛不是不行但会非常吃力因为 MySQL 的 B 树索引和事务机制在这种高并发顺序写入场景下开销太大。还有向量数据库专门给 AI 应用做相似度检索用的它处理的是“哪句话和这句话意思最接近”这种非精确匹配查询。SQLite 则是嵌入式场景的王者就一个 .db 文件不需要安装服务端手机 App 本地缓存、浏览器存储都在用它。所以我现在看一个项目第一反应不是“用什么数据库”而是“这个业务的数据长什么样、怎么读写、对一致性要求多高”。关系型、时序型、文档型、向量型各管一摊组合使用才是常态。2. SQL 分类五大门类理清楚增删改查只是入门2.1 DDL 与 DML表结构的“图纸”和数据的“施工”SQL 按功能分成五类这个体系是面试高频但很多干了两年的人还是说不全。我按重要程度一个个过。DDLData Definition Language数据定义语言管的是表结构的生命周期。CREATE TABLE、ALTER TABLE、DROP TABLE、TRUNCATE TABLE全在这里面。这类语句有个特点执行后直接修改数据库的元数据影响的是“表长什么样”而不是“表里有什么”。我以前见过一个新手直接在生产库执行 DROP TABLE恢复花了两小时从那之后我定了个死规矩所有 DDL 操作必须经过审批且先在测试环境跑一遍。DMLData Manipulation Language数据操纵语言就是我们天天挂在嘴边的 INSERT、UPDATE、DELETE、SELECT 里的前三个——严格来说 SELECT 被归为 DQL但日常没人分这么细。DML 操作的是实际数据行是业务代码里最常用的语句。这里有个特别重要的点DML 操作是可以回滚的前提是在事务里且还没提交。而 DDL 在大多数数据库里执行后是没法回滚的——MySQL 的 DDL 内部会触发隐式提交这意味着你执行 ALTER TABLE 的那一刻前面所有未提交的事务都自动提交了。这个坑我踩过一次批量更新数据到一半想反悔结果发现之前的操作已经被连坐提交了。关于 DDL 还有个小细节值得记住TRUNCATE 和 DELETE 表面都是清空表但 DELETE 是逐行删除会记录行日志可以配合 WHERE 条件删一部分也能在事务里回滚TRUNCATE 是直接释放整个表的数据页速度快得多但是不可回滚也不触发删除触发器。所以清理大表用 TRUNCATE秒级完成业务上删数据必须用 DELETE 加 WHERE。2.2 DQL 的核心价值查询不只是 SELECTDQLData Query Language就一个关键字——SELECT但它是最值得花时间深挖的部分。SELECT 的完整执行顺序很多人不知道先 FROM 确定表再 WHERE 过滤原数据再 GROUP BY 分组再 HAVING 过滤分组结果再 SELECT 投影取列再 DISTINCT 去重最后 ORDER BY 排序LIMIT 截断。如果你脑子里没有这个执行链路写复杂嵌套查询时很容易写出逻辑错乱的 SQL。顺带讲一个几乎所有新人都会问的问题DISTINCT 和 GROUP BY 都能去重到底用哪个答案是能 GROUP BY 就别用 DISTINCT。DISTINCT 是对结果集做去重它必须把所有数据先捞出来才能判断重复GROUP BY 是先分组再聚合配合 COUNT、SUM 这类聚合函数能顺手统计信息。举个例子查订单表里所有出现过的用户 ID两种写法都对-- 写法一DISTINCT SELECT DISTINCT user_id FROM orders; -- 写法二GROUP BY可以同时带统计数量 SELECT user_id, COUNT(*) AS order_cnt FROM orders GROUP BY user_id;还有个更隐蔽的坑DISTINCT 去重是精确匹配判断不了“重复值里只保留一条”这种业务规则。比如你想去掉同一用户 ID 下的重复订单只保留最新一条DISTINCT 完全无能为力。这时候要用窗口函数。我在第四节里会专门给出去重实战的高阶写法。DQL 里还有个概念必须搞清楚SELECT 执行结果的“逻辑顺序”和“物理顺序”是两回事。不加 ORDER BY 时MySQL 返回的行顺序是存储引擎决定的——它可能看起来是按主键排的也可能不是。你永远不该依赖“默认顺序”做业务逻辑。这是我排查过很多次“为什么这段查询时灵时不灵”之后得出的血泪结论。2.3 DCL 与 TCL权限和事务看起来无用实则保命DCLData Control Language是管权限的GRANT、REVOKE。大多数开发同事觉得这是 DBA 的事自己碰不到。但你要有最起码的认知生产环境的数据库账号永远不该给一个超级权限的 root应用账号应该只拥有所在库的 SELECT、INSERT、UPDATE、DELETE 权限。前两年有个同事误操作删了线上的配置表检查完发现他用的是 root 连接因为本地开发环境一直这么干切到生产顺手也这么连了。这不是技术问题这是安全意识问题。TCLTransaction Control Language更核心——它管的是 COMMIT、ROLLBACK、SAVEPOINT。读这一节的人应该都知道事务有四大特性 ACID原子性、一致性、隔离性、持久性。但光知道名字没用你得理解为什么需要隔离性。两个事务同时改同一行数据到底听谁的数据库用锁和隔离级别解决这个问题。MySQL InnoDB 默认是 REPEATABLE READ可重复读在这个级别下事务内重复读同一行结果是稳定的但也因此可能产生幻读——事务 A 查询某个范围的记录事务 B 插入了新记录事务 A 再查时发现多了一行“幻影”。生产业务里如果需要防止幻读得用 SERIALIZABLE 级别或者加间隙锁。实操上最容易犯的错误是写了 UPDATE 或 DELETE 但忘了 COMMIT尤其在命令行客户端或者没开自动提交的代码里。我曾经见过一个服务因为事务没提交把某个热门订单表锁了十分钟线上告警响成一片。所以我的习惯是任何对数据产生修改的操作第一时间写 COMMIT或者用代码框架的声明式事务强制统一提交。3. 索引、事务与慢 SQL从“能跑”到“跑得快”3.1 为什么 SQL 会慢索引失效场景复盘查询慢百分之八十和索引有关。索引的原理说白了就是给表建一个排序好的“目录”InnoDB 里用的是 B 树。B 树的叶子节点通过双向链表连接所以按范围查“age BETWEEN 20 AND 30”非常快——定位到第一条然后沿着链表往后扫就行。但索引不是万能的很多操作用不上索引。我随手列几个高频失败场景都是我实际踩过或看别人踩过的对索引列做了函数运算或隐式类型转换WHERE DATE(create_time) 2024-01-01这种写法会让索引失效应该改成create_time 2024-01-01 AND create_time 2024-01-02。前置模糊查询LIKE %keyword%百分号在开头整个索引没法用LIKE keyword%就能走索引。联合索引没遵守最左前缀原则建了(user_id, status)联合索引只查status就跳过了 user_id索引用不上。对索引列做操作WHERE id 1 5这种会把索引废掉要改成WHERE id 4。排查慢 SQL 的第一件事永远是看执行计划。MySQL 里执行EXPLAIN SELECT ...输出结果里有几个关键字段要看type访问类型从好到差依次是 system、const、eq_ref、ref、range、index、ALL、key实际用到的索引、rows预估扫描行数。如果 type 是 ALL 且 rows 很大那就是全表扫描优化方向很明确加索引或者改写 SQL。3.2 慢 SQL 优化先定位再动手优化慢 SQL 的逻辑是“先定位再动手”上来就改 SQL 是乱开枪。生产环境开慢查询日志把执行时间超过 1 秒的语句捞出来-- MySQL 开启慢查询日志 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1; SET GLOBAL slow_query_log_file /var/log/mysql/slow.log;捞出来之后逐个分析。除了加索引之外最常见的几板斧是**避免 SELECT ***只取需要的列。这样能减少 IO 和网络传输如果覆盖了索引还能避免回表查询。分页优化LIMIT 100000, 20这种深分页越往后越慢因为数据库要扫前十万行再丢掉。优化方法是改成“基于上一页最后一条记录的主键继续往后取”WHERE id 100000 ORDER BY id LIMIT 20效率天差地别。JOIN 时小表驱动大表让小的结果集作为驱动表去匹配大的表配合正确的连接字段索引能大幅减少中间行数。避免在 WHERE 子句里做计算把可预计算的内容在应用层算好传给 SQL 做等值查询。还有一个容易忽略的优化点分析表数据分布考虑是否真的需要关系模型。比如一个日志表一天涨几百万行做报表时只要按时间聚合那用日志表加按月分区的方案就比在 MySQL 里硬扛合理得多。MySQL 8.0 对分区表有不少改进但要提前判断业务需求再设计分区键分区键选错等于白折腾。这里还是得提一句你在查找资料时可能总看到“sql server writelog”这个词。SQL Server 的 Write Log 机制先写事务日志再写数据页和 MySQL InnoDB 的 redo log WAL 是同一个思想——先把变更顺序地、低成本地记到日志文件再去异步刷新数据页。理解了这个你就能明白为什么数据库重启后不会丢数据为什么写入性能比直接改数据文件高。这套“先记日志再动手”的思路是数据库性能和可靠性之间的核心平衡点。3.3 事务隔离级别的实际场景说到事务我再展开一点隔离级别的实战。SQL 标准定义了四种隔离级别隔离级别脏读不可重复读幻读READ UNCOMMITTED可能可能可能READ COMMITTED不会可能可能REPEATABLE READ不会不会可能InnoDB 可部分避免SERIALIZABLE不会不会不会脏读是“读到别人没提交的数据”一旦对方回滚你读到的就是无效数据不可重复读是同一事务里两次读同一行结果不同因为别的已提交事务改了这行幻读是两次范围查询结果条数不同。实际业务怎么选多数互联网公司用 READ COMMITTED 或 REPEATABLE READ两者在并发性和一致性上相对平衡。如果你做金融对账那数据一致性优先级最高直接上 SERIALIZABLE用性能换正确性。我遇到过最典型的一个线上事故就是两个事务并发给同一个账户余额做扣减因为没控制好隔离和锁最终把余额扣成了负数。解决方案其实很简单——把“查询余额、校验、扣减”放到一个事务里并且对账户行加SELECT ... FOR UPDATE行锁问题立刻消失。这一行 FOR UPDATE 值一年工作经验真的。4. 实操手册常见数据库工具与高频操作4.1 从零搭建 MySQL 环境说不清为什么总有同事在自己电脑上装 MySQL 时装失败然后来问我。我给你们一套最省心的 Windows 本地安装法——用免安装压缩包。到 MySQL 官网下载 mysql-8.4.x 的 ZIP 包类似 mysql-8.4.11解压到比如D:\mysql-8.4.11-winx64。然后在这个目录下创建一个my.ini配置文件最简配置[mysqld] basedirD:/mysql-8.4.11-winx64 datadirD:/mysql-8.4.11-winx64/data port3306 character-set-serverutf8mb4用管理员身份打开命令行进入 bin 目录执行初始化命令mysqld --initialize-insecure--initialize-insecure会生成一个无密码的 root 账号适合本地开发--initialize则生成随机密码在日志里可以找到适合对安全要求高的场景。启动服务执行mysqld --console看到ready for connections就说明启动成功。另开一个终端执行mysql -u root就能进入。路径尽量不要带中文和空格字符集默认 utf8mb4这个一定要设不然表情符号和生僻字存储会乱码。这套流程我前后装了不下十次踩过的坑全是漏配 basedir、datadir 导致服务起不来或者路径带空格导致 my.ini 解析失败。4.2 导入导出与日常维护实操中的高频动作日常开发中最高频的三个操作是用 Navicat 导入 SQL 文件、把 Excel 数据入库、对脏数据去重。逐个说。Navicat 导入 SQL 文件。很多人直接双击 .sql 文件想打开那是错的。正确姿势是Navicat 里先建好目标数据库然后右键数据库选“运行 SQL 文件”选择你的 .sql 文件执行完成后检查日志。导入大文件时超过几百 MBNavicat 容易卡死我的建议是用命令行导入mysql -u root -p dbname file.sql快且稳。如果导入过程中报“2008 数据库存疑”类似 SQL Server 的数据库状态异常大多是文件损坏、日志不一致或者路径不对先检查物理文件是否完整再考虑重建或恢复。Excel 导入数据库。这也是常用操作用 Navicat 的“导入向导”选 Excel 文件按列映射到目标表字段。要注意Excel 第一行如果放的是中文表头要选“忽略第一行”日期列要提前在 Excel 里设置成数据库能识别的格式大文件建议转成 CSV 再导入编码注意选 UTF-8。如果是纯开发环境也可以先用工具把 Excel 转成 INSERT 语句再执行脚本——但生产环境不建议这么干宁可走正式的导入通道。SQL 去重实战。我记得热词列表里“sql语句去重”“清洗---sql语句去重”出现了多次说明这是普遍痛点。去重有三层境界第一层查出去重后的数据。用 DISTINCT 或 GROUP BY。第二层删除表里重复行只保留一条。MySQL 里经典写法是DELETE t1 FROM your_table t1 INNER JOIN your_table t2 WHERE t1.id t2.id AND t1.dup_key t2.dup_key;这里利用了自连接找出同组重复里 id 较大的行删掉。执行前务必先 SELECT 验证要删除的行数SELECT t1.* FROM your_table t1 INNER JOIN your_table t2 WHERE t1.id t2.id AND t1.dup_key t2.dup_key;第三层需要“每个用户保留最新一条记录”这种业务去重用窗口函数 ROW_NUMBER()WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY create_time DESC) AS rn FROM orders ) SELECT * FROM ranked WHERE rn 1;这条 SQL 在 MySQL 8.0、SQL Server、PostgreSQL、Oracle 里都通用。它的逻辑是按 user_id 分组组内按 create_time 倒序编号编号为 1 的就是每组最新那条。这个写法也是我面试候选人时最爱考的一道题能独立写出来的SQL 基本功一般都不差。“数据库同步软件”这类工具在热词里也出现过。如果你需要把生产库实时同步到分析库或者做灾备常见方案有 MySQL 主从复制、基于 binlog 的 canal 同步或者商业的同步工具。核心原理都是解析数据库的增量日志binlog/redo log把这些变更操作在目标库上重放。需要提醒的是主从复制不保证实时一致有延迟两边表结构必须一致否则同步会中断报错。我见过最业余的失误是在主库改了表结构忘记在从库同步执行导致整个同步链路宕了两小时。4.3 工具选型从命令行到图形界面工具这块我的建议是“命令行能力必须会图形界面用来提效”。命令行是通用语言到哪台服务器都能用Navicat、DBeaver 这类工具则让你快速看表结构、导数据、跑查询。常见工具分类我列个表工具类型适用场景mysql / psql / sqlcmd官方命令行服务器运维、脚本执行、快速复查Navicat / DBeaver图形客户端日常开发、数据导入导出、表结构设计DataGrip图形客户端复杂 SQL 编写、多数据库统一管理Flyway / Liquibase版本管理数据库结构变更纳入 Git 管理dbx文件型工具SQLite 等嵌入式数据库的快捷管理顺带提一嘴“dbx数据库工具”这个热词它一般指针对 SQLite 这类文件型数据库的可视化管理工具。SQLite 和我们上面聊的 MySQL 不太一样它是一个嵌入式的 C 语言库数据库就是一个 .db 文件很多 App 的本地缓存和浏览器的存储都用它。如果你拿到一个 .db 文件想查看内容不要用文本编辑器打开那全是二进制乱码用 SQLite 的官方命令行工具sqlite3或者 dbx 这类 GUI 工具打开就能像操作普通数据库一样查数据。再说一个热词“mybatisplus根据java实体类生成创建表的sql语句”。MyBatis-Plus 有个能力是内置默认的建表 SQL 生成器——根据 Java 实体的字段映射自动拼出 CREATE TABLE 语句。这个对快速开发有用但生产库的表结构变更我还是建议走 Flyway 这种迁移工具原因是你需要完整的变更历史记录上线后回滚也有依据。“自动生成”适合原型开发不适合严肃的线上环境。4.4 高频问题速查表我把平时被问得最多的几个数据库操作问题整理成一张速查表可以直接收藏问题解决方案MySQL 密码有效期怎么查执行SHOW VARIABLES LIKE default_password_lifetime;MySQL 8.0 是password_reuse_interval相关的策略给用户设置永不过期用ALTER USER userhost PASSWORD EXPIRE NEVER;执行 SQL 脚本怎么带库名mysql -u root -p -D dbname script.sql-D指定默认数据库人大金仓数据库怎么跑 Docker官方镜像一般是docker run -d --name kingbase -e SYSTEM_PASSWORDxxx -p 54321:54321 kingbase/k8s用 ksql 连接验证sw 安装时显示 SQL 安装失败多半是系统缺少 Visual C 运行库或本机已有冲突的 SQL Server 组件先装 VC redistributable再清理旧实例后重试修改 MySQL 表结构ALTER TABLE table_name ADD COLUMN col_name INT;/MODIFY COLUMN .../DROP COLUMN ...SQL Server 2008 R2 数据库“存疑”检查 .mdf/.ldf 文件权限、磁盘空间然后执行ALTER DATABASE dbname SET ONLINE;Excel 入库后中文乱码CSV 文件导入时选 UTF-8 编码或者把 Excel 另存为 CSV UTF-8 格式再导这张表解决的是“下次遇到不要再找我”系列问题。每个我都在实际工作中或帮同事排查时验证过。5. SQL 注入与防御懂攻击才能写好防御代码5.1 注入攻击的原理一句话把验证绕过去热词里有“sql注入”“sql注入万能密码绕过”这块确实值得仔细讲。SQL 注入的本质是——用户输入的数据被当成了 SQL 代码的一部分去执行。最经典的例子就是万能密码绕过假设登录逻辑是String sql SELECT * FROM users WHERE username username AND password password ;如果用户名输入admin --密码随便填拼出来的 SQL 变成SELECT * FROM users WHERE usernameadmin -- AND passwordxxxMySQL 里--后面是注释后面的密码校验直接被忽略等于只用用户名就完成了登录。这是最老套但依然有效的攻击思路。更危险的版本是 OR 11 --它会让 WHERE 条件恒为真直接查出整张用户表。我在学习这个知识点的时候用的是 BWAPPBuggy Web Application靶场里面内置了几十个 SQL 注入测试场景从低难度到高难度都有。还有 CTF 比赛里类似“swpuctf 2021 新生赛 sql”这类题目解题思路基本都是从参数点注入 payload依次尝试闭合引号、注释、联合查询、报错注入等方式最终拿到 flag。这些靶场和题目是安全学习非常宝贵的练习材料——原因很简单不了解攻击手法的人写不出真正安全的代码。5.2 防御的核心预编译与参数化防御 SQL 注入不是靠过滤关键字也不是靠写复杂正则最关键的一步是永远不要手动拼接 SQL 字符串全部用参数化查询Prepared Statement。Java 里用 JDBC 和 MyBatis 的#{}PHP 用 PDO 的 preparePython 用 DB-API 的?占位符。核心原理是SQL 语句的骨架先发给数据库服务端预编译用户输入只作为纯参数值传给服务端不参与 SQL 语法解析。这样无论你输入什么都只是值只是字符串永远不会变成可执行代码。以 MyBatis 为例同样一句查询写法不一样安全性天差地别!-- 安全参数化PreparedStatement -- select idlogin resultTypeUser SELECT * FROM users WHERE username #{username} AND password #{password} /select !-- 危险字符串拼接注入风险 -- select idlogin resultTypeUser SELECT * FROM users WHERE username ${username} AND password ${password} /select#{}会生成?占位符走预编译${}是直接把字符串拼进 SQL。需要做动态列名、表名排序时逼不得已才用${}但字段值必须经过白名单校验。顺带说一个平时代码审计容易忽略的坑LIKE 查询和 IN 查询最容易出注入。LIKE %${keyword}%一旦用拼接黑客输入%还能把整表数据都捞出来。正确写法是用 CONCAT 拼接LIKE CONCAT(%, #{keyword}, %)既安全又性能好。5.3 面试常考的 SQL 题目“sql面试题”也是热词。数据库方向的面试题翻来覆去就那么几类核心考察的是你有没有真正理解数据模型和 SQL 执行机制。我整理几个高频的查第二高工资SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 1 OFFSET 1;或者用子查询WHERE salary (SELECT MAX(salary) FROM employee) ORDER BY salary DESC LIMIT 1。求每个部门的平均工资SELECT dept_id, AVG(salary) FROM employee GROUP BY dept_id;。升级版是要求保留部门名称那要 JOIN 部门表。用一条 SQL 查出去重后的订单数和原始订单数SELECT COUNT(*) AS total, COUNT(DISTINCT order_no) AS distinct_cnt FROM orders;。找出连续登录三天的用户这类题用窗口函数 LAG/LEAD 或自连接解法考察对日期处理和窗口函数的掌握。行转列pivot把不同月份的收入从多行转成多列用 CASE WHEN GROUP BY 实现深度考察 GROUP BY 的理解。面试时把这些题答上来不是终点更重要的是能说清楚每一步为什么这么写。面试官真正想听的是你对执行计划、索引选择、去重逻辑这些底层机制的判断而不是背出来的语法。我在面试别人时经常追问一句“这条 SQL 走没走索引你怎么验证”能答出“EXPLAIN 看 type 和 key”的候选人基本可以判断是有实战经验的。最后分享几个我自己的实操体会这篇文章写完我自己也把知识体系重新捋了一遍。最后想再叮嘱几句都是这些年花钱买来的教训。第一个体会数据库设计的前瞻性比什么都重要。表结构一旦上线后面每一行业务代码都长在它上面。字段类型、是否允许 NULL、谁做主键、要不要预留扩展字段——这些决策后期改动的成本远超你想象。我见过最痛苦的重构就是把一个用字符串拼接当主键的表改成自增 ID牵扯了几百个接口。年轻的时候觉得设计表结构简单现在觉得这是整个系统里最需要慎重对待的环节。第二个体会慢 SQL 优化不是 DBA 一个人的事是每个写 SQL 的人的事。代码写完顺手跑一个 EXPLAIN是成本最低的保命操作。我们团队后来定了一个规矩任何涉及多表 JOIN 或被高频调用的查询必须附上执行计划截图才能合并到主干。有了这个约束之后生产环境的慢查询数量肉眼可见地降了下来。第三个体会安全意识和安全意识之上的“安全能力”是两回事。知道 SQL 注入有危害不算本事能在每一层都堵住漏洞才算。写代码的人必须自己会复现一次注入攻击才会真正明白为什么预编译是不可妥协的底线。我很推荐想深入这块的朋友去 BWAPP 靶场里亲手试试把每个漏洞级别都打一遍比看一百篇安全文章都管用。数据库这门基本功越往深走越发现它和业务离得近。你把索引原理搞明白了自然就知道为什么 ORM 自动生成的 SQL 有时慢得离谱你把事务隔离级别吃透了就不会写出并发扣减为负的惨痛 Bug。希望这篇从原理到分类再到实战的梳理能帮你把那些零散的知识点串成一张完整的网。
返回列表