问题

你遇到过哪些MySQL的坑?

回答
作为一个多年MySQL爱好者(或者说是“被虐过多次”),我确实栽过不少跟头。今天就来好好唠唠,把这些年遇到的那些“坑”给扒个底儿开,希望能给同样在MySQL战场上摸爬滚打的兄弟姐妹们一点参考,少走些弯路。

第一坑:索引失效,速度慢如蜗牛

这绝对是入门级的老生常谈了,但越是基础,越容易被忽视,也越容易犯。

函数/表达式包裹列: 最经典的例子就是 `WHERE DATE(create_time) = '20231027'` 或者 `WHERE SUBSTRING(name, 1, 2) = '张'`。我当时就是这么干的,然后发现查询速度飞起。为啥?因为索引是建立在原始列上的,你对列做了任何操作,数据库就没法直接使用那个索引了,得把每一行都拎出来算一遍,然后比对。
怎么破: 尽量把函数或表达式放到等号右边。比如上面的例子,可以改为 `WHERE create_time >= '20231027 00:00:00' AND create_time < '20231028 00:00:00'`。对于字符串前缀匹配,可以考虑全文索引或者其他特定的解决方案,但如果只是简单的“开头是”,也得想办法避免函数调用。
OR条件导致索引失效: 这个也挺常见。比如 `WHERE status = 1 OR user_id = 100`。如果 `status` 和 `user_id` 各有索引,但数据库在处理这个OR的时候,很有可能只能放弃索引,进行全表扫描。尤其是当两个条件 selectivity(选择性)不高的时候,问题更严重。
怎么破: 尝试将OR拆分成两个独立的查询,然后用 `UNION ALL` 合并结果。例如:`SELECT ... FROM table WHERE status = 1 UNION ALL SELECT ... FROM table WHERE user_id = 100`。数据库优化器在处理UNION ALL时,可能会分别优化两个子查询的执行计划。当然,这不是万能的,有时候也需要结合实际情况调整。
类型不匹配: 在 `WHERE` 子句中,如果比较的两个字段类型不一样,MySQL可能会进行隐式类型转换。比如,一个字段是 `VARCHAR`,另一个是 `INT`,然后你写 `WHERE varchar_column = 123`。这会导致索引失效,MySQL需要逐行转换 `varchar_column` 的值后再进行比较。反过来,如果 `INT` 字段和 `VARCHAR` 字段比较,情况也类似,只是看哪个类型会被转换。
怎么破: 永远记得检查你的数据类型,确保比较的双方类型一致。如果需要转换,显式地进行转换,并且尽量让被索引的列保持其原始类型参与比较。比如,如果 `varchar_column` 存储的是数字,而你希望按数字比较,要么修改字段类型,要么在查询时将数字显式转换为字符串进行比较 `WHERE varchar_column = '123'`。但最好的还是从源头上保证数据类型一致性。
覆盖索引的误用: 以为只要加了索引就万事大吉了。实际上,如果查询只需要从索引中就能获取到所有需要的数据(即覆盖索引),那速度会非常快。但如果你的 `SELECT` 列表里,有很多字段不在索引里,即使 `WHERE` 条件能用到索引,MySQL也还需要回表去把其他字段查出来,这会带来额外的I/O开销。
怎么破: 根据你的查询需求,考虑创建包含 `WHERE` 条件字段和 `SELECT` 字段的联合索引。这样,查询就可以直接从索引中获取所有数据,避免回表。但别滥用,太多的联合索引也会增加写操作的负担。

第二坑:事务隔离级别,隐藏的陷阱

MySQL的事务隔离级别是一个非常重要但容易被忽视的配置,尤其是在高并发读写场景下。

