拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

MySQL进阶实战:从SQL查询优化到主从同步全攻略

MySQL进阶实战:从SQL查询优化到主从同步全攻略

如果你正跟着教程刷MySQL,或者干脆是“第一天装环境、第二天建表、第三天开始查数据”的节奏,那你大概率会遇到我这次要聊的内容。

Day3-MySQL-SQL-2,粗看像是某个课程笔记的编号,实际意思是:入门MySQL的第3天,从SQL基础语句进阶到SQL实用技巧的第二讲。因为前面已经花了时间把库表建出来、把增删改查跑通,到了这一步,重点就不再是“我能查出来”,而是“怎么把SQL写得又快又对、怎么避免写出有安全破绽的语句、以及真的在线上跑挂了该怎么排查”。今天这篇就是围绕这几个方向展开,把SQL查询进阶、安装部署、连接报错、慢查询分析、主从同步这些高频场景一次性串起来。

适合刚装好MySQL、能跑通基础SELECT但还不熟练的朋友,也适合已经在写业务代码、遇到慢SQL或数据同步需求时需要系统化思路的开发者。我会尽量用大白话讲清楚每个步骤背后的为什么,让你不只是抄命令,而是真的知道自己在干什么。

1. 环境准备:先把MySQL跑起来,后面才不会“劝退”

学SQL有个很尴尬的阶段:语法还没学会,环境先装崩了。我见过太多人卡在“MySQL安装配置教程”这一步,最后放弃学习。所以别急着去看JOIN和子查询,先确保本机的MySQL真的是一个靠谱的、能长期用的环境。

1.1 安装MySQL的两种主流方式,以及为什么我更推荐后者

如果你用的是Windows,最省事的办法是去MySQL官网下载MySQL Installer。这里注意一个细节:官网下载页会把“MySQL Community Server”和“MySQL Installer”放在一起,很多人点进去下了一堆没用的组件。其实你只需要MySQL Server本体,加上MySQL Workbench(图形工具)就够了。

下载后选“Server only”模式,安装完会弹出配置向导。我个人强烈建议在这一步把端口保持3306、字符集选utf8mb4、认证方式选“Use Strong Password Encryption”(MySQL 8默认方式)。字符集和认证方式这两个选项,新手常忽略,后面遇到乱码和连接报错再回头改就麻烦了。

如果是Linux环境,尤其是CentOS、RHEL这类系统,官方提供的rpm安装包是最稳的。很多人图省事直接从系统自带源装,结果装出来一个老版本MySQL,后面跑SQL和教程对不上。正确做法是先到MySQL官方yum仓库,下载对应系统版本的rpm包,然后执行:

rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm yum install mysql-server -y

装完之后启动并做基础安全设置:

systemctl start mysqld systemctl status mysqld grep 'temporary password' /var/log/mysqld.log mysql_secure_installation

第一次安装完MySQL会生成一个临时密码,写在日志文件里。你登录后第一件事就是改密码,然后再跑安全配置脚本。这个脚本会问你是否删除匿名用户、禁止root远程登录等,建议全部选yes。我后面排查连接问题的时候,发现很多所谓的“连不上”都是因为没做安全初始化,root密码还是空,或者匿名用户抢占了权限。

另外多说一句,现在生产环境也在大量用Docker部署MySQL,比如docker run -d --name mysql8 -e MYSQL_ROOT_PASSWORD=1234 -p 3306:3306 mysql:8.0这种方式。容器化的好处是干净、隔离、可快速销毁重建,但新手阶段还是建议先在本机原生安装一次,因为你会更清楚配置文件在哪、日志在哪,排查问题的时候不容易晕头转向。

1.2 连接报错排查:见过最多的3个报错和解决套路

装好MySQL之后,很多人第一个拦路虎就是连不上。我筛选了搜得最多的几个报错(mysql ssl连接错误、error 2002、e0434352),基本覆盖了90%的新手踩坑场景。

