力荐:一条update语句引发的“血案”

有一次得到应用同学的反馈,有一个前端应用登录很慢,已经开始影响业务登录了,稍后DBA介入,发现是由于CPU使用率过高导致,为了能够缓解问题和进一步分析,做了一些改进措施,最后问题得到了化解,但是对于这个问题后续也进行了更多的分析,也算是事后诸葛亮吧。

 

整个分享的思路如下:

问题背景

提出疑问

问题的对比测试

问题的验证

问题总结

 
 

 

问题背景  

 

查看慢日志的情况如下:   

 

两个查询的统计信息如下,可以看到平均执行时间竟然都在40s左右。

 

 

涉及的SQL语句如下,这个也是当时从慢日志中得到的。

 

 

相关的表只有一个,表结构如下:

 

 

整个调用过程的要点是下面的形式,里面有一个update操作,字段APNS_PUSH_ID为varchar

 

 

其实单独运行的语句就类似下面的形式:

 

这样一个update语句竟然很慢,着实感到很奇怪,因为单独执行,查看执行计划是没有问题的。

 

 

提出疑问  

 

对于这个问题的疑问如下:

 

1、对于字符型字段作为索引,目前来看没有很直接的原因发现字符型索引和数字型索引存在巨大的差别。从后来我单独得到的执行计划和后来复现情况来看,没有发现存在很巨大的差别。

 

2、对于慢日志中得到的语句,看到内部已经做了转换。

 

 

而对于这种转换,可能关注点都在NAME_CONST这个部分,在查看了一些资料之后,发现在其他版本和环境中,主要是和字符集转换有关,但是单独执行上面的转换语句,查看执行计划没有任何问题。

 

3、在5.1版本中发现了相应的bug描述,但是目前的环境是在5.6,所以问题应该已经得到修复。

 

我希望得到一些确切的信息,能够复现,能够找到一些相关的bug或者相关的解决方案。

 

 

问题的对比测试  

 

 

我找了套环境尝试复现这个问题,我把表里的数据复制到一个测试环境,然后写了下面的存储过程来复现和对比。

 


 

测试前,保证handler是初始化状态

 

 

然后运行存储过程,其实这个过程就是当时问题发生时的一个调用环节。

 

 

查看Handler的状态,可以看到Handler_read_next的值极高,其实这是一个全表扫描。

 


 

而如果单独执行同样的sql语句。

 


 

查看Handler的情况,Handler_read_rnd_next为0,很显然是一个索引扫描。

 


 

如果查看单独update语句的执行计划,是看不到太多的明细信息的。

 

 

我们可以打开trace,MySQL 5.6以后有一个特性,可以试试。

  

 

可以看到内部做了字符集的转换,而转换的过程其实也可以这么理解,convert(`push_list_s`.`APNS_PUSH_ID` using utf8)这个操作是把全表的APNS_PUSH_ID先做转换和push_id做匹配,这也就无形中导致了全表扫描。

 


 

 

执行单个语句,查看trace的情况。

 


 

 可以看到解析的时候是在做键值的匹配。

  

 

对于这个问题,经过这样的分析测试,会发现在存储过程中和单独执行的场景中还是存在差别的,而问题的关键就在于字段APNS_PUSH_ID的字符集,

 

 

所以唯一的差别就在于字符集,MySQL对于字符集的支持非常灵活,数据库级,表级,字段级别都可以定制,而对于这个问题的直接修复,就是统一字段” APNS_PUSH_ID”的字符集为表级的UTF8。

 

 

问题的验证  

 

问题的验证步骤如下:

 

 

统一字符集之后,再次执行,就会发现效率就会大大提高。

 

 

而且MySQL的回复如下:

Problem is that the stored routine does not explicitly declare the charset of the parameter that is passed to the stored routine. It must match the column's charset to which you're comparing it to. 
 

 

问题总结  

 

其实对于问题还是需要刨根问底,找到了问题的症结,就会让我们在处理问题的时候更加坦然。我自己也尝试从Oracle的对比中得到一些解决问题的思路,但是Oracle对于字符集的支持是统一管理方式的,所以也是无果而终,不过这种对比方式给了我一些思路。对于字符集的设定,虽然灵活方便,但是也要使用统一得当。

 

 作者介绍  杨建荣

 

  • 【DBAplus社群】联合发起人;
  • Oracle ACE-A、YEP成员,现就职于搜狐畅游,拥有6年以上的数据库开发和运维经验,曾任amdocs DBA,擅长电信数据业务,数据库迁移和性能调优;
  • 拥有Oracle 10g OCP,OCM,MySQL OCP认证,对shell,ava有一定的功底,曾在2015年数据库大会进行关于数据迁移和升级的主题分享,现在每天仍在孜孜不倦的进行技术分享,每天通过微信,技术博客共享,已连续坚持800多天。

本文来自合作伙伴"DBAplus",原文发布时间:2016-05-23

时间: 2024-07-30 07:56:39

力荐:一条update语句引发的“血案”的相关文章

一条update语句的优化探索