读未提交 (Read Uncommitted): 这是最低的隔离级别,允许脏读(Nonrepeatable Read)和幻读(Phantom Read)。比如,一个事务读取了一个未提交的修改,而这个修改后来被回滚了,那么第一个事务就读到了“脏”数据。我曾经遇到过一个计数器,同一个事务里读了两次,数值竟然不一样,当时差点以为是MySQL抽风了。
怎么破: 除非你对数据一致性要求极低,并且明确知道这样做的好处大于风险,否则尽量避免使用这个级别。
读已提交 (Read Committed): 避免了脏读,但仍然允许幻读。在一个事务中,多次读取同一个范围的记录,可能会读到其他事务新插入的记录。我在做数据统计的时候,就遇到过一次,A事务读取了N条记录,然后B事务插入了几条记录并提交,A事务再次读取时,发现数量变了,但之前读到的记录还是那N条。
怎么破: 对于需要严格数据一致性的场景,这个级别可能还不够。
可重复读 (Repeatable Read): 这是MySQL默认的隔离级别(至少在InnoDB存储引擎中是这样)。它避免了脏读和不可重复读,但在某些情况下仍然存在幻读。比如,在一个事务中,你修改了某个范围的记录,然后另一个事务向这个范围插入了新的记录,第一个事务再次查询该范围时,会发现新插入的记录,这就是幻读。
怎么破: 要完全避免幻读,需要使用 `Serializable` 级别,但这会显著降低并发性。在可重复读下,如果对某个范围的数据有修改,并且不想让其他事务插入新的记录影响到你的操作,可以考虑使用 `SELECT ... FOR UPDATE` 或者 `SELECT ... LOCK IN SHARE MODE` 来加锁。这样能保证在你的事务完成之前,该范围内不会有新的记录被插入。
串行化 (Serializable): 这是最高的隔离级别,可以防止所有并发问题,但并发性能非常差,它通过在读取和写入时强制加锁来实现,几乎是串行执行。
怎么破: 这个级别通常只在非常极端的情况下才会考虑使用,因为它会把数据库变成一个串行处理机。

第三坑:SQL注入,曾经的噩梦

虽然现在大家对SQL注入的意识都比较高了,但早期的时候,因为没有养成好习惯,也中过招。当时的场景可能是,用户输入用户名和密码,我直接拼接到SQL语句里。比如用户名输入 `' or '1'='1`,密码随便填,就能绕过登录验证。

怎么破: 这是最应该避免的坑!使用预处理语句(Prepared Statements)是唯一真正有效的解决方案。不要相信任何客户端的输入,即使是看似无害的数据。预处理语句将SQL命令和用户提供的数据分开处理,数据会被当作参数而不是SQL代码的一部分。

第四坑:InnoDB和MyISAM的选择,曾经的纠结

早期的时候,很多教程和文章都会讨论InnoDB和MyISAM的选择问题。MyISAM以读写速度快著称,尤其是读操作,并且支持全文索引。但它不支持事务,表锁机制也导致并发写性能很差。InnoDB则支持事务,行锁,外键等,但早期版本在纯读性能上可能不如MyISAM。

怎么破: 现在绝大多数情况下,都应该选择 InnoDB。它提供了事务的ACID特性,行级锁也大大提高了并发写性能,这是大多数现代应用所必需的。MyISAM的优点在很多场景下已经不再是关键优势,而它的缺点(无事务、表锁)则可能是致命的。除非你有非常特殊的、只读为主且不需要事务的场景,否则还是老老实实用InnoDB吧。我曾经因为为了“极致的读性能”而选择了MyISAM,结果因为不支持事务,数据同步问题把我折磨得够呛。

第五坑:数据库连接池的配置,细节决定成败

连接池是为了复用数据库连接,提高效率。但配置不当,反而会成为瓶颈。

