数据库十宗罪01查询引发宕机!

2024年 5月 2日 54.8k 0

说明:

    数据库中查询相关的BUG并不少见,例如Oracle早期版本的Cardinality feedback、BITMAP CONVERSION等功能不稳定,有时会严重影响查询性能,除了打补丁或升级版本以外,还可以通过设置隐含参数禁用相关不稳定的功能,跳过此问题。

    这也是Oracle数据库比较高明的一个地方,新功能基本上都会有一个手动关闭的接口,可以在解决问题的同时最小化减少对业务的影响决,这里并不是在对Oracle无脑吹,评判一个数据库产品功能、代码是否足够健壮,很多时候就是通过这些不起眼的细节处分出高下的。

    近期发现多起查询引发BUG的问题,这次的BUG比较严重,查询语句直接引起数据库宕机,并且没有类似Oracle的隐含参数功能,只能通过升级解决。

    此类问题定义为"数据库的十宗罪之一"并不为过,很常见的查询操作,因为BUG直接导致数据库宕机,而且解决方式影响也比较大,只能通过升级数据库解决。

    其中一个是某国产数据库,另一个是MySQL数据库。

    国产数据库先不多说了,厂商定位及解决问题还算及时,最后升级数据库解决了此问题。

    今天主要说下MySQL数据库因为BUG导致查询引发的宕机问题。

