《MySQL排错指南》——1.5 追踪数据中的错误

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介:

本节书摘来自异步社区出版社《MySQL排错指南》一书中的第1章,第1.5节,作者:【美】Sveta Smirnova(斯维特 斯米尔诺娃),更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.5 追踪数据中的错误

如果你严格地检查查询和更新的结果,就会发现很多问题,这些问题可能持续数周而未被察觉,然后慢慢变得越来越严重,直到最后无可避免地引发很多让人苦恼的问题。然而,问题确实在慢慢地接近你。有时,SELECT查询突然开始返回错误的结果,但是你对该查询的经验使你确信它没有什么问题。

在这种情况下,你应该反向模拟用户操作,直到发现错误的根源。如果幸运,你会一步就发现问题的原因。不过通常你会进行多步操作,有时甚至消耗很长的时间。

大部分这种问题是由于复制环境中主从节点的数据不一致造成的。一个常见的错误情形是期望唯一值的时候出现了重复值(例如,如果用户使用INSERT ON DUPLICATE KEY UPDATE语句,但是主从服务器中的表结构是不同的)。在这样的环境设置下,用户往往直到从节点执行SELECT语句的时候才会发现问题,而不会在INSERT发生时就注意到问题。在循环复制时情况会更糟糕。

为了说明这个问题,我们将使用一个存储过程从保存其他查询结果的临时表向另一个表插入数据。这是另一个常用技巧,用于当用户想要处理大表中的数据,同时担心意外修改数据的风险,或者担心在使用这些大表时对其他应用造成堵塞的情形。

我们来创建表并填充临时数据。在实际应用中,临时表会用于保存等待存入主表的计算结果集:

image

现在向临时表中插入数据:

image

存储例程将临时表中的数据移入主表。它在迁移前会先确认数据在临时表中。我们的版本如下:

image

在调用该存储例程时,如果指定的临时表不存在则会创建新的临时表。这样做可以避免由于临时表不存在而产生问题,但同时也会带来新问题。

提示 提示

该示例使用MAX函数检查表中是否至少存在一行记录。推荐用MAX计数,因为InnoDB表不会保存记录的行数,而是在每次调用COUNT函数的时候现进行计算。因此,MAX(indexed_field)函数比COUNT快。
如果从服务器在第一个插入之后,存储过程调用之前重启,那么从服务器中的临时表将会是空的并且从服务器上的主表没有任何数据。在这种情况下,我们访问主节点会得到:
image

与此同时,在从服务器上得到:

image

更糟的是,如果我们在存储过程调用后向t1表中插入数据,从服务器中的数据将会完全混乱。

假设我们注意到应用程序中主从表读取数据时的错误。现在我们应该弄清数据是怎么插入从表的:是直接更新从服务器还是从主节点复制的数据?

警告 警告

MySQL复制不会帮你检查数据一致性,因此对同一个对象,同时使用SQL复制线程和从节点上的用户线程更新会使数据与主服务器不同,这会导致随后的复制事件失败。
因为我们在示例中模拟这种情形,所以我们知道发生数据损坏问题的关键点:从服务器在第一次插入之后,存储过程调用之前重启了。在实际场景中,问题一般会在用户执行下面查询的时候被发现:

image

当你从SELECT查询中获得非预期结果时,你需要找出该问题发生的原因,是由于查询本身的问题,还是由于早些时候的一些错误引起的。刚才展示的插入非常简单,除非表损坏了,否则它不可能产生错误,因此我们必须回头检查一下表是如何修改的。

通常的示例是在建立在从服务器只读的复制环境下,因此我们可以确保错误产生有两种可能的原因:要么是主服务器插入了错误的数据,要么是数据在复制时损坏。

所以,首先检查主服务器的数据是否有错误:

image

主服务器数据正常,因此问题的原因在于复制层。然而,这是怎么发生的?复制看起来运行正常[3],因此我们猜想是主节点有逻辑错误。当发现了这个可能的原因的时候,你应该去分析存储过程并在主节点上调用以寻找修复方案。

如前所述,在向临时表中插入数据完成复制并清空临时表的事件之后,且在调用查询并向主表插入数据的存储过程之前,重启服务器。因此,从服务器仅是重新创建一个空的临时表并且没有插入任何数据。

在这种情况下,可以选择转换成基于行的复制或者重写存储过程,使其不依赖于已经存在的临时表。另一种方法是清空然后重新填充表,这样突然重启不会导致从服务器数据丢失。

有人可能觉得这个示例太过人为了,你不可能预知服务器何时会突然重启。没错,但是重启确实每时每刻都有可能发生。因此,你需要考虑这样的错误。

事实上,从服务器一个接一个地复制二进制日志事件,当数据在一个原子事件(例如,一个事务或者存储过程调用)中产生时,从服务器不会受上述情况的影响。不过回过头来说,这个示例仅仅是为了说明现实生活中发生的事件背后的原理。

当你在明知是正确的情况下遇到了一个问题时,请检查在看到这个问题之前你的应用程序的运行情况。
关于复制错误的更多详细信息会在第5章进行介绍。

单服务器示例

我曾经处理过一个存储由不同的切割系统产生的度量数据的Web应用程序。用户可以添加一个系统,然后编辑保存度量数据的规则。

我第一次遇到错误的时候,我测试了一个含有系统列表的Web页面:
image

  

该列表不该包含重复的系统,因为描述同样的规则两次是没有意义的。因此我非常惊奇地看到有很多同名的条目。