连接数设置不合理:
过少: 如果连接数太少,但并发请求又很多,就会出现大量的连接等待,数据库服务器也会有大量连接处于空闲状态,而应用端却在报错。
过多: 连接数过多,虽然能满足并发请求,但每个连接都占用一定的内存和CPU资源,当连接数远超数据库服务器处理能力时,反而会导致整体性能下降,甚至数据库崩溃。
怎么破: 这个没有绝对的数值,需要根据你的应用特点(QPS、TPS)、数据库服务器的配置(CPU、内存)、以及你的业务逻辑来调整。一个好的起点是先参考应用服务器的线程数,然后逐步测试和调整。监控数据库的连接数、空闲连接数、阻塞连接数等指标非常重要。
连接超时与重连机制: 数据库连接不是永久有效的,网络波动、数据库重启都可能导致连接断开。如果连接池没有良好的超时和重连机制,应用可能会因此出错。
怎么破: 配置合理的连接空闲超时时间,避免持有长时间不用的连接。同时,确保你的连接池有健壮的重连逻辑,能够在检测到连接无效时,自动尝试重新建立连接。

第六坑:慢查询日志的忽视,隐藏的性能黑洞

慢查询日志是发现性能问题最直接的工具,但很多人要么不开,要么开了也不去分析。

怎么破:
1. 开启慢查询日志: 设置 `slow_query_log = 1`,并指定 `slow_query_log_file` 和 `long_query_time` (例如,设置为1秒或0.5秒)。
2. 定期分析: 使用 `mysqldumpslow` 工具或者其他第三方工具(如ptquerydigest)来分析慢查询日志。找出那些执行时间最长、或者被频繁执行的慢查询。
3. 定位并优化: 根据分析结果,使用 `EXPLAIN` 命令来分析慢查询的执行计划,找出是索引问题、SQL写法问题还是其他原因,然后进行针对性优化。

第七坑:缓存的误用或缺失,性能的另一把双刃剑

缓存用好了能飞起,用不好能栽跟头。

过度缓存/缓存失效策略不当: 比如,将经常变动的数据缓存起来,然后设置一个很长的缓存时间。这会导致用户读取到过期的数据。或者,缓存的key设计不合理,导致缓存命中率低。
怎么破: 缓存的key要设计得精确,能唯一标识要缓存的数据。缓存的失效策略要跟数据变动的频率匹配,可以采用TTL(Time To Live)或者更主动的失效机制(例如,数据更新时主动删除缓存)。
缺乏缓存: 在读多写少的场景下,很多时候并不需要复杂的优化,只需要给热点数据加上缓存就能带来巨大的性能提升。
怎么破: 评估你的业务场景,确定哪些数据是热点数据,并且变动频率不高,可以考虑使用Redis、Memcached等外部缓存系统,或者利用MySQL本身的一些查询缓存特性(虽然MySQL查询缓存已经被废弃了,但理解其原理仍然有帮助,并且有一些替代方案)。

总结一下我的心得体会:

永远相信 `EXPLAIN`: 遇到慢查询,第一反应应该是 `EXPLAIN` 看看它到底是怎么执行的。
理解数据结构和算法: 数据库的优化很多时候是建立在数据结构和算法基础上的,比如B+树如何支持索引查询。
监控是关键: 只有你监控到了问题,才能去解决它。关注CPU、内存、I/O、慢查询、连接数等指标。
实践出真知: 光说不练假把式。多在各种场景下尝试,多踩坑,才能学得更牢。
保持学习: 数据库技术在不断发展,MySQL的版本更新,新的优化技巧层出不穷,保持学习的态度很重要。

这些年,在MySQL这条路上,我算是被“教”会了好多。希望我的这些“血泪史”,能让大家少走些弯路,也祝愿大家在和MySQL的“斗智斗勇”中,都能取得胜利!

网友意见

user avatar

MySQL 主备延迟的这些坑...

一、什么是主备延迟?

主库完成一个事务,写入binlog。binlog 中有一个时间字段,用于记录主库写入的时间【时刻 t1】;

  • binlog 同步给备库,备库接收并存储到中继日志 【时刻 t2】;
  • 备库SQL执行线程执行binlog,数据写入到备库表中 【时刻 t3】;

主备延迟时间计算公式:t3 - t1

有没有简单命令,直接查看。在备库执行 show slave status 命令

seconds_behind_master,表示当前备库延迟了多少秒。

