《MySQL排错指南》——1.3 当错误可能由之前的更新引起时

1.3 当错误可能由之前的更新引起时

如果SELECT查询返回了非预期的结果集,这并不总是意味着查询语句本身有错误,也有可能是因为你以为已经进行了插入、更新或者删除等操作,而事实上它们并未生效。

在你调查这种可能之前,你应该先完全仔细检查前一节讨论的SELECT语句编写错误的问题。在SELECT语句编写正确并且能够返回你想要的值的情况下,现在我开始调查由数据本身的问题导致错误的可能性。为了确认问题是由数据本身而非SELECT语句产生的,我尝试精简语句,使其变成某个独立表的简单查询。如果是小表,那么移除所有的WHERE条件和GROUP BY语句,然后通过“野蛮”的SELECT FROM table - name检查所有的数据。对于大表来说,用WHERE条件来筛选出你想要的值是明智的选择。如果你仅关心查询结果集的条数是否和预期的一致,也可以考虑用COUNT()来显示条数。

一旦你确定SELECT查询工作正常,那就意味着是数据不一致产生的问题,你就需要定位哪里出了问题。有很多可能的原因:使用了错误的备份、错误的UPDATE语句,或者从节点与主节点之间同步异常(这里先仅列出这些最常见的可能)。在这一节中,我们会看到一些关于DELETE或UPDATE操作在随后的SELECT查询中没有生效的示例。下一节会介绍一些令人困惑的情况,在这些情况中,问题会在被触发很久后才出现,当然我们也会告诉你如何反向定位这类错误。本章并不涉及事务中的问题,这类问题将在第2章中讨论。这里展示的场景都是基于数据库中的数据已经稳定的前提,也就是说,所有使用到的事务都已经完成了。我将继续使用从现实场景中简化过的示例。

我们从可能的最佳情形开始,即错误发生后立即提示数据不一致的问题。我们将使用下面的初始数据集:

在应用程序中,临时表包含从主日志表中查询出来的部分结果集。这是一个保存日常常用数据经常使用的技术手段,当你只需要用到主表中的一小部分数据并且用户不想改变主表中的数据或者锁定主表的时候,可以使用临时表。

所以在这个示例中,当使用完结果集后,用户想要同时删除两个表中的相应行。通常人们很难想象用一个查询语句去做多件事情。不过现实可以与你的设想不同,并且你还会得到非预期的结果或负面影响:

如果用户注意观察输出的DELETE语句的相应结果,就会立即发现出了问题。DELETE操作没有影响到任何行意味着它什么都没做。然而,一条语句的输出通常不是这么显而易见,有时候它并不可见,因为SQL语句是在程序或者脚本内部执行的,并且没有人会去监控执行结果。通常情况下,你应该始终检查语句执行的返回信息,从而了解有多少行数据受影响且它们的值是否与你预期的一致。在应用程序中,你必须明确检查信息功能。

继续下面的讨论。如果你立即执行SELECT查询,你可能会很惊讶,以为查询语句出现了错误或者查询缓存没有清除:

如果把SELECT语句改为查询行的数量,就可以确认这不是缓存或者其他相关的问题。这个小例子也告诉我们可以通过对同一张表进行不同的查询方式来确认数据的一致性:

这里COUNT(*)仍然返回一个正数,这表明表是非空的。细心的用户应该已经注意到,DELETE操作实际上没有删除任何行。为了找出原因,我们可以将DELETE语句改为相应的SELECT语句。这样做可以告诉我们哪些行满足了删除条件。

尽管这个简单的示例中没有WHERE语句,但是这个技巧对于包含WHERE语句的删除和更新操作同样有效。SELECT语句返回的行即为DELETE操作将要删除的行或者UPDATE操作将要更新的行:

与之前的结果一致,这里也返回空集合。这就是为什么没有删除任何行!然而,现在仍不清楚产生这个现象的具体原因,但是既然我们有一个SELECT查询,就可以利用第一节提到的相关技术。在这个场景中,最佳选择就是用EXPLAIN命令执行SELECT语句然后分析输出结果:

输出中最后的信息表明查询语句被修饰成了内部连接(inner JOIN),该内部连接仅当另一张表也有满足条件的行时才会同时返回两张表的行。对于t1表中的每一行,在t2表中应至少有一行的值匹配。在这个示例中,因为t2表是空的,自然连接操作返回空集合。