今天经开发同学反馈,发现有一些update语句阻塞了部分业务流程,为什么说一些而不是一条,是因为这些update语句都在一个存储过程中,语句结构相仿,真有一种一荣俱荣,一损俱损的感觉.而比较纠结的是这样的update语句有差不多10个.从我收到反馈到观察分析,里面的第一条update语句运行了近5个小时,还没有完成,从SQL Monitor的报告来看,似乎进度甚微,按照这个进度,这些语句的执行时间会非常惊人. 我先拿到了一个初步的报告. 概览信息如下: 这条语句从生成的执行计划来看,简直完美,但

怎么把这条update语句改成linq?

问题描述 UPDATED_BIAOSETAUDIT_FLAG=#{auditFlag},AUDIT_NAME=#{auditName},AUDIT_DATE=getDate(),AUDIT_NOTE=#{auditNote}WHEREMESSAGE_ID=#{messageId}怎么改成linq急求3Q用的是c#,如查询语句写成这样:varquery=fromfindb.D_BIAOwhere(f.MESSAGE_ID==MESSAGE_ID)selectnewModel{MESSAGE_ID=

jdbc-JDBC 并发事务中执行多条相同的Update语句,为什么会造成死锁?

问题描述 JDBC 并发事务中执行多条相同的Update语句,为什么会造成死锁? 如题,我在多个线程中并发向A库的XXXX表提交事务,每个事务中执行两条Update语句: update xxxx set column1 = '11' where ID = '11' update xxxx set column2 = '22' where ID = '22' 结果在运行过程中抛出死锁异常: 事务(进程ID 211)与另一个进程被死锁在锁|线程资源上,并且已被选作死锁牺牲品.请重新运行该事务. 解决

执行一条sql语句update多条记录实现思路_MsSql

通常情况下,我们会使用以下SQL语句来更新字段值: 复制代码 代码如下: UPDATE mytable SET myfield='value' WHERE other_field='other_value'; 但是,如果你想更新多行数据,并且每行记录的各字段值都是各不一样,你会怎么办呢?举个例子,我的博客有三个分类目录(免费资源.教程指南.橱窗展示),这些分类目录的信息存储在数据库表categories中,并且设置了显示顺序字段 display_order,每个分类占一行记录.如果我想重新编排这

一个SQL语句引发的ORA-00600错误排查

作者介绍 杨建荣,[DBAplus社群]联合发起人.现就职于搜狐畅游,Oracle ACE-A.YEP成员,超7年数据库开发和运维经验,擅长电信数据业务.数据库迁移和性能调优.持Oracle 10G OCP,OCM,MySQL OCP认证,<Oracle DBA工作笔记>作者.   Merge是从Oracle 9i就引入的功能,它是有别于其他DML中的一种特殊语句,类似于MySQL中的 insert into on duplicate key操作,而且Merge功能更丰富,可以同时对一个表中的

mysql数据库UPDATE语句一个bug分析

这个我认为的bug,反馈给MySQL官方,但是MySQL官方认为这并不是一个bug,并给出了解释,我认为这个解释是合理的,但是不可避免的是这条语句实在太危险了. 问题描述 示例表结构与表数据: # 表结构 mysql> show create table t; +-------+--------------------------------------------------------------------------------------------------------------

新手求教,执行带参数的update语句

问题描述 我用的数据库是oracle我在class中主要实现了将符合条件的号码select选出来,然后对没条记录进行update,其中update的条件是:whereid=@id其中id是在select中的一个字段,如果这样的话,我的update语句应该怎样写,然后where语句中的参数@id怎么去拿到select中的值呢?请大家帮帮忙,我是菜鸟,拜托,谢谢 解决方案 解决方案二:@换成:whereid=:id解决方案三:那我的参数:id,怎么传呢?谢谢,我不知道应该怎么写是不是这样写updat

更新-c#update语句的灵异现象

问题描述 c#update语句的灵异现象 各位大神,我在写update语句的时候遇到这样的灵异现象: 我想实现的功能是,每次预定一个房间以后,对输出报表进行更新,如果之前当天没有预定信息则insert,如果有更新,即将预定数加1,总利润加上此次预定的房间价格. 奇怪的是第一次insert语句成功以后,当天继续预定,update语句都是正确的, 但是数据库灵异的显示预定数为0.不知道是数据库的bug还是? 我的sql语句 但是数据库一直就是这个,除了第一条insert是对的,为1,再更新就是0了.

hql-jdbc执行update语句,突然卡死了

问题描述 jdbc执行update语句,突然卡死了 我原先用的是hibernate的hql语句执行批量的update,并且是更新的同一条数据,执行一半的更新时,突然就卡住不动了,也不知道是什么原因 数据库是SqlServer2008.后来换成jdbc的sql语句执行批量更新(还是不断更新同一条记录),结果还是出现相同情况,程序卡住了(在执行jdbc.query()方法卡住了),我在用hibernate时,乐观锁和悲观锁都试过了,还是不行,jdbc也是,试过了给sql语句加锁,结果还是不行,请问各