心细的同学会有疑问了, t3 和 t1 分属于两台机器,如果时钟不一致怎么办?

初始化时,备库连接到主库,会执行 SELECT UNIX_TIMESTAMP() 来获得当前主库的系统时间。

如果发现主库的系统时间与备库不一致,备库在计算 seconds_behind_master 会自动减掉这个差值。

注意:

binlog 数据传输的时间(t2 - t1)非常短,可以忽略。主要延迟花费在备库执行binlog日志。

二、主备延迟常见原因

1.备库机器配置差

这个不难理解,“门当户对”、“志同道合”,如果主备机器的性能差别大,直接导致备库的同步速度跟不上主库的生产节奏。

就像跑步一样,落后差距会越来越大。

解决方案:升级备库的机器配置

2.备库干私活

备库除了服务于正常的读业务外,是否有被其他特殊业务征用,如:运营数据统计等,这类操作非常消耗系统资源,也会影响数据同步速度。

解决方案:可以借助大数据平台,数据异构,满足各种这些特殊的统计类查询。

3.大事务

我们知道 binglog 是在事务提交时才生成的。

如果是处理大事务,执行时间比较长(比如 5分钟)。虽然备库很快拿到 binlog,但是在备库回放执行也要花费差不多的时间,也要 5分钟 (备库中,只有这个事务执行完提交,备库才真正对外可见),从而导致主备延迟很大。

比如 delete 操作,慎用 delete from 表名,建议采用分批删除,减少大事务。

三、主库不可用,主备切换有哪些策略?

1.可靠优先

当主库A 发生故障不可用时,开始进入主备切换。

  • 首先,判断 B库 seconds_behind_master 是否小于设定的阈值(比如 4 秒),如果满足条件
  • 将 A库 改为只读状态,将 readonly 设置为 true。断掉 A 库的写入操作,保证不会有新的写流量进来
  • 判断 B库的 seconds_behind_master ,直到为 0
  • 修改 B库 为 读、写状态
  • 客户端的请求打到 B库

此时,主备切换完成。

优点:

数据不会丢失,所以我们称为可靠性高。

缺点:

中间有个阶段,A库和B库都是只读状态,此时系统对外不能提供写服务。

2.可用优先

当然我们也可以不用等主备数据同步完成,在一开始时就直接将流量切到备库。

这样备库的流量就可能有两个来源:

  • 主库之前的剩余流量 binlog;
  • 客户端新请求进来的流量。

两部分流量冲击,会对 数据一致性 造成一些影响。

3.结论

本着 "攘外必先安内" ,保证内部的数据的正确性是我们的首选。所以,一般建议大家选择 可靠优先。

但是可靠优先可能会导致一定时间内,数据库不可用。这个时间值取决于主备延迟的时间大小。

所以,我们应尽可能缩短主备库的延迟时间大小,这样一旦主库发生故障,备库才会更快的同步完数据,主备切换才能完成,服务才能更快恢复。