问题现象:

    DB:MySQL 8.0.24

    查询如下语句:

      SELECT GROUP_CONCAT(b.id) chen_data_id,
      GROUP_CONCAT(DISTINCT b.chen_point) chen_point
      FROM (SELECT ft.id chen_id, max(fd.chen_ope_tm) chen_ope_tm
      FROM cjc_t1 a
      JOIN cjc_t2 b
      ON a.rpt_base_id = b.id
      AND b.frp_sort2_code = '*****'
      JOIN cjc_t3 ft
      ON ft.frp_data_id = a.rpt_data_titl_id
      JOIN cjc_t4 fd
      ON ft.id = fd.chen_id
      WHERE 1 = 2
      GROUP BY ft.id) a
      JOIN cjc_t4 b
      ON a.chen_id = b.chen_id
      AND a.chen_ope_tm = b.chen_ope_tm
      WHERE 1 = 2;

      这里先不讨论SQL写的烂不烂,再烂也不是宕机的理由。

      就算数据库是这样设计的:

      客户也可能是这样使用的:

      没毛病,客户永远都是对的。

      上面的查询引起MySQL数据库自动宕机,前台信息如下:

        mysql> use cjc
        Database changed
        mysql> SELECT GROUP_CONCAT(b.id) chen_data_id, GROUP_CONCAT(DISTINCT b.chen_point) chen_point FROM (SELECT ft.id chen_id, max(fd.chen_ope_tm) chen_ope_tm FROM cjc_t1 a JOIN cjc_t2 b ON a.rpt_base_id = b.id AND b.frp_sort2_code = '*****' JOIN cjc_t3 ft ON ft.frp_data_id = a.rpt_data_titl_id JOIN cjc_t4 fd ON ft.id = fd.chen_id WHERE 1 = 2 GROUP BY ft.id) a JOIN cjc_t4 b ON a.chen_id = b.chen_id AND a.chen_ope_tm = b.chen_ope_tm WHERE 1 = 2;
        ERROR 2013 (HY000): Lost connection to MySQL server during query
        No connection. Trying to reconnect...
        ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/db/mysqldata8024/13308/socket/mysql.sock' (111)
        ERROR:
        Can't connect to the server

        error日志信息如下:

          05:47:34 UTC - mysqld got signal 11 ;
          Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardware.
          Thread pointer: 0x7ffe80018b80
          Attempting backtrace. You can use the following information to find out
          where mysqld died. If you see no messages after this, something went
          terribly wrong...
          stack_bottom = 7fffe8051da0 thread_stack 0x46000
          /db/mysqldata8024/app/8.0.24/bin/mysqld(my_print_stacktrace(unsigned char const*, unsigned long)+0x2e) [0x1e7746e]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(handle_fatal_signal+0x303) [0xec1f83]
          /lib64/libpthread.so.0(+0x13bb0) [0x7ffff7faabb0]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(Unique::walk(int (*)(void*, unsigned int, void*), void*)+0x1a) [0xe9656a]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(Item_func_group_concat::val_str(String*)+0x103) [0x10c6f33]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(Item::send(Protocol*, String*)+0x25d) [0xfe153d]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(THD::send_result_set_row(mem_root_deque const&)+0xd8) [0xcffe18]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(Query_result_send::send_data(THD*, mem_root_deque const&)+0x33) [0x118cad3]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(Query_expression::ExecuteIteratorQuery(THD*)+0x26d) [0xe4b5cd]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(Query_expression::execute(THD*)+0x2f) [0xe4b92f]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(Sql_cmd_dml::execute(THD*)+0x4e6) [0xddb5a6]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(mysql_execute_command(THD*, bool)+0x9a8) [0xd88408]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(dispatch_sql_command(THD*, Parser_state*)+0x3d7) [0xd8b957]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(dispatch_command(THD*, COM_DATA const*, enum_server_command)+0xd5a) [0xd8cb9a]
          /db/mysqldata8024/app/8.0.24/bin/mysqld(do_command(THD*)+0x174) [0xd8ea34]
          /db/mysqldata8024/app/8.0.24/bin/mysqld() [0xeb38e0]
          /db/mysqldata8024/app/8.0.24/bin/mysqld() [0x2340b8e]
          /lib64/libpthread.so.0(+0x8fdd) [0x7ffff7f9ffdd]
          /lib64/libc.so.6(clone+0x3f) [0x7ffff732bbdf]


          Trying to get some variables.
          Some pointers may be invalid and cause the dump to abort.
          Query (7ffe80bceb28): SELECT GROUP_CONCAT(b.id) chen_data_id, GROUP_CONCAT(DISTINCT b.chen_point) chen_point FROM (SELECT ft.id chen_id, max(fd.chen_ope_tm) chen_ope_tm FROM cjc_t1 a JOIN cjc_t2 b ON a.rpt_base_id = b.id AND b.frp_sort2_code = '*****' JOIN cjc_t3 ft ON ft.frp_data_id = a.rpt_data_titl_id JOIN cjc_t4 fd ON ft.id = fd.chen_id WHERE 1 = 2 GROUP BY ft.id) a JOIN cjc_t4 b ON a.chen_id = b.chen_id AND a.chen_ope_tm = b.chen_ope_tm WHERE 1 = 2
          Connection ID (thread ID): 11
          Status: NOT_KILLED


          The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains
          information that should help you find out what is causing the crash.

          问题分析:

          通过gdp查找具体代码位置为sql_parse.cc:1841

          继续检查sql_parse.cc:1841位置代码,没有分析出有效信息

          既然出问题的是8.0.24版本,那么8.0.25版本修复的BUG中能否有相似的问题?

            https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-25.html

            果然看到了相似度极高的BUG

            描述如下:

            For a prepared, implicitly grouped SELECT statement in which the WHERE clause was determined always to be false, the result of some aggregate functions could sometimes be picked up from the previous execution of the statement. (Bug #103192, Bug #32717969)

            翻译为中文:

            对于已准备好的、隐式分组的SELECT语句(其中WHERE子句被确定为始终为false),某些聚合函数的结果有时可以从该语句的前一次执行中提取。(Bug #103192, Bug #32717969)

            解决方案:

            也咨询了原厂,答复如下:

            数据库实例的crash是由于下面的bug导致的:

              Bug 32717969 - WRONG RESULT OF EXECUTE PREPARE ON 'SMALLINT'

              其关联bug是:

                Bug 32801496 - MYSQL 8.0.24 CRASHES WHEN QUERY USES GROUP_CONCAT(DISTINCT WITH EMPTY RESULT SET

                上述bug已经在 8.0.25 版本中修复,因此解决方法就是升级到 8.0.25 以上,建议用户升级到目前最新的GA版 MySQL 8.0.36,以规避更多Bug的影响,增加稳定性,同时获得更多增强的功能特性。

                经过测试,此问题在8.0.24版本可以稳定重现,升级到8.0.3x版本可以解决此问题,很遗憾,此问题并不能通过修改某个参数临时解决。

                相似问题:

                之前看过微信公众号号“爱可生开源社区”发布过的一篇文章,标题是《故障分析 | 一条 SELECT 语句跑崩了 MySQL ,怎么回事?》,原文连接如下:

                  https://mp.weixin.qq.com/s/c3I6uy8g4bVQGa9KYRZHww

                  最开始以为是同一个问题,但仔细看是两个问题,这个问题的数据库版本似乎是8.0.19,也是某类查询触发BUG导致实例宕机。

                  通过原文可以看到,触发宕机的语句如下:

                    SELECT DISTINCT T.CUST_NO
                    FROM testDB.TABLE_TRANSACTION T
                    WHERE EXISTS (SELECT 1 FROM testDB.Table1 T1 WHERE T.CUST_NO = T1.CUST_NO)
                    AND T.AGENT_CERT_NO IS NOT NULL

                    宕机时error.log日志如下:

                      Trying to get some variables.
                      Some pointers may be invalid and cause the dump to abort.
                      Query (7f55ac0ca298): SELECT DISTINCT T.CUST_NO FROM testDB.TABLE_TRANSACTION T WHERE EXISTS (SELECT 1 FROM testDB.Table1 T1 WHERE T.CUST_NO = T1.CUST_NO ) AND T.AGENT_CERT_NO IS NOT NULL
                      Connection ID (thread ID): 65
                      Status: NOT_KILLED

                      分析详细过程请通过上面的链接查看原文。
                      这个案例的解决方案如下,可以通过修改参数临时解决此问题,原文如下:

                      在上述排查分析中,我们得到这个 bug 是由于使用了 semi-join 的 DuplicateWeedout 执行策略导致了问题的发生,如果在短时间内无法升级变更数据库,而又想尽量避免这个问题的发生。

                      一方面肯定是业务侧避免该 SQL 的执行,从 DBA 的角度上考虑的是该 SQL 怎样才能正常执行,那么经过验证:

                      以下三种解决方案均可解决当前 select 查询导致的数据库崩溃问题。

                      1、业务表设置合理统一的字符集(utf8mb4)和排序规则,避免 exist 在半连接中使用了DuplicateWeedout 策略,加快 SQL 执行效率;

                      2、关闭数据库级别的 DuplicateWeedout 优化策略:

                        SET [GLOBAL|SESSION] optimizer_switch='duplicateweedout=off';

                        3、升级 MySQL 版本到 8.0.24 ;

                        结束语:

                        数据库升级需谨慎;

                        上面第二个案例讲到,8.0.19的这个严重BUG,已经在8.0.24版本修复了,但也不建议直接升级到8.0.24版本,个人认为修改参数解决是最稳妥的,屏蔽的功能也不是必须要使用,但如果就是要升级到8.0.24,也需要提前知道升级到8.0.24版本会遇到哪些新的BUG,数据库升级是一个比较大的变更,需要充分评估必要性和风险,例如第一个案例里讲到的BUG就是8.0.24特有的,如果不做这些准备工作,你会发现,当你终于搞定了一个BUG!然后发现出现了更多BUG。

                        相关文章

                        Oracle如何使用授予和撤销权限的语法和示例
                        Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
                        下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
                        社区版oceanbase安装
                        Oracle 导出CSV工具-sqluldr2
                        ETL数据集成丨快速将MySQL数据迁移至Doris数据库

                        发布评论