第一个是ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'。这个报错的意思很直白:客户端在本地找不到MySQL的socket文件。最常见的原因是MySQL服务根本没启动。你先确认进程在不在:

systemctl status mysqld # 或者 ps -ef | grep mysqld

如果进程确实没起来,看错误日志/var/log/mysqld.log。十有八九是数据目录初始化失败或者配置文件权限不对。再有一种可能是socket文件路径和配置文件不一致,你可以用mysql --socket=/var/lib/mysql/mysql.sock -uroot -p指定socket路径临时绕过,然后回头把my.cnf里的socket路径统一。

第二个是SSL连接错误,比如ERROR 2026 (HY000): SSL connection error。MySQL 8默认开启了SSL,如果客户端和服务端TLS版本不匹配,就会报这个。解决办法有两种:要么在连接命令后面加--ssl-mode=DISABLED(仅限测试环境),要么把服务端my.cnf里tls_version配置调整成与客户端兼容。生产环境不建议关SSL,但你可以用--ssl-mode=PREFERRED让连接自动协商。

第三个是0xe0434352,这个其实是.NET程序里的CLR异常,经常出现在Windows下用MySQL连接器做开发的时候。报错本身只是一个外壳,真正的异常细节要去Windows事件查看器里看Application日志。大部分情况下是MySQL Connector/NET版本和你的.NET运行时版本不匹配,统一到一起就能解决。

排查连接问题有一条通用思路:先确认网络通不通(ping + telnet端口),再确认认证通不通(账号密码与host授权),最后确认协议通不通(socket还是TCP,SSL还是非SSL)。很多人一上来就怀疑配置文件,其实80%的问题都出在前两步。

2. 第3天核心:SQL查询进阶,把数据“查明白”

环境准备好了,接下来是今天的主菜:SQL本身。既然标题叫SQL-2,说明你已经过了基础语法关。还没过关也没关系,这一节我直接把最常用、最高频的进阶查询场景拆开讲,你跟着敲一遍,基本就熟了。

2.1 条件筛选:从WHERE到组合条件,别再用一条条数据肉眼找

SQL查询的本质是“按条件过滤数据”。很多新手写的SQL一股脑SELECT *,然后把结果拉到Excel里再用筛选功能慢慢找。这在数据量小的时候没问题,一旦表里几十万行,这种方式既不优雅也浪费时间。

我建议你从今天开始养成一个习惯:写SQL之前,先想清楚你要的是“哪几列、哪几行”,而不是“整张表”。条件筛选的核心是WHERE子句,它可以组合普通条件、范围条件、模糊匹配和集合匹配:

-- 普通等值条件 SELECT user_id, user_name, created_at FROM t_user WHERE status = 1; -- 范围条件 SELECT order_id, amount, created_at FROM t_order WHERE amount BETWEEN 100 AND 500; -- 模糊匹配 SELECT user_name FROM t_user WHERE user_name LIKE '张%'; -- 集合匹配 SELECT user_name FROM t_user WHERE city IN ('北京', '上海', '广州');

这里有个常见的坑:LIKE + 通配符。很多人以为LIKE '%关键词%'就是万能匹配,却忽略了这个写法会导致索引失效。如果数据量大,这往往就是慢SQL的根源之一。后面讲优化的时候我会细说。

WHERE子句同时支持AND和OR,复杂条件记得用括号分组。比如“查询状态正常且金额在100到500之间,或者用户等级为VIP的订单”,写出来就是WHERE (status = 1 AND amount BETWEEN 100 AND 500) OR user_level = 'VIP'。别小看括号,很多人漏了括号,结果逻辑完全跑偏,最后查出来的数据莫名其妙。

2.2 排序、去重、空值处理:日常数据清洗三板斧

这一节对应的热搜词是“mysql排序”“sql语句去重”“sql去除空值”。我每次带新人,都会专门让他们把这三件事练熟,因为接手任何旧系统的第一周,你几乎天天都在做数据清洗。

排序用ORDER BY,默认是升序(ASC),降序加DESC。多字段排序时,排序列用逗号隔开,从左到右决定优先级。比如“先按创建时间降序,同一时间的再按金额降序”:

SELECT order_id, created_at, amount FROM t_order ORDER BY created_at DESC, amount DESC;

这个顺序很重要,因为排序字段从前到后是嵌套关系——只有前一个字段相等时,后一个字段才会起作用。如果写反了,结果可能让你怀疑人生。

去重有两种常见写法:SELECT DISTINCT和GROUP BY。DISTINCT适合对单列或组合列去重:

SELECT DISTINCT city FROM t_user;

如果要“去重后统计数量”,直接用COUNT(DISTINCT 列名),一步到位。很多人会先查出来再数,反而多绕一圈。

空值处理是SQL里最容易写错的地方。首先明确一个概念:NULL不是空字符串,也不等于'',更不等于0。它是一个“未知的值”。判断NULL必须用IS NULL或IS NOT NULL,用= NULL是查不出来的:

-- 查询没有填写手机号的用户 SELECT user_id, user_name FROM t_user WHERE phone IS NULL; -- 查询填写了手机号的用户 SELECT user_id, user_name FROM t_user WHERE phone IS NOT NULL;

如果你想把NULL显示成其他值,用IFNULL(列名, 默认值)或者标准SQL里的COALESCE(列名, 默认值):

SELECT user_id, IFNULL(nick_name, '未设置昵称') AS display_name FROM t_user;

这里有个很容易被忽略的细节:排序时空值默认是排在最前面的(升序时)。如果业务上希望“有值优先”,可以在ORDER BY里加IS NULL判断:ORDER BY phone IS NULL, phone。这个技巧在整理客户数据时特别实用。

2.3 聚合统计别只靠Excel,GROUP BY才是正道

很多做过数据分析的人,习惯把MySQL当成一个“取数工具”,把明细数据导出来,再用Excel做透视表。这种方式在小数据量时没问题,但一旦涉及几十万行甚至更多,Excel直接卡爆。

SQL里的GROUP BY就是“数据库里的透视表”。它配合聚合函数(COUNT、SUM、AVG、MAX、MIN)能直接完成分组统计,比如按省份统计用户数、按月份统计订单总额:

SELECT province, COUNT(*) AS user_cnt FROM t_user GROUP BY province; SELECT DATE_FORMAT(created_at, '%Y-%m') AS month, SUM(amount) AS total_amount, AVG(amount) AS avg_amount FROM t_order GROUP BY DATE_FORMAT(created_at, '%Y-%m');

注意GROUP BY的一个硬性规则:SELECT后面出现的普通列,必须出现在GROUP BY里。比如你想按月份统计订单,那SELECT里除了聚合函数,只能出现月份这个分组字段。很多人在这里报错ONLY_FULL_GROUP_BY,不是因为SQL写错了,而是因为MySQL 5.7之后默认开启了严格模式,不允许SELECT一些“组团后无意义”的列。

如果你想在分组后再过滤,不能用WHERE了,得用HAVING。区别在于:WHERE是在分组前过滤原始行,HAVING是在分组后过滤聚合结果。举个例子,找出订单数超过100单的省份:

SELECT province, COUNT(*) AS order_cnt FROM t_order GROUP BY province HAVING COUNT(*) > 100;

很多入门教程和面试题都喜欢考“WHERE和HAVING的区别”,实际工作中这个区别也确实影响SQL正确性。记住一句话:WHERE管“哪些行参与计算”,HAVING管“哪些结果能留下”。

2.4 多表连接:JOIN是SQL进阶的第一道门

如果说WHERE、GROUP BY、ORDER BY是“单表三板斧”,那JOIN就是真正把SQL从“查表工具”变成“数据关系分析工具”的关键。前面第2天你应该已经建了好几张表,今天可以把它们真正关联起来。

最常见的场景是:订单表只存用户ID,用户表存用户姓名。如果我想查“每个订单对应的用户名”,就得把两张表连接起来:

SELECT o.order_id, o.amount, u.user_name FROM t_order o INNER JOIN t_user u ON o.user_id = u.user_id;