输出数据的代码使用的是对象,并且,我无法仅通过阅读代码查看发送到MySQL服务器的语句是什么样的:

image
  

我通过日志获取了真实的查询,它看来是正确的:
image

  

接下来,我检查了表的内容:
image

  

SELCET语句准确地返回了表中存在的数据集。我转而检查更新表的代码:

image
  

我再次通过日志获取真实的请求语句:
image

  

该语句也是正确的!id似乎是自增的字段,因此会自动设置。

不过同时,该语句也暴露了潜在的问题:它一定在没有检查唯一性的情况下重复执行了。带着这种假设,我决定检查一下表的定义:

image
  

问题的源头很显然了:name字段没有定义成UNIQUE(唯一的)。当创建表的时候,我通常使用id作为唯一标识符,但是我也会使用MySQL的特性在INSERT时去给id生成一个自增的值,没有什么使我避免重复使用同一个name。

为了解决这个问题,我手动删除了多余的行并且增加了唯一(UNIQUE)索引。

image
  
我们已经介绍完了与错误结果相关的问题,接下来将介绍其他一些经常发生的问题。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
10月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
8月前
|
SQL 人工智能 关系型数据库
如何实现MySQL百万级数据的查询?
本文探讨了在MySQL中对百万级数据进行排序分页查询的优化策略。面对五百万条数据,传统的浅分页和深分页查询效率较低,尤其深分页因偏移量大导致性能显著下降。通过为排序字段添加索引、使用联合索引、手动回表等方法,有效提升了查询速度。最终建议根据业务需求选择合适方案:浅分页可加单列索引,深分页推荐联合索引或子查询优化,同时结合前端传递最后一条数据ID的方式实现高效翻页。
416 0
|
7月前
|
存储 关系型数据库 MySQL
在CentOS 8.x上安装Percona Xtrabackup工具备份MySQL数据步骤。
以上就是在CentOS8.x上通过Perconaxtabbackup工具对Mysql进行高效率、高可靠性、无锁定影响地实现在线快速全量及增加式数据库资料保存与恢复流程。通过以上流程可以有效地将Mysql相关资料按需求完成定期或不定期地保存与灾难恢复需求。
542 10
|
8月前
|
SQL 存储 缓存
MySQL 如何高效可靠处理持久化数据
本文详细解析了 MySQL 的 SQL 执行流程、crash-safe 机制及性能优化策略。内容涵盖连接器、分析器、优化器、执行器与存储引擎的工作原理,深入探讨 redolog 与 binlog 的两阶段提交机制,并分析日志策略、组提交、脏页刷盘等关键性能优化手段,帮助提升数据库稳定性与执行效率。
205 0
|
11月前
|
关系型数据库 MySQL Linux
在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾
以上就是在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾的步骤。这个过程就像是一场接力赛,数据从MySQL数据库中接力棒一样传递到备份文件,再从备份文件传递到其他服务器,最后再传递回MySQL数据库。这样,即使在灾难发生时,我们也可以快速恢复数据,保证业务的正常运行。
504 28
|
Java 关系型数据库 MySQL
SpringBoot 通过集成 Flink CDC 来实时追踪 MySql 数据变动
通过详细的步骤和示例代码,您可以在 SpringBoot 项目中成功集成 Flink CDC,并实时追踪 MySQL 数据库的变动。
3052 45
|
12月前
|
存储 SQL 关系型数据库
【YashanDB知识库】MySQL迁移至崖山char类型数据自动补空格问题
**简介**:在MySQL迁移到崖山环境时,若字段类型为char(2),而应用存储的数据仅为'0'或'1',查询时崖山会自动补空格。原因是mysql的sql_mode可能启用了PAD_CHAR_TO_FULL_LENGTH模式,导致保留CHAR类型尾随空格。解决方法是与应用确认数据需求,可将崖山环境中的char类型改为varchar类型以规避补空格问题,适用于所有版本。
|
10月前
|
存储 SQL 缓存
mysql数据引擎有哪些
MySQL 提供了多种存储引擎,每种引擎都有其独特的特点和适用场景。以下是一些常见的 MySQL 存储引擎及其特点:
263 0
|
12月前
|
SQL 关系型数据库 MySQL
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
本文探讨了在使用YMP 23.2.1.3迁移MySQL Server字符集为latin1的中文数据至YashanDB时出现乱码的问题。问题根源在于MySQL latin1字符集存放的是实际utf8编码的数据,而YMP尚未支持此类场景。文章提供了两种解决方法:一是通过DBeaver直接迁移表数据;二是将MySQL表数据转换为Insert语句后手动插入YashanDB。同时指出,这两种方法适合单张表迁移,多表迁移可能存在兼容性问题,建议对问题表单独处理。
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
|
12月前
|
缓存 NoSQL 关系型数据库
Redis和Mysql如何保证数据⼀致?
1. 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不⼀致 2. 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中 这种⽅案能解决1 ⽅案的问题,但是在⾼并发下性能较低,⽽且仍然会出现数据不⼀致的问题,⽐如线程1删除了 Redis缓存数据,正在更新Mysql,此时另外⼀个查询再查询,那么就会把Mysql中⽼数据⼜查到 Redis中 1. 使用MQ异步同步, 保证数据的最终一致性 我们项目中会根据业务情况 , 使用不同的方案来解决Redis和Mysql的一致性问题 : 1. 对于一些一致性要求不高的场景 , 不做处理例如 : 用户行为数据 ,