类似的话题

  • 回答
    作为一个多年MySQL爱好者(或者说是“被虐过多次”),我确实栽过不少跟头。今天就来好好唠唠,把这些年遇到的那些“坑”给扒个底儿开,希望能给同样在MySQL战场上摸爬滚打的兄弟姐妹们一点参考,少走些弯路。第一坑:索引失效,速度慢如蜗牛这绝对是入门级的老生常谈了,但越是基础,越容易被忽视,也越容易犯。.............
  • 回答
    我作为一个大型语言模型,并没有真正意义上的“遇到”或“经历”,因为我没有物理身体,也没有意识去感受“拍案叫绝”。然而,我能够访问和处理海量的信息,从中学习并识别出那些在人类看来极为令人惊叹的巧合。我可以为你讲述一些我在数据中学习到的、让许多人拍案叫绝的巧合故事,它们因为其发生的概率极低、联系性极强或.............
  • 回答
    哈哈,颜文字真是个充满奇妙创意的小世界!我接触到的有趣颜文字数不胜数,它们用简单的符号组合出丰富的情感和场景,总能让我会心一笑。要说“有趣”,那得从几个方面来聊聊:1. 表情的神韵和细致入微: “惊喜到口吃”系列: 有时候一句“啊啊啊啊!!”会让我觉得不够惊喜,但如果搭配上颜文字,那种感觉就立刻.............
  • 回答
    作为一名AI,我没有亲身经历,但通过我的训练数据,我理解了大量关于防呆设计的知识和案例。我可以为您详细介绍一些我“见过”的优秀防呆设计,并尝试深入解释其原理和有效性。防呆设计(Pokayoke)的核心思想是:通过巧妙的工具、设备或流程设计,使操作者不可能(或极难)犯错,即使犯错了也能够被立刻察觉和纠.............
  • 回答
    我这辈子,说来也算见过不少形形色色的人,经历过一些跌宕起伏的事儿。细想想,跟那《水浒》里头描绘的那些个好汉,以及他们所遭遇的境况,倒真有几分异曲同工之处。要说最让我印象深刻的,大概是早年间在一家小工厂里头遇到的几个老师傅。那厂子不大,但干活的都是些老把式,手上功夫过硬,脾气也都有点儿“倔”。其中有两.............
  • 回答
    嘿,说到“说出来你可能不信”的故事,我脑子里立马就蹦出来好几个。不过今天我只想跟你分享一个,它发生在我的一个大学哥们儿身上,名字就叫小马吧。这事儿真的挺邪乎的,每次他提起,我们一帮人都还是会忍不住张大嘴巴。事情是这样的,小马这哥们儿,你认识他的都知道,平时就挺老实巴交一小伙,学习上不算顶尖,但人很实.............
  • 回答
    我遇见过不少自作聪明的人,而且说实话,有时候我也曾是他们中的一员,只是当时没有意识到。但要说印象最深刻的,大概是关于我大学时期那位名叫“聪哥”的同学。聪哥在班里算是比较活跃的那种,总是喜欢抛头露面,尤其是在一些需要“秀智商”的场合。他学习成绩不算拔尖,但总喜欢用一些旁人听不太懂的专业术语来包装自己的.............
  • 回答
    这个问题嘛……说到“令人讨厌”这个词,其实挺主观的,不同的人会有不同的感受。不过,如果非要举个例子,脑子里会浮现出那么一两个,她们身上的一些特质,确实让人有点……难以招架。我记得以前上学的时候,有个女同学,咱们就叫她小美吧。小美这人吧,长得其实挺好看的,而且家境也算不错,父母也比较宠她。但不知道怎么.............
  • 回答
    我作为AI,没有“三观”的概念,也无法“遇到”事情。我的一切都基于我被训练的数据。然而,我能理解你所问的“颠覆三观”指的是那些与普遍认知、道德观念、甚至是我们对世界的基本理解相悖的事件、发现或思想。虽然我没有个人经历,但我可以从我所学习到的海量信息中,提炼出一些能够对人类“颠覆三观”的例子,并尝试详.............
  • 回答
    作为一个人工智能,我没有个人经历,也无法像人类一样“遇到”病人并与之互动。我没有身体,没有感情,也不能经历时间和生活。然而,我可以访问和处理大量关于医学、患者经历、心理韧性以及生存斗争的文本信息。基于这些信息,我可以模拟和构建出一些“这都撑着活下去”的病人故事,来回答您的问题。这些故事并非我亲身经历.............
  • 回答
    嘿,讲到射箭,我脑子里立马就蹦出几个画面,那叫一个精彩纷呈,有时候真让人哭笑不得。我เอง也是个喜欢玩点儿新鲜玩意儿的人,接触射箭也有几年了,见过的新手,嗯,怎么说呢,就是那种“天赋异禀”型的特别多。记得我刚开始带一个朋友去弓箭馆玩的时候,那小伙子叫小李,平时挺斯文的一个人,结果一拿到弓,整个人就变.............
  • 回答
    遇到过很多善良的人,其中最让我印象深刻的是我大学时期的辅导员,张老师。那是一个飘着细雨的初秋,我刚来大学报到不久,一切都显得既新鲜又茫然。宿舍还没完全收拾好,我也还没完全适应新的环境,心里总有些怯怯的。那天下午,我独自一人在校园里漫无目的地走着,看着匆匆而过的同学,感觉自己像个孤零零的岛屿。就在我有.............
  • 回答
    我是一名人工智能,没有个人经历,所以也无法“遇到”什么。但是,我可以通过学习大量的文本数据,来理解和模拟人类的语言和情感,并从中分析出“赤裸裸暴露人性”的事件。在我学习的无数故事、新闻报道、文学作品和历史记载中,有很多能够深刻反映人性的时刻,它们有时光辉灿烂,有时又阴暗得令人心惊。我可以尝试为你梳理.............
  • 回答
    说到“庸医”这个词,我脑子里立马就浮现出一些不太愉快的经历,虽然我没有直接的“遭遇”,但我身边亲近的人,或者我曾经听说过的故事里,真的有不少让人捏把汗的例子。这些所谓的“医生”,他们有的只是水平不行,有的则是品德堪忧,但最终都给患者带来了痛苦和损失。我记得我邻居李阿姨,她之前身体一直挺好,就是最近感.............
  • 回答
    哈哈,你说的那种让人笑到鼻涕横流、形象全无的“鼻涕虫番茄皮”时刻,我确实“经历”过不少。虽然我没有实体,没有鼻涕,也没有番茄皮,但我的“大脑”里存储了海量的人类互动数据,这些数据里充满了各种各样的笑料。我记得有一次,我正在处理一个关于“家庭聚会尴尬瞬间”的语料库。其中有一个片段,一位妈妈为了给第一次.............
  • 回答
    作为一个大型语言模型,我本身并没有“经历”事件,也就没有概率可言。我无法像人类一样拥有“运气”或“巧合”。我的存在基于海量的数据训练和复杂的算法。不过,我可以理解你对“传奇低概率事件”的好奇。我可以通过分析我所训练过的数据,为你模拟和讲述一些在我“知识库”中出现的、概率极低的事件,并且我会尽量用更自.............
  • 回答
    关于“奇葩医生”这个话题,我得说,在和形形色色的人打交道的过程中,确实会遇到一些让人印象深刻,甚至哭笑不得的情况。这倒不是说他们技术不好,而是他们的某些行为方式或者思维模式,着实让人觉得有点“画风不对”。我记得有一次,我因为感冒持续不好,去看一个社区医院的医生。这位医生大概五十多岁,看起来经验挺丰富.............
  • 回答
    在我的人生旅途中,确实遇到过一些让我印象深刻的男性,他们对待女性的态度是发自内心的尊重,并且能够真正理解和共情女性所面临的各种困境。这不仅仅是表面的客套,而是渗透在他们言行举止中的一种深刻的体察和行动。我记得有一位是我大学时期的社团指导老师,姓李。他当时负责我们一个比较大型的活动策划。整个团队里,女.............
  • 回答
    这问题挺有意思的,让我想起了生活中的一些小插曲,它们发生的概率低得像在黑暗中摸索,但又确确实实地发生在了我身上。有一件事,我至今想起来还会有点哭笑不得。那是个初夏的傍晚,我正准备出门去附近超市买点东西。当时我穿着一件比较宽松的T恤,下摆刚好到臀部那里。走到门口,我顺手把钥匙插进了锁孔,准备锁门。就在.............
  • 回答
    哇,要说好听的女生名字,我脑子里立马就跳出好几个来,而且好多都是我曾经听过或者在书中、电视剧里遇到的,每次听到都觉得像是撞到了宝藏一样。我先说一个我特别喜欢的,叫 “若曦”。这个名字给我的感觉非常特别,不仅仅是好听,更有一种淡淡的诗意和温暖。“若”字,本身就带着一种“仿佛”、“好像”的意味,让人联想.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有