这里o和u是表的别名,能让SQL短一点,也更清晰。JOIN的类型有INNER JOIN(只返回匹配上的)、LEFT JOIN(左表全保留,右表没匹配上就补NULL)、RIGHT JOIN(右表全保留),实际工作中LEFT JOIN最常用,RIGHT JOIN几乎可以统一改写成LEFT JOIN,语法更好理解。

JOIN的核心是理解ON后面的关联条件。很多人会把WHERE里写筛选条件,ON里却写了关联条件,其实ON也可以混合筛选条件。比如“查所有用户,以及他们在2024年下的订单”:

SELECT u.user_id, u.user_name, o.order_id, o.amount FROM t_user u LEFT JOIN t_order o ON u.user_id = o.user_id AND o.created_at BETWEEN '2024-01-01' AND '2024-12-31';

注意这里我把时间条件写到ON里而不是WHERE里,这是LEFT JOIN的一个关键用法:如果写到WHERE里,会把没订单的用户也过滤掉,LEFT JOIN就变成了INNER JOIN的效果。很多刚接触LEFT JOIN的人都会在这上面栽跟头。

JOIN多了以后,SQL会变复杂,性能问题也容易冒出来。一个经验法则:JOIN的表越多,查询越慢。能拆开的尽量拆开,能先过滤再JOIN的优先过滤。比如不要WHERE一个大范围然后JOIN三张表,可以先在子查询里把数据量缩小,性能会好很多。这个我们放到慢SQL优化那节再展开。

3. 安全底线:SQL注入必须懂,但学的目的是防

在热搜词里,我注意到“sql注入万能密码绕过”和“fofa查询sql注入”这类搜索。这里必须先把话说明白:SQL注入是一种严重的安全漏洞,学习它的目的只有一个——写出没有漏洞的代码。我不鼓励任何人用SQL注入去攻击别人的系统,那是违法的,而且毫无职业成就感可言。真正专业的做法是理解漏洞成因,然后彻底堵住它。

3.1 什么是SQL注入,为什么会发生

SQL注入的根本原因是:把用户输入直接拼接进了SQL字符串里,导致用户输入被数据库当成了SQL代码执行。举个最常见的登录场景,一个新手可能会这么写校验逻辑:

SELECT * FROM t_user WHERE user_name = '用户输入' AND password = '用户输入';

如果程序把用户输入直接拼上去,而用户在用户名框里输入的是admin' --,拼出来的SQL就变成了:

SELECT * FROM t_user WHERE user_name = 'admin' -- ' AND password = 'xxx';

在MySQL里,--后面是注释。也就是说,密码校验直接被注释掉了,如果表里有admin这个用户,攻击者不需要知道密码就能以admin身份登录。这就是早年各种“万能密码”的根本原理,不是什么神奇的黑魔法,只是利用了“输入被当成代码执行”这个缺陷。

为什么会发生?本质是信任了不该信任的数据。用户输入天然是不可信的,你无法预知用户会输入什么。如果代码把输入当成“纯粹的字符串”处理,没问题;一旦把输入当成“SQL片段”拼进去,风险就产生了。

3.2 防御三件套:参数化查询、输入校验、最小权限

既然知道了成因,防御就顺理成章:让用户输入永远只当字符串,不当代码。最可靠的手段就是参数化查询,也叫预处理语句。

不同语言的写法略有差异,但核心思想都一样:先用占位符把SQL结构固定下来,再把值作为参数传进去。这里分别看Java和Python的写法。

Java的JDBC预处理:

PreparedStatement ps = conn.prepareStatement( "SELECT * FROM t_user WHERE user_name = ? AND password = ?" ); ps.setString(1, userName); ps.setString(2, password); ResultSet rs = ps.executeQuery();

Python的pymysql写法:

cursor.execute( "SELECT * FROM t_user WHERE user_name = %s AND password = %s", (user_name, password) )

