- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
最近碰到一个 case,一台主机上,部署了多个实例。之前使用的是 MySQL 8.0,启动时没有任何问题。但升级到 MySQL 8.4 后,部分实例在启动时出现了以下错误.
[Warning] [MY-012582] [InnoDB] io_setup() failed with EAGAIN. Will make 5 attempts before giving up.
[Warning] [MY-012583] [InnoDB] io_setup() attempt 1.
[Warning] [MY-012583] [InnoDB] io_setup() attempt 2.
[Warning] [MY-012583] [InnoDB] io_setup() attempt 3.
[Warning] [MY-012583] [InnoDB] io_setup() attempt 4.
[Warning] [MY-012583] [InnoDB] io_setup() attempt 5.
[ERROR] [MY-012584] [InnoDB] io_setup() failed with EAGAIN after 5 attempts.
[ERROR] [MY-012954] [InnoDB] Cannot initialize AIO sub-system
[ERROR] [MY-012930] [InnoDB] Plugin initialization aborted with error Generic error.
[ERROR] [MY-010334] [Server] Failed to initialize DD Storage Engine
[ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
[ERROR] [MY-010119] [Server] Aborting
[System] [MY-010910] [Server] /usr/local/mysql/bin/mysqld: Shutdown complete (mysqld 8.4.0) MySQL Community Server - GPL.
[System] [MY-015016] [Server] MySQL Server - end.
下面我们来分析下这个报错的具体原因及解决方法.
首先搜索下这个报错是在哪个文件产生的.
# grep "io_setup() failed" -r /usr/src/mysql-8.4.0
/usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc: ib::warn(ER_IB_MSG_757) << "io_setup() failed with EAGAIN."
/usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc: << "io_setup() failed with EAGAIN after "
接着分析该文件中产生报错的具体函数.
// storage/innobase/os/os0file.cc
bool AIO::linux_create_io_ctx(ulint max_events, io_context_t *io_ctx) {
ssize_t n_retries = 0;
for (;;) {
memset(io_ctx, 0x0, sizeof(*io_ctx));
int ret = io_setup(max_events, io_ctx);
if (ret == 0) {
/* Success. Return now. */
return (true);
}
switch (ret) {
case -EAGAIN:
if (n_retries == 0) {
/* First time around. */
ib::warn(ER_IB_MSG_757) << "io_setup() failed with EAGAIN."
" Will make "
<< OS_AIO_IO_SETUP_RETRY_ATTEMPTS
<< " attempts before giving up.";
}
if (n_retries < OS_AIO_IO_SETUP_RETRY_ATTEMPTS) {
++n_retries;
ib::warn(ER_IB_MSG_758) << "io_setup() attempt " << n_retries << ".";
std::this_thread::sleep_for(OS_AIO_IO_SETUP_RETRY_SLEEP);
continue;
}
/* Have tried enough. Better call it a day. */
ib::error(ER_IB_MSG_759)
<< "io_setup() failed with EAGAIN after "
<< OS_AIO_IO_SETUP_RETRY_ATTEMPTS << " attempts.";
break;
...
}
ib::info(ER_IB_MSG_762) << "You can disable Linux Native AIO by"
" setting innodb_use_native_aio = 0 in my.cnf";
break;
}
return (false);
}
可以看到,错误信息主要是在执行io_setup,产生 EAGAIN 错误时打印的.
函数中的io_setup是一个 Linux 系统调用,用于初始化一个异步 I/O (AIO) 上下文(context)。异步 I/O(AIO)允许程序在发出 I/O 操作请求后继续执行其他工作,而不是等待操作完成。io_setup是 Linux 内核提供的异步 I/O 接口,通常用于高性能应用程序和数据库系统,以实现非阻塞 I/O 操作。max_events 指定了这个异步 I/O 上下文可以处理的最大并发 I/O 请求数。io_setup执行成功时会返回 0,失败时则返回 -1,并通过 errno 表示具体错误.
当返回的错误是 EAGAIN 时,则意味着指定的 max_events 超过了系统允许的最大异步 I/O (AIO) 事件数.
系统允许创建的最大异步 I/O 事件数是在/proc/sys/fs/aio-max-nr中定义的,默认值跟系统有关,通常是 65536.
所以,解决方法找到了,直接调整/proc/sys/fs/aio-max-nr的值即可.
# echo 1048576 > /proc/sys/fs/aio-max-nr
注意这种只是临时修改,系统重启就会失效。如果要永久修改,需调整 /etc/sysctl.conf.
# vim /etc/sysctl.conf
fs.aio-max-nr=1048576
# sysctl -p
问题解决了,接下来我们分析下同一台主机,为什么之前的 MySQL 8.0 没问题,升级到 MySQL 8.4 就报错了呢?
这个时候,就需要分析函数中 max_events 的生成逻辑了.
下面是AIO::linux_create_io_ctx函数被调用的堆栈信息.
#0 AIO::linux_create_io_ctx (max_events=256, io_ctx=0x7fffe02d1500)
at /usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc:2559
#1 0x0000000004db1649 in AIO::init_linux_native_aio (this=0x7fffe02d1d70)
at /usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc:6139
#2 0x0000000004db16ed in AIO::init (this=0x7fffe02d1d70)
at /usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc:6167
#3 0x0000000004db1826 in AIO::create (id=LATCH_ID_OS_AIO_IBUF_MUTEX, n=256, n_segments=1)
at /usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc:6200
#4 0x0000000004db1a2b in AIO::start (n_per_seg=256, n_readers=64, n_writers=4)
at /usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc:6254
#5 0x0000000004db261a in os_aio_init (n_readers=64, n_writers=4)
at /usr/src/mysql-8.4.0/storage/innobase/os/os0file.cc:6514
#6 0x0000000004ee6b4d in srv_start (create_new_db=false)
at /usr/src/mysql-8.4.0/storage/innobase/srv/srv0start.cc:1743
#7 0x0000000004bdfc92 in innobase_init_files (dict_init_mode=DICT_INIT_CHECK_FILES, tablespaces=0x7fffe77bf720)
at /usr/src/mysql-8.4.0/storage/innobase/handler/ha_innodb.cc:5744
#8 0x0000000004bf0e22 in innobase_ddse_dict_init (dict_init_mode=DICT_INIT_CHECK_FILES, tables=0x7fffe77bf740,
tablespaces=0x7fffe77bf720) at /usr/src/mysql-8.4.0/storage/innobase/handler/ha_innodb.cc:13133
#9 0x00000000049153b8 in dd::bootstrap::DDSE_dict_init (thd=0xb7ce6b0, dict_init_mode=DICT_INIT_CHECK_FILES,
version=80300) at /usr/src/mysql-8.4.0/sql/dd/impl/bootstrap/bootstrapper.cc:746
#10 0x0000000004916195 in dd::bootstrap::restart_dictionary (thd=0xb7ce6b0)
at /usr/src/mysql-8.4.0/sql/dd/impl/bootstrap/bootstrapper.cc:907
#11 0x0000000003814e54 in bootstrap::handle_bootstrap (arg=0x7fffffffcf20)
at /usr/src/mysql-8.4.0/sql/bootstrap.cc:340
#12 0x0000000005792a92 in pfs_spawn_thread (arg=0xb7ece50) at /usr/src/mysql-8.4.0/storage/perfschema/pfs.cc:3051
#13 0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#14 0x00007ffff5ff0b0d in clone () from /lib64/libc.so.6
堆栈中的重点是 #6 的srv_start函数,这个函数会调用os_aio_init来初始化异步 I/O 系统.
// storage/innobase/srv/srv0start.cc
dberr_t srv_start(bool create_new_db) {
...
if (!os_aio_init(srv_n_read_io_threads, srv_n_write_io_threads)) {
ib::error(ER_IB_MSG_1129);
return (srv_init_abort(DB_ERROR));
}
...
调用 os_aio_init 时,会传递两个参数:srv_n_read_io_threads 和 srv_n_write_io_threads。这两个参数实际上对应的就是 MySQL 中的 innodb_read_io_threads 和 innodb_write_io_threads,这两个参数分别用来表示 InnoDB 中用于读操作、写操作的 I/O 线程数.
如果初始化失败,会打印ER_IB_MSG_1129错误.
ER_IB_MSG_1129是一个预定义的错误代码,对应的错误信息是在share/messages_to_error_log.txt中定义的.
ER_IB_MSG_1129
eng "Cannot initialize AIO sub-system"
所以,错误日志中看到的[ERROR] [MY-012954] [InnoDB] Cannot initialize AIO sub-system其实就是在这里打印的.
有的童鞋可能猜到了,异步 I/O 系统初始化失败与 innodb_read_io_threads 和 innodb_write_io_threads 的设置有关,事实也确实如此.
下面,我们分析下 MySQL 启动过程中需要初始化多少个异步 I/O 请求.
异步 I/O 的初始化主要是在AIO::linux_create_io_ctx中进行的,接下来,我们分析下AIO::linux_create_io_ctx的调用场景:
该函数用来判断系统是否支持 AIO.
bool AIO::is_linux_native_aio_supported() {
...
if (!linux_create_io_ctx(1, &io_ctx)) {
return (false);
}
...
}
这里只会初始化 1 个异步 I/O 请求.
该函数是用来初始化 Linux 原生异步 I/O 的.
dberr_t AIO::init_linux_native_aio() {
...
ulint max_events = slots_per_segment();
for (ulint i = 0; i < m_n_segments; ++i, ++ctx) {
if (!linux_create_io_ctx(max_events, ctx)) {
return (DB_IO_ERROR);
}
}
return (DB_SUCCESS);
}
函数中的 m_n_segments 是需要创建的异步 I/O (AIO) 上下文的数量,max_events 是每个异步 I/O (AIO) 上下文支持的最大并发 I/O 请求数。所以,这个函数会初始化 m_n_segments * max_events 个异步 I/O 请求.
在 MySQL 的启动过程中,AIO::is_linux_native_aio_supported 只被调用一次,而 AIO::init_linux_native_aio 则会被调用三次,分别用于 insert buffer 线程、读线程和写线程的初始化.
这两个函数都是在AIO::start中调用的.
// storage/innobase/os/os0file.cc
bool AIO::start(ulint n_per_seg, ulint n_readers, ulint n_writers) {
#if defined(LINUX_NATIVE_AIO)
/* Check if native aio is supported on this system and tmpfs */
if (srv_use_native_aio && !is_linux_native_aio_supported()) {
ib::warn(ER_IB_MSG_829) << "Linux Native AIO disabled.";
srv_use_native_aio = false;
}
#endif /* LINUX_NATIVE_AIO */
...
if (0 < n_extra) {
...
s_ibuf = create(LATCH_ID_OS_AIO_IBUF_MUTEX, n_per_seg, 1);
...
}
...
s_reads =
create(LATCH_ID_OS_AIO_READ_MUTEX, n_readers * n_per_seg, n_readers);
...
s_writes =
create(LATCH_ID_OS_AIO_WRITE_MUTEX, n_writers * n_per_seg, n_writers);
...
return true;
}
函数中的 n_per_seg 实际上就是 max_events.
n_per_seg 等于 8 * OS_AIO_N_PENDING_IOS_PER_THREAD,因为 OS_AIO_N_PENDING_IOS_PER_THREAD 是个常量,值为 32,所以 n_per_seg 等于 256.
而AIO::init_linux_native_aio 中的 m_n_segments 实际上表示的是线程的数量:对于 insert buffer 线程,线程数为 1;对于读操作线程,线程数为 n_readers;对于写操作线程,线程数为 n_writers.
关键是在创建 AIO 对象时,会调用 AIO 的构造函数,而构造函数中的 m_slots 又决定了 max_events 的值.
AIO *AIO::create(latch_id_t id, ulint n, ulint n_segments) {
...
AIO *array =
ut::new_withkey<AIO>(UT_NEW_THIS_FILE_PSI_KEY, id, n, n_segments);
...
}
AIO::AIO(latch_id_t id, ulint n, ulint segments)
: m_slots(n),
m_n_segments(segments),
...
[[nodiscard]] ulint slots_per_segment() const {
return (m_slots.size() / m_n_segments);
}
以读线程为例,AIO::create中的 n 等于 n_readers * n_per_seg,n_segments 等于 n_readers.
在初始化 AIO 对象时,n_readers * n_per_seg 将赋值给 m_slots,n_readers 将赋值给 m_n_segments.
所以AIO::init_linux_native_aio中的 max_events = slots_per_segment() = m_slots.size() / m_n_segments = n_readers * n_per_seg / n_readers = n_per_seg.
基于上面的分析,我们可以推论出 MySQL 在启动过程中需要初始化的异步 I/O 请求数的计算公式.
(1 + innodb_read_io_threads + innodb_write_io_threads) * 256 + 1
最后一个 1 是判断系统是否支持 AIO.
下面通过一个具体的案例来验证下上面的计算公式是否正确.
首先通过/proc/sys/fs/aio-nr查看当前系统中已分配的异步 I/O 请求的数量.
# cat /proc/sys/fs/aio-nr
4866
接着,启动一个 MySQL 8.4 实例,启动命令中显式设置 innodb_read_io_threads 和 innodb_write_io_threads.
# /usr/local/mysql8.4/bin/mysqld --defaults-file=/etc/my_3308.cnf --innodb-read-io-threads=64 --innodb-write-io-threads=4 &
实例启动后,再次查看/proc/sys/fs/aio-nr.
# cat /proc/sys/fs/aio-nr
22531
两个数之间的差值是 17665.
按照之前的公式计算,也是 17665,完全吻合.
(1 + 64 + 4) * 256 + 1 = 17665
因为 innodb_read_io_threads 的默认值在 MySQL 8.4 中发生了变化.
在 MySQL 8.4 之前,innodb_read_io_threads 默认为 4,而在 MySQL 8.4 中,innodb_read_io_threads 默认等于主机逻辑 CPU 的一半,最小是 4,最大是 64.
static MYSQL_SYSVAR_ULONG(
read_io_threads, srv_n_read_io_threads,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"Number of background read I/O threads in InnoDB.", nullptr, nullptr,
std::clamp(std::thread::hardware_concurrency() / 2, 4U, 64U), 1, 64, 0);
不巧,问题 case 主机的逻辑 CPU 是 128 核,所以就导致了 innodb_read_io_threads 等于 64.
这就意味着,在/proc/sys/fs/aio-max-nr等于 65536(默认值)的情况下,该主机上只能启动 3(65536/17665) 个 MySQL 8.4 实例.
io_setup() failed with EAGAIN
错误,可适当增加/proc/sys/fs/aio-max-nr
的值。(1 + innodb_read_io_threads + innodb_write_io_threads) * 256 + 1
io_setup(2) — Linux manual page: https://www.man7.org/linux/man-pages/man2/io_setup.2.html 。
最后此篇关于升级到MySQL8.4,MySQL启动报错:io_setup()failedwithEAGAIN的文章就讲到这里了,如果你想了解更多关于升级到MySQL8.4,MySQL启动报错:io_setup()failedwithEAGAIN的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在努力学习本教程 https://github.com/cf-platform-eng/spring-boot-cities/tree/master/cities-service在 Cloud F
0){ echo "Username or email already exists."; }else{ $query = mysql_que
我想在Linux上编译ARToolKit源码,下载源码,按照ARToolKit文档,配置GLUT , OpenGL, libjpeg 等库。 转到 ARToolKit 目录并键入 ./Configer
你好,我在这个程序中遇到错误,wcout 不是 `std' 的成员。如您所见,我也使用了 iostream,但没有用。我有 Dev-C++ 4.9.9.2,我的操作系统是 XP SP3我需要你的帮助。
我尝试对一个网站进行多次投票,但我得到: panic: runtime error: invalid memory address or nil pointer dereference [signal
当我将“ using namespace std; int main(){ string s[30]; int n = 20; for(int i = 0; i using
我设置了 EMAIL_URL process.env.MAIL_URL="smtp://xx%40gmail.com:yyy@smtp.gmail.com:465" 这个工作找到了 Email.sen
我有如下文件夹结构 . ├── docker-compose.yml └── web-app ├── create_tomcat_admin.sh ├── Dockerfile
这是我在 php 中的查询代码: $query3 = mysql_query("SELECT * FROM area_of_work") or die('Invalid query:'. mysql_
我正在尝试将 therubyracer/therubyracer-heroku 安装到我的应用程序,因为它在 heroku 服务器中不起作用,因为我没有 javascript 运行时环境。 当我尝试安
我正在开发一个非常简单的 React 应用程序。它有一个组件,在这个组件中,我试图在单击链接时设置状态。但出于某种原因,setState 甚至没有得到认可。它带有下划线,表示未解析的函数或方法 set
我有三个来源:代码处理.h typedef enum {typeBool, typeVarDeclaration, typeFuncDeclaration } nodeEnum; typedef st
我正在尝试从我的 Android 手机向 raspi 发送连续的命令。我使用了此链接中的代码,但出现错误。 链接:https://stackoverflow.com/questions/2347143
我正在尝试从数据库中加载数据并将其放入不同的 View 中。 log cat 返回错误,它找不到“_id”列。 有人可以帮我解决这个问题吗? SqlHelper代码: public class Fib
我的alertview给出了这个问题..它与sqlite无关..它给出了一些信息.. 我该如何解决这个问题? UIAlertView *infoShow = [[UIAlertView alloc]
这个问题在这里已经有了答案: launch activities from different package (5 个答案) 关闭 10 年前。 最近我一直在开发一个新的应用程序,我正在尝试使用
这是我的代码: #include #include typedef struct test { int *a; char *s; }TEST; int main (void) {
所以我定义了一个函数,如果它是由它的 lonesome 实现的,那么它非常适合对线性数组进行合并排序,但是如果我把它放到一个类中,它就会出错。我认为这是一个很好的例子,说明我不太了解类(class)的
我是 IOS 新手。我有一个示例项目,并试图在此基础上学习 Obj-C。 现在我正处于学习如何使用 UIAlertController 的阶段。我有这样的代码: if (loanAmount == 0
当我尝试使用 wincachegrind 并获取 cachegrind 文件时,它返回 Cannot find call target. cachegrind.out line number:68 有
我是一名优秀的程序员,十分优秀!