把MySQL电子书当工程做:从建库到主从复制的落地实操
发布时间:2026/9/25 6:13:16来源:尧图网络
简介一份MySQL中文电子书合集内容覆盖数据库基础、SQL增删改查、数据类型与存储引擎InnoDB、MyISAM、唯一/全文/联合索引创建、EXPLAIN查询分析、存储过程与触发器、视图、数据库范式设计以及mysqldump备份恢复、用户权限和加密等安全实践适合从零入门到进阶的开发者系统学习。资源共三十八个HTML文件按章节和附录拆分可离线在浏览器中阅读压缩包体积仅一点三二MB轻巧便携目录结构清晰。已有五百一十三人学习下载既可按顺序通读也可针对索引调优、备份策略、监控性能指标CPU、内存、I/O负载等主题快速检索。书中示例贴近实际运维场景阅读时动手实践能帮助读者更扎实地掌握MySQL核心原理、查询优化方法和日常故障排查技能。全书覆盖从基础概念到高级调优的完整知识链适合自学或作为工作参考手册。1. 为什么“MySQL 电子书中文”要当工程做而不是当书读很多刚入门的读者下载了一堆中文版 MySQL 电子书被目录里的事务、索引、存储过程、主从复制吓退最后书签停在第五章。我的建议很直接把电子书当成一份图纸把每章知识转成能跑的 SQL 和配置文件才算真正读完。这篇笔记想跟你聊的就是“怎么靠一份中文 MySQL 电子书完成从建库到主从复制的全套落地动作”。适合刚装好 MySQL 却不知道下一步干什么的初学者也适合用过 MySQL 但总在连接报错、锁等待、配置不生效上翻车的熟手。文章里所有命令我都按常见生产环境习惯来写你照着敲踩坑部分我会单独拎出来讲。2. 从电子书里抽出这份起步路线选材、装库、建表到第一条查询2.1 中文资料那么多先解决“跟哪本走”的问题我不推荐同时打开三本 MySQL 电子书对比着学主线一多最后哪条都走不通。常见做法是选定一本以“实操为主、理论够用”的中文电子书然后按它的章节目录走一遍安装与配置、SQL 基础、索引、事务与锁、存储过程与触发器、备份与主从。选书时看三点第一有没有针对 MySQL 8.0 的说明旧书里很多配置项在 8.0 下已经改了默认值第二命令是否给完整结果而不是只贴一条孤零零的 SQL第三讲锁和事务时是否配了“开两个会话演示”的案例只看概念不动手等于没看。选好主线后我建议你先做一个动作建一个专门的实验库把书里涉及的表结构都敲进去。这一步能同时验证客户端连接、字符集、SQL 模式是否正常也为后面所有章节的实操准备好场地。我自己习惯给实验库起名study_db所有折腾都在里面不碰业务库翻车了直接 drop 重建相当于给自己留了一颗后悔药。2.2 最小可运行的建库建表与查询样例下面这段脚本覆盖了电子书第一章到第三章常用到的基础操作我建议你一字不差地敲一遍。-- 建库显式指定字符集避免继承服务器默认设置 CREATE DATABASE IF NOT EXISTS study_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE study_db; -- 用户表id 用自增主键手机号加唯一索引 CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(32) NOT NULL COMMENT 用户名, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 订单表关联用户订单金额用 DECIMAL 而不是 FLOAT CREATE TABLE orders ( id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, amount DECIMAL(10,2) NOT NULL DEFAULT 0.00, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待支付 1已支付 2已取消, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表; -- 插入两条测试数据 INSERT INTO user (name, phone) VALUES (张三, 13800001111), (李四, 13800002222); INSERT INTO orders (user_id, amount, status) VALUES (1, 99.90, 1), (2, 19.90, 0); -- 一条带 JOIN 的查询验证数据没问题 SELECT u.name, o.amount, o.status FROM user u JOIN orders o ON o.user_id u.id WHERE o.status 1;这段脚本里有三个参数值得你注意。字符集我用了utf8mb4不是utf8因为 MySQL 的utf8最多存 3 个字节emoji 和生僻字会报错这也是很多新手导入数据时乱码的根源。订单金额用DECIMAL(10,2)而不是FLOAT因为浮点型在比较和累计时会产生精度偏差电子书里如果没强调这点你迟早会在对账时发现差几分钱。订单表只建了idx_user_id普通索引没有建复合索引因为现在查询条件还很简单索引不是越多越好。跑通这段后你已经完成了“读电子书第一章”的闭环库、表、数据、查询全部真实存在。接下来就可以往索引和事务这两个硬骨头走了。3. 把索引和事务两章敲成实验explain、隔离级别与锁等待3.1 索引不是“建了就快”用 explain 验证才是关键电子书里讲索引时通常会铺开 B 树、回表、覆盖索引这些概念。概念要记但更要落地。我见过太多人在 where 条件列上乱建索引结果查询没变快写入反而变慢了。正确路径是先写出查询再EXPLAIN看执行计划最后决定要不要建索引。-- 先用 explain 看这条查询的访问类型 EXPLAIN SELECT * FROM orders WHERE user_id 1; -- 建索引后再看一次 ALTER TABLE orders ADD KEY idx_user_status (user_id, status); EXPLAIN SELECT * FROM orders WHERE user_id 1 AND status 0;看执行计划时重点关注type和key两列。type从好到差一般是system、const、eq_ref、ref、range、index、ALL如果看到ALL说明这条查询在做全表扫描数据量一大必然慢。第一次EXPLAIN时user_id有单列索引所以type应该是ref加了联合索引(user_id, status)后查询同时过滤两个字段走的是同一个索引key会变成idx_user_statusrows估算值也会下降。有一个电子书不一定写透的细节联合索引(user_id, status)对“只查user_id”的查询也有效因为联合索引的最左前缀原则但对“只查status”的查询无效因为status不是最左列。所以建联合索引前你要先想清楚业务里的高频查询到底以哪个字段作为过滤起点。3.2 事务隔离级别用两个会话实测脏读、不可重复读与幻读事务这章最容易看得云里雾里。我的经验是开两个 MySQL 客户端窗口一个会话写一个会话读亲眼看一下不同隔离级别下的差异。先查当前隔离级别-- 查看全局和会话级隔离级别 SELECT global.transaction_isolation; SELECT session.transaction_isolation; -- 把当前会话设为读已提交注意只影响本会话 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;MySQL 8.0 的默认隔离级别是REPEATABLE READ很多中文电子书还停留在旧版写法让你查tx_isolation这个变量在 8.0 已经改名成transaction_isolation。如果你按旧书敲会报Unknown system variable tx_isolation。实操时这样玩会话 A 开启事务并更新一条数据但不提交会话 B 查询同一行。在默认的REPEATABLE READ下B 看到的是旧值把 B 改成READ COMMITTED后如果 A 提交了B 下次查询才能看到新值。这就是“当前读”和“快照读”的直观感受。再进一步在REPEATABLE READ下会话 A 插入一条新记录并提交会话 B 在同一事务里查不到这条记录但用SELECT ... FOR UPDATE又可能看到这正是幻读的根源。这块建议你亲手敲一遍比背十遍定义都管用。3.3 锁等待的现场排查谁堵了谁一目了然锁是生产环境里最容易让 MySQL“假死”的元凶。表象通常是某个更新语句一直卡住不返回等个几十秒后报Lock wait timeout exceeded。遇到这种问题不要重启数据库先查是谁在等锁、谁持着锁-- 查看当前所有正在执行的语句和状态 SHOW FULL PROCESSLIST; -- 查看当前事务和锁等待情况 SELECT * FROM performance_schema.data_lock_waits\G -- 杀掉已经阻塞很久的连接假设线程 id 是 123 KILL 123;SHOW FULL PROCESSLIST里能看到每个连接的Command和State。如果某条UPDATE的State是Waiting for table metadata lock大概率是有人开着事务没提交占了元数据锁如果State是Waiting for lock held by another transaction那就是典型的行锁竞争。data_lock_waits表能直接看到等锁和被锁的线程关系比翻日志快得多。这里有个安全提示KILL要慎用先确认线程 ID 对应的连接不是别的同事在跑批量任务。我一般先SHOW FULL PROCESSLIST看Command是不是Sleep且持续时间很长再决定杀不杀。处理锁问题的第一原则是“找到持有锁的事务并让它尽快提交或回滚”而不是盲目杀连接。4. 从安装到连接的常见翻车现场配置、编码与 SQL 模式排查4.1 报错 2002 连不上本地 MySQLsocket 路径与 service 状态先查热词里那个error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock几乎每个新手都会遇到。现象很直白客户端连接时报错原因是客户端去/tmp/mysql.sock找 socket 文件但服务端把 socket 文件放在别处或者服务端根本没起来。先别急着改配置按顺序做三步排查。# 第一步确认服务进程在不在 ps -ef | grep mysqld # 第二步找到真实的 socket 文件路径 mysql -uroot -p -h 127.0.0.1 -P 3306 # 登录后在 MySQL 里执行 SHOW VARIABLES LIKE socket; # 第三步如果上面能登录说明 TCP 方式正常只是 socket 路径不对 # 连接时显式指定 socket 文件路径例如 mysql -uroot -p --socket/var/lib/mysql/mysql.sockps查不到mysqld进程时问题通常出在服务启动失败这时去看错误日志比瞎猜效率高。日志默认位置一般在/var/log/mysqld.log或/var/lib/mysql/目录下具体以你机器上的配置为准。如果ps能看到进程、但 socket 连接不上多半是客户端和服务端读取的my.cnf不一致/tmp/mysql.sock是编译时的默认路径实际路径可能被改到了/var/run/mysqld/下。这个问题有个很笨但有效的解法连接时不依赖 socket直接用-h 127.0.0.1走 TCP绕开路径分歧。4.2 SQL 模式导致 GROUP BY 报错only_full_group_byMySQL 5.7 以后默认开启了ONLY_FULL_GROUP_BY很多中文电子书里那句“分组查询时可以查非分组字段”已经失效。现象是你执行一条SELECT name, COUNT(*) FROM user GROUP BY status直接报错提示Expression #1 of SELECT list is not in GROUP BY clause。原因是 MySQL 要求SELECT列表里的非聚合列必须出现在GROUP BY子句里。解决有两个方向。业务上如果确实需要查“每组里某个非分组字段的值”改成用ANY_VALUE(name)或子查询取特定行如果你确定自己的业务逻辑不需要这个约束可以关掉但我不推荐。-- 查看当前 sql_mode SELECT sql_mode; -- 只在当前会话关闭 only_full_group_by SET SESSION sql_mode STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION;关掉ONLY_FULL_GROUP_BY会让一些写法回到 5.6 时代的宽松状态GROUP BY只保留一列也能查出非聚合列结果具有随机性取值不一定来自你期望的那一行。我的习惯是改应用 SQL 而不是改全局模式因为全局一放开所有连这个实例的业务都跟着变宽松万一有人写了依赖随机值的 SQL排查起来就麻烦了。4.3 配置文件改了不生效my.cnf 的作用域和顺序修改my.cnf后重启 MySQL发现参数没变这是另一种高频翻车。原因通常是实例启动时读取的配置文件路径和你改的不是同一个。Linux 下可以用下面的命令确认实际读取顺序# 查看 mysqld 实际会读取哪些配置文件 mysqld --verbose --help | grep -A 2 Default options # 查看当前实例实际生效的参数值 mysql -uroot -p -e SHOW VARIABLES LIKE max_connections; # 如果你改的是 [mysqld] 段的参数确认没写进 [client] 或 [mysql] 段mysqld --verbose --help输出的第一行会列出配置文件读取顺序常见的是/etc/my.cnf、/etc/mysql/my.cnf、~/.my.cnf后读的覆盖先读的。你改了/etc/my.cnf但同目录下还有/etc/mysql/conf.d/里的文件也定义了同名参数最终生效的是后面加载的那个。排查办法是在配置文件里加一行注释标记比如# 2025-01-test-change然后SHOW VARIABLES确认当前值里有没有特征变化。这个办法很土但复杂环境里特别好用。4.4 中文乱码从库到表到连接三个层面对齐中文乱码问题本质是字符集不一致。服务端character_set_server是latin1库是utf8mb4连接又是utf8三层各说各话结果就是网页上看到一串问号。我在实验环境里通常这样统一-- 查看当前所有字符集相关变量 SHOW VARIABLES LIKE character_set_%; -- 连接层指定字符集解决当前会话的乱码 SET NAMES utf8mb4;SET NAMES utf8mb4等于同时设置了character_set_client、character_set_connection、character_set_results三个变量。另一个容易忽略的是导入 SQL 文件时的编码Windows 下用记事本另存为 UTF-8 格式的文件可能带 BOMMySQL 会把 BOM 当成字段内容的一部分表现就是第一个字段名或第一行数据前面多了一个不可见字符导入后查询报Unknown column。解决方案是用sed -i 1s/^\xEF\xBB\xBF// 文件名.sql去掉 BOM或者用支持“无 BOM 的 UTF-8”的编辑器重新保存一次。5. 从单机到主从binlog、同步配置与远程表同步实操5.1 先搞懂主从复制在同步什么中文电子书里主从复制章节通常上来就给配置命令很少解释原理。我用一句话说明主库把写入操作记录到 binlog从库通过 IO 线程拉取 binlog 并写入自己的 relay log再由 SQL 线程重放 relay log 里的操作到本地数据文件。所以配置核心只有两件事主库开 binlog从库配置指向主库的连接信息。主从复制常见的用途是读写分离、容灾备份和“把远程库的这张表同步到本地”。最后这个需求要特别注意主从复制是实例级别的不是表级别的。你没法只同步一张表而不同步其他表只能通过replicate-do-table参数让从库只重放指定表的变更但 binlog 仍然会传输所有库的变更。5.2 主库和从库的最小配置模板下面这套配置我按 MySQL 8.0 的常见习惯写主库和从库各自改各自的文件。# 主库 /etc/my.cnf 追加 [mysqld] server_id 1 log_bin /var/log/mysql/mysql-bin binlog_format row expire_logs_days 7# 从库 /etc/my.cnf 追加 [mysqld] server_id 2 relay_log /var/log/mysql/mysql-relay-bin read_only 1主库的binlog_format我建议直接用row不要用statement。row格式记录的是行变更前后的数据主从数据一致性更好statement格式记录的是 SQL 原文遇到NOW()、UUID()这类非确定性函数从库重放结果可能和主库不一致。expire_logs_days 7是控制 binlog 保留时长的避免磁盘被撑爆但注意从库追不上的时候 binlog 被提前清理会导致复制中断这个参数要结合从库延迟情况调整。从库配置里加read_only 1是为了防止有人误写从库造成数据不一致。注意read_only对拥有 SUPER 权限的账号不生效所以运维账号别乱发 SUPER 权限。5.3 从零配置复制的完整命令序列假设主库 IP 是192.168.1.10从库 IP 是192.168.1.11在从库上执行下面的步骤。-- 第一步主库上创建复制专用账号不要用 root 做复制 CREATE USER replica192.168.1.% IDENTIFIED BY StrongPass2024; GRANT REPLICATION SLAVE ON *.* TO replica192.168.1.%; FLUSH PRIVILEGES; -- 第二步主库上锁定表并查看当前 binlog 位置 FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS; -- 记下 File 和 Position 两个值例如 mysql-bin.000003 和 154拿到 binlog 位置后如果主库已有业务数据建议先用mysqldump把数据导出并导入从库再开启复制如果从库直接用空库开启复制会丢失主库历史数据。-- 第三步在从库上配置复制源主库信息按实际填 CHANGE MASTER TO MASTER_HOST192.168.1.10, MASTER_USERreplica, MASTER_PASSWORDStrongPass2024, MASTER_LOG_FILEmysql-bin.000003, MASTER_LOG_POS154; -- 第四步启动复制并检查状态 START SLAVE; SHOW SLAVE STATUS\G第 5 行的\G是把结果纵向输出列太多时横向看会换行纵向更清晰。关注Slave_IO_Running和Slave_SQL_Running两个状态都必须是Yes。Seconds_Behind_Master表示从库落后主库的秒数理想情况是 0。如果 IO 线程报连接错误先确认主库账号权限和防火墙如果 SQL 线程报错通常是主库已有数据和从库冲突导致的常见做法是跳过该事务或重新初始化从库数据这不优雅但有效。5.4 只同步一张表的写法与边界只想同步远程库的某张订单表到本地时很多人的第一反应是弄个定时任务把表导过来。这个方案对数据量小的表没问题但表到几十 GB 级别定时全量导入就没法用了。更合理的方案是先用主从复制把整个实例同步过来再从从库把目标表通过CREATE TABLE ... SELECT或mysqldump导出到本地业务库。从库的replicate-do-table参数可以这样配[mysqld] replicate-do-table sales.orders这个参数写在从库配置里表示“只复制sales库的orders表”多张表就写多行。有两个边界你必须知道第一replicate-do-table对跨库写入不生效比如主库执行INSERT INTO sales.orders SELECT * FROM other.temp这种语句从库可能跳过第二主从复制模式下从库不允许只复制一张表的 DDL 而不同步同库其他表的结构变更实际操作中如果你只设置了表级过滤主库执行DROP DATABASE时从库仍然会执行。所以表级复制适合读多写少、表结构稳定的场景做不到按需订阅某些表却不影响其他表。6. 读完电子书后的自我验收性能参数、存储过程与面试题自测到这一步你已经把一本中文 MySQL 电子书从“看”变成了“做”。但学没学会得靠自测。我的验收清单分三块能不能看懂EXPLAIN输出能不能写一个带游标的存储过程能不能说清一道锁相关的面试题。性能调优这块我不建议你上来就调innodb_buffer_pool_size。先把SHOW GLOBAL STATUS里的Threads_connected、Slow_queries、Innodb_row_lock_current_waits看一遍再决定动哪里。常见的快速配置项参考如下注意这只是起步值不是生产标准。参数建议起始值说明innodb_buffer_pool_size物理内存的 50%~70%决定 InnoDB 缓存数据和索引的内存大小max_connections按业务峰值评估默认 151连接过多时先排查慢查询而不是盲目加高long_query_time2 秒超过该时间的查询记录到慢日志transaction_isolationREAD COMMITTED 或 REPEATABLE READ高并发场景评估是否放宽隔离级别自测存储过程时我建议写一个带DECLARE CONTINUE HANDLER的循环插入这是面试里常考的点也是很多电子书讲得浅的部分。下面这段是我常用的模板DELIMITER $$ CREATE PROCEDURE sp_batch_insert(IN cnt INT) BEGIN DECLARE i INT DEFAULT 0; DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; END; START TRANSACTION; WHILE i cnt DO INSERT INTO user (name, phone) VALUES (CONCAT(用户, i), CONCAT(139, LPAD(i, 8, 0))); SET i i 1; END WHILE; COMMIT; END$$ DELIMITER ; CALL sp_batch_insert(100);DELIMITER $$的作用是把语句结束符临时改成$$因为过程体里有多条以分号结尾的语句不换结束符的话客户端会在第一条分号处就认为是完整语句报语法错误。这个坑在热词“mysql中触发器中分隔符”里也被频繁搜到触发器和存储过程一样都要处理分隔符。最后说下我的习惯。每次翻完一本 MySQL 电子书的索引或事务章节我都会把自己当面试官拿三道题自测联合索引最左前缀是什么RR 隔离级别下幻读为什么还会存在SELECT ... FOR UPDATE和UPDATE在并发下的行为差异。答不上来就回去翻那本书对应的小节再不行就开两个会话复现一次。技术文看到这里希望你能从“读过标题”变成“跑通命令”这条路我已经替你先踩过一遍了希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网