注意POSTGRESQL风格的写法(%s)和MySQL Connector/Python略有区别,但原理一样:数据库驱动会把参数序列化成合法的字符串字面值,而不是SQL片段。即使输入admin' --,它也只会被当成“用户名的一部分”,注释符号在此刻失去作用。

第二个防线是输入校验。比如用户名只允许字母、数字、下划线,那就在业务层直接拦掉其他字符。这不只是为了防注入,也让数据更规范。可以自己写正则,也可以用白名单校验。注意校验规则要用“只允许合法内容”,而不是“拦截已知危险内容”——因为攻击手段是不断演进的,白名单比黑名单安全得多。

第三个防线是最小权限。给应用连数据库的账号,只授予它需要的库表权限。比如一个只做查询的应用,账号就只要SELECT权限,这样即使被注入,攻击者也做不了破坏。很多人图省事直接用root连应用数据库,这是最危险的做法之一。一旦应用被打穿,等于整台数据库都裸奔了。

3.3 已有代码怎么自查:三分钟快速定位风险点

接手旧项目时,第一件事就是排查历史SQL是不是都是参数化写法。这里给一个最快的方法:全局搜索代码里的SQL语句,看有没有用字符串拼接加+或+的方式拼SQL。比如Java里如果有:

String sql = "SELECT * FROM t_user WHERE name = '" + name + "'";

这基本就是明摆着的SQL注入漏洞,必须改成参数化。

除了代码层面的自查,数据库账号上也可以加固:show grants查看账号权限;select user, host from mysql.user看有没有无密码用户;show variables like 'sql_mode'确认是否为严格模式。这些排查动作花不了几分钟,但能把风险降一个数量级。

4. 从“能跑”到“跑得快”:慢SQL优化实战

热词榜上“慢sql优化”和“并行sql优化”出现的频率很高,说明这是大家真正头疼的问题。我自己的经验是:代码写得再花哨,一条慢SQL就能把整个服务的响应时间拖垮。下面是我在实际优化中觉得最有效的几个步骤。

4.1 一条慢SQL是怎么拖垮整个业务的

先讲个真实场景:某天凌晨2点,线上告警——所有用户的订单列表接口响应时间超过5秒。一查数据库,有一张订单表已经积累了1000万条数据,而业务代码还在用SELECT * FROM t_order WHERE user_id = ?这种基础查询。

为什么慢?因为user_id字段上没有索引,MySQL只能从头到尾扫一遍整张表(全表扫描),再逐行匹配。1000万行,每行都要看一遍,再快也扛不住。而且这个查询一多,数据库的CPU、IO和内存全被占满,其他正常查询也被连带拖慢。这就是慢SQL的“放大效应”——一条慢查询,拖死一个库的并发能力。

定位慢SQL的第一步是打开慢查询日志。在my.cnf里配置:

slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 1

long_query_time设置为1秒,意思是超过1秒的查询都会记录下来。跑一段时间后,打开这个日志文件,就能看到最消耗时间的SQL是哪些。也可以用mysqldumpslow工具做汇总排序,快速找到TOP N。

4.2 看懂EXPLAIN,比背优化口诀更管用

拿到一条慢SQL,很多人第一反应是“加索引”。加之前,我强烈建议先做一次执行计划分析:

EXPLAIN SELECT * FROM t_order WHERE user_id = 10086;

输出结果里重点看这几列:

  • type:访问类型。从好到差大致是const、ref、range、index、ALL。ALL就是全表扫描,基本等于宣告“很慢”。
  • key:实际用到的索引名称。如果是NULL,说明没用到索引。
  • rows:预估要扫描的行数。这个数字越大越危险。
  • Extra:如果出现Using filesort或Using temporary,说明查询里有额外的排序或临时表操作,通常都有优化空间。

举个例子,如果EXPLAIN结果type是ALL,rows等于10000000,你要做的第一件事不是改SQL逻辑,而是看user_id上有没有索引:

SHOW INDEX FROM t_order;

如果确实没有索引,加上:

ALTER TABLE t_order ADD INDEX idx_user_id (user_id);

