ARTICLE DETAIL

资讯详情

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

MySQL基础全攻略:建库建表、索引优化、事务锁机制与运维实践

MySQL基础全攻略:建库建表、索引优化、事务锁机制与运维实践 不用报任何班也不用啃完一本几百页的书。学 MySQL先把“数据库基础”这四个字吃透后面所有的高并发方案、读写分离、性能调优才有落地的底气。我见过太多人一上来就装环境、建表、写查询结果一问存储引擎有什么区别、事务隔离级别到底隔离了什么、为什么这个查询明明有索引却还是慢整个人就卡住了。这篇就把我这些年踩过的坑和带新人总结出来的经验一次讲清楚。不管你是刚接触数据库的纯小白还是被公司里线上问题逼着补课的半路出家选手只要能花半天时间把这些基础环节过一遍再看复杂的项目案例思路会顺很多。1. 学 MySQL 之前先把这三件事想明白1.1 为什么是 MySQL而不是 Oracle 或 PostgreSQL很多新人喜欢问“学哪个数据库好”我的建议是如果是为了就业和日常开发MySQL 是第一选择。原因很简单市面上绝大多数互联网公司的业务系统、Java 项目、Python 项目默认的数据库就是 MySQL。你去看招聘要求几乎每一条后端岗都写着“熟练掌握 MySQL”这个“熟练”不是指会装、会建表而是指你理解它的底层逻辑能处理真实问题。但这不意味着 MySQL 在所有场景都最优。Oracle 在银行、电信这类对事务和稳定性要求极高的传统行业还是主流PostgreSQL 在复杂查询和地理信息处理上有天然优势。可对于大多数人的职业起步来说MySQL 的社区资料最全、问题最容易搜到、生态工具也最成熟。我给你的建议是入门认准 MySQL把它学透之后再看其他数据库会非常快。数据库的核心概念——表、索引、事务、锁——都是相通的差的只是语法细节和引擎实现。1.2 基础阶段必须啃下来的知识地图学 MySQL 最忌讳的就是“东学一块西学一块”。今天搜到一条命令就敲一下明天看到一个函数就试一下看起来每天都在学实际上脑子里的知识是散的。我整理过一份基础路线按这个顺序走基本不会迷路库和表的概念什么是数据库、数据表、字段、记录这些是最基本的物理单位。SQL 分类DDL数据定义、DML数据操作、DCL数据控制。很多人分不清这些后面才会出现“怎么删不掉数据”“怎么权限不对”的疑问。数据类型整数、小数、字符串、日期时间、枚举等选错类型会吃大亏这个后面细讲。约束与索引主键、外键、唯一约束、普通索引、联合索引这是查询性能的根基。事务与锁ACID 特性、隔离级别、锁的分类。基础中的核心重点。常用函数与聚合排序、分组、条件统计掌握这些就能覆盖日常开发 90% 的查询需求。把这六块吃透再去看存储过程、视图、触发器、分区表这些进阶内容就只是时间问题了。1.3 存储引擎选型别只在 InnoDB 和 MyISAM 之间纠结很多新手建表的时候根本没关注过存储引擎直接用了默认配置。在 MySQL 5.5 之前的默认引擎是 MyISAM5.5 之后改成了 InnoDB现在用的 5.7 和 8.0 版本默认都是 InnoDB。这里面有一个非常关键的认知默认引擎已经帮你把最重要的选择做了如果你非要去手动改必须清楚自己在干什么。我做一个最直观的对比表方便你理解两者差异对比项InnoDBMyISAM事务支持支持 ACID不支持事务外键约束支持不支持锁粒度行级锁表级锁崩溃恢复支持有 redo log不支持崩溃可能丢数据全文索引5.6 后才支持原生支持适用场景大部分业务系统只读报表、日志分析类为什么默认是 InnoDB因为对于绝大多数业务来说数据可靠性和并发能力比那点查询性能更重要。MyISAM 在纯读场景下确实更快但它一个表级锁就注定了并发写入会排队一条慢查询能把整张表堵死。我自己接手过一个遗留系统里面的报表表用了 MyISAM业务量一上来就锁表后来全部迁到 InnoDB 才稳定。所以除非你有非常明确且充足的理由否则一律用 InnoDB。这不是偷懒是在用前人的经验避坑。2. 环境搭建Windows 安装 MySQL 5.7 和 8.0 的实操细节2.1 下载版本选择5.7.44 与 8.0 到底怎么选热词里高频出现的“mysql 5.7.44 官方为什么之后 5.7.43 呢”很多人没搞懂这个版本号的含义。简单解释一下5.7.44 属于 5.7 系列的补丁版本官方对 5.7 系列的维护一直持续到 2023 年才结束 EOL所以 5.7.43 之后继续推送了 5.7.44 作为最后的收官版本之一这很正常用不着多虑。至于选 5.7 还是 8.0我的建议是新项目、新学习直接上 8.0。8.0 的默认字符集已经是 utf8mb4窗口函数、CTE 公共表表达式这些功能非常实用性能也有明显提升。接手老旧项目跟着线上版本走绝大多数老项目跑的是 5.7你没有必要在生产环境冒险升级。学习兼就业优先 8.0因为现在新公司建库基本都是 8.0 起步你学新的不亏。2.2 Windows 下 MySQL 8.0 解压版详细安装流程Windows 上装 MySQL 有两条路一个是下载安装版 exe 一路点下一步一个是下载解压版自己配置。如果你以后要跟 Linux 服务器打交道我强烈建议你用解压版走一遍手动配置流程因为这种“自己动手改配置文件”的方式能帮你把原理搞清楚以后在服务器上哪怕没有可视化界面也能搞定。第一步去官网下载 mysql-8.0.x-winx64.zip 包解压到一个干净路径比如 D:\tool\mysql-8.0.46-winx64。注意路径里不要有中文和空格不然以后启动服务可能报各种奇怪的错。第二步在解压目录下新建 my.ini 配置文件这是关键一步内容模板我直接给你[mysqld] # 端口号 port3306 # 安装目录改成你自己的实际路径 basedirD:/tool/mysql-8.0.46-winx64 # 数据存放目录需要提前建一个空文件夹 datadirD:/tool/mysql-8.0.46-winx64/data # 字符集 character-set-serverutf8mb4 # 默认存储引擎 default-storage-engineInnoDB # 跳过密码验证首次登录重置密码用 # skip-grant-tables [client] default-character-setutf8mb4第三步用管理员身份打开命令行窗口进入解压目录的 bin 目录执行初始化命令mysqld --initialize-insecure这里我特别说明一下加了--initialize-insecure参数初始 root 用户是没有密码的方便你第一次登录后立刻改密码。如果你用--initialize系统会随机生成一个临时密码写进日志文件你得去 data 目录找 .err 文件翻麻烦得很。第四步安装 Windows 服务并启动mysqld --install MySQL net start MySQL启动成功的标志是命令行提示“MySQL 服务正在启动 . MySQL 服务已经启动成功”如果卡在“正在启动”就没下文了大概率是 my.ini 里的路径写错或者 datadir 目录权限有问题。第五步登录并改密码mysql -uroot -p因为初始化时没设置密码提示输入密码时直接回车就行。登录后执行ALTER USER rootlocalhost IDENTIFIED BY 你的新密码; FLUSH PRIVILEGES;2.3 用 Docker 装 MySQL省心还是添乱热词里有大量“docker 安装 mysql”“docker compose 部署 mysql”“docker 离线安装 arm 架构 mysql”的搜索说明容器化部署已经成为日常了。Docker 装 MySQL 最大的好处是隔离环境不用在宿主机上留下一堆依赖换版本也方便删容器重跑一条命令搞定。一条最基础的命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ mysql:8.0但新手用 Docker 最容易踩的坑有三个第一个坑是数据丢失。容器默认把数据写在可写层一旦容器删除数据跟着没了。正确做法是挂载数据卷docker run -d \ --name mysql8 \ -p 3306:3306 \ -v /my/own/datadir:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD123456 \ mysql:8.0第二个坑是版本不一致。本地客户端是 5.x 的容器里跑的是 8.0连接时会报认证插件不兼容的错误“Authentication plugin caching_sha2_password cannot be loaded”。这个错误几乎每个从 5.7 迁到 8.0 的人都会遇到因为 8.0 默认的认证插件变了。解决方式是用 mysql_native_password 重建用户的认证方式或者干脆客户端也升级到 8.x。第三个坑是 docker pull 失败。热词里的“failed to decode referrers index: invalid”就是这么来的这多半是你本机 Docker 版本太低或者镜像源连不上导致的。升级 Docker Desktop、换一个可用的镜像源基本都能解决。2.4 破解 MySQL 忘记密码的几种方式这是个高频问题我单独说一下。忘记 root 密码在 Windows 端最常用的方式是改 my.ini往[mysqld]段加上一行skip-grant-tables重启 MySQL 服务此时所有客户端免密登录。登录后把密码改掉再把那行注释掉并重启服务恢复正常。在 Docker 环境就更简单了直接进入容器内部改配置或者用环境变量重置docker exec -it mysql8 mysql -uroot -p关键是记住一个原则任何时候修改认证相关的配置前先备份数据。别问我怎么知道的问就是见过太多改配置改崩了然后把 data 目录当垃圾清的惨案。3. 数据库基础核心建库、建表、字段设计3.1 规范化设计从区分实体和字段开始很多初学者拿到需求直接开建表结果后期各种冗余、冲突、改不动。我自己带人时总是先讲一句话先把现实世界的对象抽象成实体每个实体对应一张表实体之间有关系的用外键关联而不是把所有东西塞进一张大表里。举个例子做一个学生课程成绩系统。普通新人可能直接建一张表CREATE TABLE student_score ( id INT PRIMARY KEY AUTO_INCREMENT, student_name VARCHAR(50), student_class VARCHAR(50), course_name VARCHAR(50), score INT );看起来没毛病但你要是存上 1000 个学生、每人选 5 门课这条表里会出现大量重复的学生姓名和班级信息改个班级名得 UPDATE 一堆行。正确的做法是拆分成三张表学生表、课程表、成绩表。学生表存学生的基本稳定信息课程表存课程的基本信息成绩表只存学生 id、课程 id 和分数这三个动态关联字段。这就是第一范式到第三范式的基本思想核心目标就是减少数据冗余、保证一致性。3.2 字段类型选择这些对比直接决定表的性能上限字段类型的选择一眼看上去太基础了但实际影响很大。我总结几个最常见的对比都是实际开发中反反复复踩的坑。整数类型类型存储空间范围TINYINT1 字节-128~127SMALLINT2 字节-32768~32767INT4 字节-21亿~21亿BIGINT8 字节极大范围选类型的一个原则是够用就好。存年龄用 TINYINT 就够了你却用 BIGINT浪费 7 个字节一张表几十个字段、几千万行这个浪费就大了。反过来存订单金额这种可能超过 21 亿的值用 INT 会溢出导致数据异常。小数类型一个经典的坑金额字段用 FLOAT 或 DOUBLE 存。FLOAT 和 DOUBLE 是浮点数会有精度丢失问题0.1 加 0.2 在二进制世界里不是精确等于 0.3 的。做金融类项目金额必须用 DECIMAL它是以字符串形式存储的精确小数比如DECIMAL(10,2)表示最长 10 位、小数占 2 位。字符串类型CHAR 是定长字符串VARCHAR 是变长字符串。怎么选一个非常实用的经验长度不太会变的用 CHAR比如手机号、身份证号长度变化大的用 VARCHAR比如用户昵称、文章标题。CHAR 的查询性能略优于 VARCHAR因为它是定长的磁盘存储位置可以精确计算但你要是把长度差异很大的文本存进 CHAR空格填充的浪费会让你哭着改表。3.3 主键设计的几个硬性建议主键这个东西新人觉得不就是加个 PRIMARY KEY 吗没什么好学的。但主键设计不好后期索引分裂和性能问题会非常明显。我的建议是使用自增 INT/BIGINT 作为主键除非有极强的业务要求不要用业务字段当主键。原因有两点第一自增主键写入时是有序的B 树索引的叶子节点会顺序写入能减少页分裂。UUID 作为主键是随机字符串插入时索引页会频繁分裂造成性能下降和碎片空间。在一张亿万级的表里这个差异是肉眼可见的。第二业务字段做主键的风险在于业务会变。比如用手机号做主键哪天业务支持多账号体系了手机号变成了可变更字段你改主键比改数据难一万倍。自增主键是纯物理标识跟业务彻底解耦永远不用担心这种问题。3.4 常用 DML 语句实操增删改查的避坑写法数据操作的核心语句其实就四类INSERT、UPDATE、DELETE、SELECT。但我说几个很多人忽略的细节都是真实开发中容易出问题的地方。INSERT 插入时如果你插入的数据量很大建议用批量插入而不是逐条插入比如INSERT INTO t_user (name, age) VALUES (张三, 18), (李四, 20), (王五, 22);逐条插入会产生大量的日志和网络往返批量插入能显著提升效率。在线上做数据迁移时这个优化经常能把几小时的任务缩短到十几分钟。UPDATE 没加 WHERE 条件会把整张表的数据都改了这是事故级错误。我个人的防御习惯是执行 UPDATE 和 DELETE 前先 SELECT 一遍确认 WHERE 条件筛选出的数据是要操作的那批再用同样的条件去执行更新或删除。DELETE 后想找人恢复数据几乎不可能。哪怕开启了 binlog恢复也是一场噩梦。所以养成习惯删除前先备份尤其是生产环境的表。备份语句很简单mysqldump -uroot -p 数据库名 表名 备份文件.sql4. 索引与排序查询慢的根源和解决思路4.1 索引为什么快从全表扫描到 B 树查找很多人不理解为什么建了索引查询就快了。我用生活化的方式讲一下。没有索引时MySQL 要查某条数据只能把整张表从头到尾扫一遍对应到磁盘上就是每一行都读出来比对这叫全表扫描。假设表里有 1000 万行平均查一次要读几百万行数据。有索引后MySQL 维护了一棵 B 树树的每个节点存放索引值和对应数据行的指针。查数据时沿着树往下找就好比在字典里查字不需要从第一页翻到最后一页直接按拼音或偏旁索引定位查找次数是树的高度通常也就三层到四层。这个差距有多大全表扫描可能是秒级甚至分钟级走主键索引查找通常是毫秒级。线上接口卡死、数据库 CPU 打满八成就是某个关键查询没有走索引在做全表扫描。4.2 常见索引类型和创建方式MySQL 中常见的索引有三种分类普通索引最基本的索引没有唯一性限制只为加速查询。唯一索引索引列的值必须唯一适合手机号、身份证等字段。联合索引多个字段组合成一个索引遵循最左前缀原则。创建索引的语法CREATE INDEX idx_name ON t_user(name); CREATE UNIQUE INDEX idx_phone ON t_user(phone); CREATE INDEX idx_class_score ON t_student(class_id, score);这里特别说一下联合索引的最左前缀原则。联合索引 (class_id, score) 本质上先按 class_id 排序再按 score 排序。如果你查询条件是WHERE score 90这个索引用不上因为 order by 的第一个排序键 class_id 缺失了。你必须把 class_id 放在条件里WHERE class_id 1 AND score 90才能命中索引。所以设计联合索引时字段顺序不能拍脑袋要把区分度高的、查询频率高的字段放在前面。4.3 排序也走索引别乱加 ORDER BY热搜词里有“mysql排序”排序在实现上分为文件排序和索引排序两种。文件排序是指 MySQL 把查出来的数据先放到内存或磁盘上排序当排序的数据量大且超过 sort_buffer 阈值时会落盘成多个临时文件再归并排序性能非常差。最理想的排序方式是让排序走索引。因为你建联合索引时索引本身就是按顺序排列的MySQL 直接从索引里读出来就是有序的根本不需要额外排序。比如我建了索引 (class_id, score)执行SELECT * FROM t_student WHERE class_id 1 ORDER BY scoreMySQL 会直接从索引中按顺序读取数据。反过来说如果排序字段没有索引或者排序方向和索引方向不一致MySQL 就只能文件排序。所以看到慢 SQL 里出现 Using filesort 这个关键词第一反应就是检查排序字段是否在索引中。4.4 索引失效的几类真实场景有索引并不代表一定能用到索引。以下几种情况是我排查慢 SQL 时遇到最频繁的对索引列使用了函数比如WHERE YEAR(create_time) 2024这个写法会让索引失效因为需要对每一行先算函数再比较。正确写法是WHERE create_time 2024-01-01 AND create_time 2025-01-01。隐式类型转换有一个 varchar 类型的字段 phone你写WHERE phone 13800138000数字是整数MySQL 会把 varchar 转成数字再比较索引失效。LIKE 前导通配符WHERE name LIKE %张%因为你要匹配任意位置B 树没法从固定位置开始查。但WHERE name LIKE 张%是可以走索引的因为开头已知。联合索引未满足最左前缀前面已经讲过综合起来一句话联合索引的查询条件里必须命中第一个字段否则整个索引都用不上。每次遇到慢查询我习惯先去执行EXPLAIN命令看执行计划查看 type 字段和 possible_keys、key 字段确认到底走了哪个索引有没有全表扫描。EXPLAIN 是排查性能问题的第一利器后面接 SELECT 语句它能告诉你 MySQL 实际会怎么执行这条查询。5. 事务、锁与隔离级别数据库并发稳定性的基石5.1 事务的 ACID 特性用转账场景一次吃透事务是数据库里最核心的概念之一。什么叫事务一句话概括一组操作要么全部成功要么全部失败不允许出现“做了一半”的状态。ACID 四个特性用银行转账来讲最直观。假设你给朋友转 100 块钱数据库要做两步扣你的余额、加朋友的余额。原子性Atomicity扣款和加款必须作为一个整体。如果扣完款在加款前系统崩了转账操作就回滚钱回到你的账户。不能出现钱扣了但对方没收到的情况。一致性Consistency转账前后所有相关账户的总金额不变。系统任何时候都不允许出现数据逻辑错误比如总账对不上。隔离性Isolation转账过程中其他事务不能看到中间状态。你在转账进行中查询余额要么看到转账前要么看到转账后不能看到你钱被扣了但朋友还没收到的中间状态。持久性Durability事务提交后结果永久保存。即使立刻断电、系统重启数据也不会丢失。这四个特性是数据库厂商对用户的基本承诺不是 MySQL 独有的Oracle、PostgreSQL 也都遵循。5.2 事务隔离级别详解脏读、不可重复读、幻读隔离性不是完全隔离而是有程度之分的。MySQL 的默认隔离级别是 REPEATABLE READ可重复读这个选择背后有非常实际的原因。SQL 标准定义了四个隔离级别从低到高隔离级别脏读不可重复读幻读READ UNCOMMITTED可能发生可能发生可能发生READ COMMITTED不会可能发生可能发生REPEATABLE READ不会不会可能发生InnoDB 通过 MVCC 已解决SERIALIZABLE不会不会不会用大白话解释这三个异常脏读事务 A 修改了一条数据但还没提交事务 B 读到了这个未提交的修改。如果 A 回滚了B 读到的就是一条不存在的数据。不可重复读事务 A 内两次读同一行第一次读到的是 100第二次读到的是 90因为事务 B 在这期间提交了修改。重点在于同一行数据发生了变化。幻读事务 A 内两次查询同一范围的数据第一次查出 2 条第二次查出 3 条因为事务 B 在这期间插入了新记录。重点在于数据条数发生了变化。MySQL 默认的 REPEATABLE READ 级别解决不可重复读的方式是 MVCC多版本并发控制它让事务读取数据时基于快照不会感知到其他事务已提交的修改。至于幻读MySQL 的 InnoDB 引擎在 REPEATABLE READ 下通过对范围加间隙锁基本解决了所以实际开发中你很少遇到幻读问题。这也是为什么 MySQL 官方敢把默认级别设为 REPEATABLE READ而 Oracle 的默认是 READ COMMITTED。5.3 锁的分类行锁、表锁、间隙锁到底锁住了什么热词里高频出现“mysql锁的分类”“mysql锁表”我在这里把锁的体系梳理清楚。锁的本质是为了解决并发冲突MySQL 的锁主要分为以下几类按粒度分表级锁锁住整张表MyISAM 引擎只用这种锁。效率低一旦写操作锁表其他所有读写都会被阻塞。InnoDB 在特殊场景下也会用表锁比如 DDL 修改表结构时。行级锁InnoDB 独有只锁住操作涉及的行并发性能好但管理开销比表锁大。按类型分共享锁S 锁也叫读锁。多个事务可以同时加共享锁读同一行数据互相不阻塞但不允许其他事务修改该行。排他锁X 锁也叫写锁。一个事务加了排他锁后其他事务既不能读也不能写该行直到锁被释放。这两种锁的兼容关系是共享锁之间兼容共享锁和排他锁不兼容排他锁之间不兼容。按实现机制分记录锁锁的是索引记录本身。间隙锁锁的是索引记录之间的间隙防止其他事务在间隙中插入数据。这个机制就是解决幻读的核心手段。临键锁记录锁和间隙锁的组合InnoDB 默认加锁单位。我举个例子一个表里有 id 为 1、2、3 的几条记录你在 REPEATABLE READ 级别执行SELECT * FROM t WHERE id BETWEEN 1 AND 2 FOR UPDATEInnoDB 不只会锁 id1 和 id2 这两行还会锁住 (1,2) 之间的间隙和 (2,3) 之间的间隙。目的就是防止其他事务往 1 和 2 之间插入 id1.5 的记录。5.4 死锁是怎么发生的如何避免两个事务各自持有了一把锁同时等着对方手里的锁谁也不让结果就死锁了。MySQL 检测到死锁会自动回滚其中一个事务释放它的锁让另一个事务继续执行所以死锁一般不会让数据库直接挂掉但被回滚的那个事务业务上可能就报错了。真实场景里的例子事务 A 先更新 id1 的记录再更新 id2 的记录事务 B 先更新 id2 的记录再更新 id1 的记录。两个事务互相等对方的第二把锁就是典型的死锁。避免死锁最有效的办法是所有事务都按相同的顺序访问资源。比如全公司统一约定涉及多行更新时先按 id 从小到大排序再依次更新。这样 A 和 B 都会先锁 id1再锁 id2只有一个人能成功走到第二步另一个人会阻塞死锁就破解了。还有一个小技巧是更新多行时尽量缩小锁定范围。范围越大锁定的行越多死锁概率越高。能走主键更新的就不要用范围条件。6. MySQL 常用命令与运维实用操作6.1 高频管理命令整理关键时刻全靠手速有些命令平时用不到但出了问题必须要手快。我把最常用的一批整理成速查表场景命令登录数据库mysql -uroot -p查看所有数据库SHOW DATABASES;切换到某个库USE 数据库名;查看库下所有表SHOW TABLES;查看表结构DESC 表名;查看建表语句SHOW CREATE TABLE 表名;备份整个库mysqldump -uroot -p 库名 备份.sql恢复整个库mysql -uroot -p 库名 备份.sql查看当前正在执行的慢查询SHOW PROCESSLIST;查看数据库版本SELECT VERSION();其中SHOW PROCESSLIST是排查线上数据库卡顿的第一手信息。它能显示当前所有连接正在执行的 SQL如果发现某个连接执行时间特别长、状态是 Locked基本可以判断是锁等待问题。这时候用KILL 连接ID杀掉对应会话就能快速解除僵局。6.2 执行 SQL 脚本与日常备份恢复“mysql执行sql脚本”是热词这个需求在项目上线和测试环境初始化时特别常见。执行脚本的方式有三种第一种在 mysql 命令行内执行mysql source /path/to/file.sql;第二种在系统命令行直接重定向mysql -uroot -p 数据库名 file.sql第三种进入 mysql 命令行后用\.命令跟 source 等价。日常备份我用得最频繁的是 mysqldump注意这个工具是独立于 mysql 客户端的需要在 bin 目录下执行。备份时如果库很大可以加上--single-transaction参数这样在 InnoDB 引擎下备份不会锁表不影响线上业务继续读写。恢复时有一个很容易被忽略的坑恢复前先建好数据库。mysqldump 备份的文件默认不包含 CREATE DATABASE 语句除非你备份时加了--databases参数恢复时目标是哪个库就先用CREATE DATABASE建好。6.3 线上误操作快速应对Update 忘带条件怎么办这是一个几乎每个 DBA 都经历过的噩梦。UPDATE 语句忘加 WHERE 条件或者条件写错了全表数据被改。遇到这种情况第一反应不是慌而是冷静评估损失。如果 binlog 开启并且记录格式是 ROW 模式你可以用 binlog 解析工具把误操作前的数据提取出来恢复。MySQL 8.0 自带 mysqlbinlog 工具大致流程是# 找到最新的 binlog 文件 SHOW BINARY LOGS; # 用 mysqlbinlog 解析定位到误操作的 SQL 位置 mysqlbinlog --no-defaults /var/lib/mysql/binlog.000001 # 把误操作前后的 SQL 提取出来找到事务起始点反向构造恢复语句如果没有开 binlog那就只能依赖备份库或者从库数据来恢复了所以我在前面反复强调备份的重要性不是吓唬人。一个最简单的兜底策略每天定时全量备份每两小时做增量备份。宁可备份占点磁盘也好过数据没了干瞪眼。7. 常见问题与排查技巧实录7.1 Windows 安装和启动类问题速查我把热词里出现的 Windows 安装问题以及我这些年遇到的典型启动错误整理成一张速查表报错或问题原因解决方案服务启动后立即停止my.ini 路径错误或 datadir 路径不存在检查 basedir 和 datadir 路径是否真实存在不能用反斜杠结尾1067 系统错误配置文件项格式有问题检查 my.ini 是否保存为 ANSI 编码不要用记事本另存为 UTF-81045 access denied密码错误或认证插件问题用 skip-grant-tables 重置密码8.0 注意认证插件2003 cant connect服务没启动或端口被防火墙拦截net start mysql启动服务确认 3306 端口未被占用10061 connect error服务未启动检查服务列表确认 MySQL 服务状态为 Running启动类问题的排查思路永远是先看错误日志Windows 上日志在数据目录下的 .err 文件里。很多人遇到问题第一反应是去百度搜搜半天没结果其实打开 .err 文件一看第一行就写着什么原因比自己瞎折腾高效得多。7.2 SQL 执行效率问题EXPLAIN 命令怎么读EXPLAIN 输出的内容里新手最容易看的是 type 字段这个字段直接告诉你 MySQL 用了什么方式去查数据const/system最好按主键或唯一索引查询最多返回一行。ref很好用了非唯一索引返回匹配某条件的多行。range还行通过索引范围扫描BETWEEN、、这类就是 range。index不太好全索引扫描等于把整个索引树读了一遍。ALL最差全表扫描数据量一大基本就是灾难。另外还要看 Extra 列如果出现 Using filesort 或者 Using temporary说明查询存在排序或分组临时表操作这是性能杀手。优化方向是调整索引让排序和分组尽量走索引完成。我的一个实操模板每次写完一条重要 SQL都习惯性跑一下 EXPLAIN确认 type 不是 ALLExtra 里没有 Using filesort。养成这个习惯后线上慢查询基本能少一半。7.3 MySQL 8.0 的 SSL 连接错误排查热词里有“mysql ssl连接错误”这类问题在启用 SSL 加密连接后容易出现。8.0 默认是开启 SSL 支持的所以客户端连接时如果强制要求 SSL 但证书又不匹配就会报错。排查思路分几步先确认客户端连接时是否加了--ssl-modeREQUIRED参数如果加了检查服务端证书是否有效。如果客户端和服务端都在内网环境数据链路上没有风险可以直接用非 SSL 方式连接在连接串里加ssl-modeDISABLED。另外一个常见原因是 JDBC 连接串里写了useSSLtrue但没有配置证书报错内容是Public Key Retrieval is not allowed。8.0 的 connector 对安全要求更高解决方法有两个一个是 URL 上加上allowPublicKeyRetrievaltrue另一个是把 useSSL 改成 false仅限测试环境。7.4 高并发场景下的数据库基础保障看到热词里的“mysql高并发解决方案”我要泼一盆冷水高并发不是数据库基础能直接解决的但如果你基础不牢连排查问题的资格都没有。真实的高并发方案层层递进先从 SQL 层做优化保证每条语句都走索引、没有全表扫描再引入缓存层把热点数据从 MySQL 中扛到 Redis然后做读写分离把查询压力分摊到从库最后才考虑分库分表把一个库拆成多个库把一张大表拆成多张小表。但所有这些方案都建立在同一个地基上你怎么建的表、你怎么设计的索引、你的事务隔离级别和锁用的对不对。地基没打好上面盖什么都会塌。这也是我反复强调必须先搞懂数据库基础的原因。8. 最后分享两个实用习惯先说第一个习惯写任何 SQL 前先想清楚这四条。一是涉及的字段有没有索引二是查询有没有必要的 WHERE 条件三是能不能避免 SELECT *四是事务范围能不能缩小。这四条想清楚了你的 SQL 大概率不会太烂。尤其是 SELECT *在表字段多、数据量大时会白白多查很多不需要的数据拖慢网络和内存开销。业务代码里尽量只查需要的字段。第二个习惯任何配置变更、表结构变更、数据批量操作之前先备份再操作。哪怕只是改一个字段名也要把原表结构导出来放在一边。这个习惯看起来笨但能在关键时刻救命。我在本地环境作图时也坚持这个习惯因为“顺手改一下”的操作失误率远比你想象得高。学 MySQL 这事确实没有捷径但也不需要把它当成一座翻不过去的山。把本文中提到的库表设计、索引原理、事务与锁、常用命令这些基础逐一吃透多动手敲、多 EXPLAIN、多备份你很快就会发现自己看问题的方式从“这个功能怎么写”变成了“这个功能在数据库层面到底是怎么跑的”。这一步迈过去MySQL 就真正入门了。
返回列表