我们刚刚学习了另一个有助于找出UPDATE或DELETE语句错误原因的重要技巧:把语句转换成具有相同JOIN和WHERE条件的SELECT语句。针对SELECT查询,可以使用EXPLAIN EXTENDED[1]命令去获取实际的执行计划,同时也可以避免直接操作结果集带来的危险或者修改了错误的行。

这里有一个的使用UPDATE的更复杂示例。我们仍使用items表:

description和additional字段是TEXT类型的。在这个示例中,我们将使用一个错误的语句,该语句想要把表中的中NULL值替换成更有语义的文本(一个替换成“no description”,另一个替换成“no additional comments”):

该语句会更新一些数据(“影响到3行”),让我们检查一下现在表中数据是否合理:

 

正如我们所见,有3行记录的description字段的值被修改,不过值是0而不是我们预期的“no description”。并且,additional字段的值根本没有改变。为了定位该问题发生的原因,我们应该检查警告。注意服务器返回的这些语句,我们看到有共3个警告:

这条消息看起来很奇怪。为什么上述语句执行后,这里会报告关于DOUBLE的警告,而description和additional字段的类型都是TEXT的。

我们还想知道为什么additional字段完全没有变化,并且我们也没有得到任何警告。

我们把该语句拆分成小段,然后分别检查每段都做了什么:

这是UPDATE语句惯用的开头,没有什么问题:

该段使用SET语句。我们来检查一下它实际做了什么。AND关键字在这里究竟意味什么?我们在语句中加上圆括号来突出一下运算符号优先级:

所以,实际上这个语句计算了下列表达式:

然后将值赋给description字段。计算等式会产生一个布尔类型的结果,表示为LONGLONG类型的值。为了证明这点,以--column-type-info选项打开MySQL命令行客户端,然后再次运行SELECT查询:

我们可以清楚地看到表达式的结果是0,这个值随后被插入了description字段。并且因为我们对additional字段的更新已被这个奇怪的表达式所覆盖了,所以没有值插入该字段中,也就看不到服务器端给出任何关于该字段的信息。

现在可以修改上述语句中的逻辑错误了:

如果需要你也可以检查WHERE语句,不过在这个示例里它没有错误。

这个示例表明返回值和查询执行信息的重要性。我们来进一步讨论它们。

时间: 2024-09-15 16:42:30

《MySQL排错指南》——1.3 当错误可能由之前的更新引起时的相关文章

《MySQL排错指南》导读

前言 MySQL排错指南我从2006年5月开始,作为首席技术支持工程师在MySQL AB公司MySQL支持团队的bug校验组工作,然后我到了Sun公司,最后是在Oracle公司.在日常工作中,我经常遇到用户受困于某个问题而不知所措的情况.虽然有已经被证实可用的方法去定位并快速修复问题,但是用户往往很难从大量的信息中筛选出这些可用的信息.尽管有数以千百计的著名书籍.博文和网页都详细介绍了MySQL服务器方方面面的问题,但这正是我感觉困难的地方:这些信息都关注于如何让MySQL服务器正常地工作,而忽

《MySQL排错指南》——第1章 基础

第1章 基础 MySQL排错指南当解决疑难问题的时候,为了节约时间,你可以从最简单的情况开始,然后一步步从简入繁.在MySQL支持团队工作的时候,我每个月解决成百上千的问题.其中的大部分都是从零散的请求信息开始的,最终的解决方案可能也很基础,我们将会从一些示例中看到这点.不过有些时候,我们确实会遭遇很大的挑战.所以,我们应时刻牢记从最基础的开始. 典型的基础类问题不外乎执行一个查询但是返回非预期的结果.这类问题的表现形式可能是很明显的错误,也可能是在你明知有匹配记录的情况下却没有返回结果,或者其

《MySQL排错指南》——第4章 MySQL环境

第4章 MySQL环境 MySQL排错指南MySQL服务器在运行环境中不是孤立的.即使它运行在专用服务器环境中,你依然需要考虑硬件资源和操作系统限制.在共享环境中,MySQL服务器仍然会受到其他进程的影响.关于MySQL操作系统层面的调优,这个主题可以另写一本书了.所以本章不深入讨论,只是从排错角度切入并展开.MySQL的其中一个优势就是能运行在不同环境中,但这也导致了本章很难将其细化说明.最终我决定写下你需要关心的部分,剩下的留给你去查找操作系统对应的手册来决定如何调优.