加完再看EXPLAIN,type会变成ref,rows也会大幅下降。这一步做完,90%的简单慢SQL都能解决。

4.3 索引使用的几个经典坑

加索引不是万能药。我见过不少人加了索引还是慢,主要踩了以下几个坑。

第一个坑是索引列上用了函数或计算。比如WHERE DATE(created_at) = '2024-01-01',这里对created_at用了DATE()函数,索引就失效了。正确写法是把范围写全:WHERE created_at >= '2024-01-01 00:00:00' AND created_at < '2024-01-02 00:00:00'。这样既能用上索引,语义也更清晰。

第二个坑是最左前缀原则。复合索引(user_id, status, created_at)可以匹配user_id、user_id+status、user_id+status+created_at,但如果跳过了第一列直接用status查询,索引是不会生效的。复合索引的列顺序非常重要,往往需要根据实际查询的频率来设计。

第三个坑是深分页。ORDER BY created_at DESC LIMIT 100000, 20这种写法,MySQL会先扫描10万行,再扔掉前10万行,只返回最后20行。数据量大的时候,这就是噩梦级别的慢。优化思路是“先取主键ID,再回表查数据”:

SELECT t.* FROM t_order t INNER JOIN ( SELECT id FROM t_order ORDER BY created_at DESC LIMIT 100000, 20 ) tmp ON t.id = tmp.id;

子查询里只查主键,MySQL可以用覆盖索引快速跳过10万行,然后再回表取完整数据,性能能提升好几个数量级。

4.4 并行SQL优化思路

“并行sql优化”这个热词,我想单独聊聊。并行并不是指在一条SQL里加什么魔法参数,而是指“同时执行多条SQL来分摊压力”。最常见的场景是批量导出、批量统计时,把一个大任务拆成多个小任务并发执行。

比如你要统计1000万条订单里每个城市的总金额,单条SQL不管怎么优化都要全表扫描。如果机器有4个CPU核心,可以考虑按城市名做哈希分片,把城市拆分成4批,用4个并发连接同时执行4条SELECT city, SUM(amount) FROM t_order WHERE city IN (...) GROUP BY city,最后在应用层合并结果。这样理论上能缩短到近四分之一的耗时。

另一个跟并行相关的方向是MySQL 8.0的parallel相关特性,但注意,MySQL本身在单条SQL内部没有类似PostgreSQL那样的并行扫描能力(至少到8.0的常规版本里还没有),所以往往还是要靠应用层并发。如果业务上有特别重的大查询,也可以考虑把分析型查询放到专门从库上执行,让主库专心做事务型写入,这也是一种更保守也更容易落地的“并行”思路。

5. 主从复制与数据同步:把远程库的表同步到本地

搜热词里有一句完整的需求:“把远程库的这张表同步到本地。提供详细操作步骤。”这确实是非常典型的运维场景。我拆成两种方案:一次性同步和长期持续同步。

5.1 一次性同步:mysqldump导出再导入

如果只是临时把这个表拉下来看看,或者做一次迁移,用mysqldump最直接。假设远程库在192.168.1.10,目标表是t_user,本地库是test_db:

# 在远程服务器上导出单表 mysqldump -h 192.168.1.10 -uroot -p test_db t_user > t_user.sql # 把这导出的文件传到本地 scp t_user.sql user@local-server:/tmp/t_user.sql # 在本地导入 mysql -uroot -p test_db < /tmp/t_user.sql

如果只想导数据,不导表结构(假设本地已经有相同结构的表),加--no-create-info参数。如果担心锁表影响线上业务,可以加分--single-transaction,这会用InnoDB的事务隔离来实现一致性快照,不会长时间锁表。

这个方案适合“一次性操作”。缺点是如果表很大,导出和导入都很耗时间;而且如果本地需要持续跟着远程数据更新,靠手动导就不现实了。

5.2 长期同步:MySQL主从复制怎么配

如果要持续把远程库的表同步到本地,最标准的方案是主从复制。这里“主”是远程生产库,“从”是本地库。配置步骤我尽量写细一点。

