暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 8.0版本mysqld消耗大量主机内存不释放还可能导致数据库重启【排查与解决】

skylines 2025-01-23
47
最开始的时候,系统管理员反馈业务有问题,一些任务没有完成,半夜开始出现异常。根据这些信息检查MySQL库的error日志,发现确实有异常报错,开始有锁堵塞啊,后面发现数据库服务重启了。
error日志内容
    2024-12-25T02:25:10.558518+08:00 218983 [Warning] [MY-013360] [Server] Plugin mysql_native_password reported: ''mysql_native_password' is deprecated and will be removed in a future release. Please use caching_sha2_password instead'
    2024-12-25T02:25:11.987359+08:00 0 [Warning] [MY-012985] [InnoDB] A long semaphore wait:
    --Thread 140403799836416 has waited at dict0dict.cc line 2570 for 916 seconds the semaphore:
    Mutex at 0x7fb59ea91aa0, Mutex DICT_SYS created dict0dict.cc:1018, locked by 140412670072576
    2024-12-25T02:27:14.331095+08:00 0 [Warning] [MY-012985] [InnoDB] A long semaphore wait:
    --Thread 140395180500736 has waited at ha_innodb.cc line 7170 for 867 seconds the semaphore:
    Mutex at 0x7fb59ea91aa0, Mutex DICT_SYS created dict0dict.cc:1018, locked by 140412670072576

    然后调取了一下主机的CPU和内存使用情况,很明显发现,内存的耗尽时间点和MySQL数据库服务重启的时间点是吻合的。

    还发现三天前也出现同样的内存消耗趋势情况,虽然之前的时间没有反馈业务异常,但从内存使用情况看,也知道出现过数据库服务重启,也可以推断每三天出现一次。

    主机总的内存有64G,给到innodb_buffer_pool_size也只有4G,查看整个数据库使用的内存,也只有10G左右。

    从数据库的各种内存池使用情况来看,是绝对不对使用到那么多内存的,计算加起来也就13G不到。

    再根据官方文档提供的,消耗内存的组件,计算加起来,也没有到达30G。

    然而top查看,看到的是,mysqld服务进程消耗的内存比10G大好几倍。

    那mysqld消耗的这么多主机内存,究竟去了哪里呢。查看官网,MySQL 8.0都没有找到相应的BUG,最后想到了业务应用连接数据库创建的会话,断开之后,可能占用的内存资源没有释放导致的,那就是glibc的内存管理器自身缺陷导致。

    简言之,就是调用glibc申请的内存使用完毕后,归还给OS时没有被正常回收,而变成了碎片,随着碎片的不断增长,就能看到mysqld进程占用的内存不断上升。这时,可以调用函数主动回收释放这些碎片。

    可以使用以下方法,可以对mysqld服务进程消耗的内存进行部分释放,不过要记得,这个操作尽量选择在业务少的时间段进行。

      gdb --batch --pid `pidof mysqld` --ex 'call malloc_trim(0)'
      或者
      gdb -p <mysqld_pid>
      知道问题的方向之后,我们也可以从参数方面进行改善。我们查看了数据库的max_connections设置为5000,感觉太大了。数据库最大的活动连接数才250都不到。于是,对部分参数进行了一些调整,如下:
        max_connections = 500  --改小
        max_connect_errors = 100  --增大
        max_heap_table_size = 8M  --改小
        innodb_log_buffer_size = 8M  --改小
        innodb_buffer_pool_size = 8G  --增大

        自从修改了这部分参数之后,以上每3天出现一次重启的情况没有了,直到目前为止,也没有出现过内存耗尽的情况。虽然mysqld服务消耗的内存,依然还是比较大,但是经后期观察,内存增长到主机总内存的75%左右,就不会继续往上增长,继续消耗,还出现一个小部分内存的释放。看到这样,也终于是把mysqld大量消耗内存和重启的问题控制住了,解决掉了。

        总结:

        安装MySQL 8.0的时候,需要注意innodb_buffer_pool_size和max_connections两个参数的设置,需要根据主机的实际内存和业务需求,设置合理的范围,缓存池不宜过小,最大连接数不宜过大。

        也可以安装jemalloc, 可以更好地管理内存碎片,从而降低内存使用峰值。在高并发环境下,jemalloc 的性能通常优于默认的 glibc 分配器。

        附加:

        gdb安装方法:

          # yum -y install gcc wget texinfo
          # wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gdb/gdb-8.1.tar.gz --no-check-certificate
          # tar -zxf gdb-8.1.tar.gz
          # cd gdb-8.1
          # mkdir builddir
          # cd builddir
          # ../configure
          # make && make install
          # gdb --version

          收回内存命令:

            gdb --batch --pid `pidof mysqld` --ex 'call malloc_trim(0)'


            文章转载自skylines,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论