《MySQL排错指南》——1.7 当服务器无响应的时候

1.7 当服务器无响应的时候 有时候,MySQL客户端会收到严重的错误消息"在请求中丢失与服务器的连接"或者"服务器已停止".尽管我希望你永远不会遇到这个错误,但是有所准备总是有好处的.由MySQL安装本身引起的这个问题主要有两个原因:服务器问题(最有可能是崩溃)或者滥用连接选项(通常是超时选项或者max_allowed_packet). 第3章将讨论连接相关的配置.第4章会讨论硬件问题和第三方软件相关的问题.这里简短地介绍一下如果遇到服务器崩溃该做些什么. 首先,

《MySQL排错指南》——1.4 获取查询信息

1.4 获取查询信息 正如前一节看到的一样,数据库会返回一些关于每个查询的重要信息,有些信息直接展现在MySQL的访问客户端中,而有些信息则需要通过如SHOW WARNINGS等命令才能得到.当从应用程序中调用SQL语句的时候,获取这些返回信息并确认没有异常情况发生同样重要.所有语言的MySQL API都提供了获取服务器返回信息的接口.本节将讨论这些接口.这里仅涉及C的API,因为我必须选择一种语言的API,并且大部分其他语言的API都是基于C的API的[2]. 受影响的行数我们从之前见过的输出

《MySQL排错指南》——1.1 语法错误

1.1 语法错误 这个错误听起来十分简单,但仍可能很难发现.我建议你像处理其他问题一样,非常细心地查找可能出现的SQL语法错误. 类似如下错误,很容易被发现: 在这个示例中,很显然用户少转入了个"m",错误消息也很清楚(输出结果根据页面设置进行宽度调整): 遗憾的是,不是所有的语法错误都这么显而易见.我曾经处理过一个问题,它的查询语句是这样的: 这是一个版本迁移导致的问题:该语句在5.0版本中运行正常,但是在5.1版本中出现错误.问题的原因在于,在5.1版本中,"access

《MySQL排错指南》——1.2 SELECT返回错误结果

1.2 SELECT返回错误结果 这是用户反馈的另一个非常常见的问题,主要的现象有:用户看不到更新的结果.展示的顺序错误或者查询到了非预期的结果. 这个问题主要有两方面的原因:一方面是你的SELECT查询有误:另一方面是数据库中的数据和你想象的不同.我先介绍第一种情况. 在我规划本节示例的时候,我考虑要么使用真实的示例,要么使用我自己设计的小场景.真实的示例可能占用大量篇幅,但是我自己设计的示例可能对你没有什么帮助,因为没有人会写出那样的代码.因此,我选择使用典型的真实示例作为示例,只是大幅简化

《MySQL排错指南》——4.1 物理硬件限制

4.1 物理硬件限制 对性能有不切实际的期望是通常会犯的错.我们要求MySQL服务器在忽略硬件组件延迟的情况下进行优化.因此,理解什么导致了延迟很重要. 下面列出了影响MySQL服务器的硬件资源: 内存CPU内核数量磁盘I/O网络带宽我们依次讨论每个细节. 4.1.1 内存 内存对于MySQL是非常宝贵的资源.服务器在没有磁盘交换的情况下运行很快.理想情况是数据驻留在内存中.因此,在物理内存的限制内合理配置缓冲区是非常重要的.关于这一点,1.6.4小节和3.9.3小节提供了更多详细的介绍和指导.

《MySQL排错指南》——1.9 许可问题

1.9 许可问题 MySQL有复杂的权限方案,这使得你可以精确地设置哪些用户和主机可以或不可以执行这个或那个操作.从5.5版本开始,MySQL也有了可插拔式的身份验证模式. 尽管它有很多优势,但是这个方案很复杂.例如,让user1@hostA.user2@hostA和user1@hostB不同会很容易混淆它们的权限.当用户名相同而主机名变化的时候更是如此. MySQL允许在对象和连接层面设置访问规则.可以限制某个用户对于特定的表.列等的访问权限. 用户通常会遇到两类权限问题: 应该有权限连接到服