第一步,在主库开启binlog并设置server-id。在远程服务器的my.cnf里加:

[mysqld] server-id = 1 log-bin = mysql-bin binlog_format = ROW

然后重启MySQL服务,确认binlog生效:

SHOW MASTER STATUS;

记录下输出的File和Position,比如mysql-bin.000001和154。

第二步,在主库创建专门用于复制的账号:

CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongPass@123'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES;

给复制的权限要克制,只给REPLICATION SLAVE就够了,不要给DROP、INSERT这些,避免从库账号被滥用。

第三步,在从库(本地库)配置my.cnf:

[mysqld] server-id = 2

然后重启本地MySQL服务。

第四步,在从库执行CHANGE MASTER TO,把主库信息写进去:

CHANGE MASTER TO MASTER_HOST = '192.168.1.10', MASTER_USER = 'repl', MASTER_PASSWORD = 'StrongPass@123', MASTER_LOG_FILE = 'mysql-bin.000001', MASTER_LOG_POS = 154; START SLAVE;

如果只想同步某一张表,可以在从库的my.cnf里配置过滤器:

[mysqld] replicate-do-table = test_db.t_user

再重启MySQL。这样本地从库只同步指定表,其他表的更新都不会过来。这个replicate-do-table可以写多行,支持多张白名单表。

第五步,检查同步状态:

SHOW SLAVE STATUS\G

重点看两个字段:Slave_IO_Running和Slave_SQL_Running。两个都必须是Yes。如果Slave_IO_Running是No,通常是网络不通、账号密码不对,或者MASTER_LOG_FILE参数位置不对。如果Slave_SQL_Running是No,通常是本地表结构和主库表结构不一致,比如缺列、列类型不同,需要先对齐表结构。

主从复制的原理一句话就能讲明白:主库把每一次写操作记录到binlog,从库拉binlog并重新执行一遍,最终让数据跟主库保持一致。它不是“复制一条最新的记录”,而是“复制整个变更历史”,所以历史数据也会被同步过来,这既是它的能力,也是它的代价——binlog会占用磁盘空间,要注意定期清理。

5.3 同步后的校验与自动化

配置完同步,别直接就跑,先做一次校验。最简单的校验方式:在主库和从库分别执行:

SELECT COUNT(*) FROM test_db.t_user; SELECT MAX(updated_at) FROM test_db.t_user;

两边数字一致,说明同步基本正常。更严谨一点,可以用pt-table-checksum这类工具做全量校验,不过新手阶段先跑这两条SQL就够了。

如果还想让同步更省心,可以写一个定时脚本,通过SHOW SLAVE STATUS检查两个Running状态,异常时发告警。也可以直接用Zabbix、Prometheus这类监控系统监听MySQL的从库状态指标。记住一个核心原则:主从复制的核心价值是“实时数据副本”,它不能替代备份。备份该做还是要做,不要以为有了从库就万事大吉。

6. 结语与下一步:第3天之后该练什么

写到最后,我分享一下我自己在实际项目里的体会。SQL这个东西,看起来语法不难,真正能让你拉开差距的从来不是记住多少函数,而是两件事:第一,能不能写出逻辑正确、性能可用的查询;第二,出了问题能不能有章法地排查。

所以我建议你今天学完这些内容后,不要急着往后赶进度。把前面这些SQL在本地多敲几遍,尤其把JOIN和GROUP BY的组合用熟,再把主从同步的配置亲手搭一遍,踩一遍坑会比看上十遍教程管用得多。后面几天如果继续往索引优化、事务隔离、存储过程这些方向走,你会发现今天打下的基础都在为它们铺路。

最后再分享一个小技巧:每次学完一个新SQL语法,都顺手把它放到一个专门的practice.sql文件里,配上注释和示例数据。过几个月再翻出来看,你就能清楚看到自己的成长曲线。这条经验是我自己踩过几年坑之后总结出来的,希望对你有用。

返回列表