Mysql 问题与源码

Mysql 问题与源码
本页内容

问题1: 连接数为214, 登录经常报错 too many connections

在公司MySQL企业版服务化开发的初期,我们曾经遇到一个问题,即在连接MySQL的时候报错 too many connections, 即使是新安装的MySQL。在以前的社区版MySQL也曾经遇到过类似的问题,当时MySQL是用rpm安装并使用systemd启动的方式。 此次企业版的MySQL启动并未托管到systemd,因此解决办法不能照搬。

定位过程

为了能够登录,首先只能重启MySQL,执行

show variables like "max_conne%";

发现连接数并非配置文件中定义的 2000,而是一个奇怪的数字 214;

执行

ulimit -a 或者 cat /proc/pidof mysqld/limits

发现 open files 为一个较低的默认值 1024;(代码中有改动该值的逻辑,但是最终并未生效,最终发现是公司系统镜
像/etc/security/limits.d/...的默认值有问题,此处不延伸)

core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 23883 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 23883 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited

=== 为什么是214?
查看mysqld.cc:adjust_max_connections发现原因:

[source, c++]

void adjust_max_connections(ulong requested_open_files) { ulong limit;

// TABLE_OPEN_CACHE_MIN = 400
// requested_open_files = 1024
limit= requested_open_files - 10 - TABLE_OPEN_CACHE_MIN * 2;

if (limit < max_connections)
{
  sql_print_warning("Changed limits: max_connections: %lu (requested %lu)",
                    limit, max_connections);

    // This can be done unprotected since it is only called on startup.
    max_connections= limit;
  }
}

计算很简单,看一下此处,应该会打印出一行警告日志,可以试试看,日志中是否可以找到这样的信息。

sql_print_warning(“Changed limits: max_connections: %lu (requested %lu)”, limit, max_connections);

值得关注的点是,这个requested_open_files有一个比较复杂的计算过程。

.mysqld.cc:adjust_related_options
[source, c++]

void adjust_related_options(ulong requested_open_files) { / In bootstrap, disable grant tables (we are about to create them) */ if (opt_bootstrap) opt_noacl= 1;

  /* The order is critical here, because of dependencies. */
  adjust_open_files_limit(requested_open_files);
  adjust_max_connections(*requested_open_files);
  adjust_table_cache_size(*requested_open_files);
  adjust_table_def_size();
}

.mysqld.cc:adjust_open_files_limit
[source, c++]

/** Adjust @c open_files_limit. Computation is based on: - @c max_connections, - @c table_cache_size, - the platform max open file limit. */ void adjust_open_files_limit(ulong *requested_open_files) { ulong limit_1; ulong limit_2; ulong limit_3; ulong request_open_files; ulong effective_open_files;

/*这里会有三种计算方案*/

/* MyISAM requires two file handles per table. */
limit_1= 10 + max_connections + table_cache_size * 2;

/*
  We are trying to allocate no less than max_connections*5 file
  handles (i.e. we are trying to set the limit so that they will
  be available).
*/
limit_2= max_connections * 5;

/* Try to allocate no less than 5000 by default. */
//这里可以解释了,为什么很多的系统安装后, /proc/`pidof mysqld`/limits中的值为5000
//但是这里的代码,是否应该改为 open_files_limit> 5000 ? open_files_limit : 5000;
limit_3= open_files_limit ? open_files_limit : 5000;

// 取三种方案的最大值
request_open_files= max<ulong>(max<ulong>(limit_1, limit_2), limit_3);

/* Notice: my_set_max_open_files() may return more than requested. */
effective_open_files= my_set_max_open_files(request_open_files);

if (effective_open_files < request_open_files)
{
  if (open_files_limit == 0)
  {
    sql_print_warning("Changed limits: max_open_files: %lu (requested %lu)",
                      effective_open_files, request_open_files);
  }
  else
  {
    sql_print_warning("Could not increase number of max_open_files to "
                      "more than %lu (request: %lu)",
                      effective_open_files, request_open_files);
  }
}

  open_files_limit= effective_open_files;
  if (requested_open_files)
    *requested_open_files= min<ulong>(effective_open_files, request_open_files);
}

.my_file.c:my_set_max_open_files
[source, c++]

uint my_set_max_open_files(uint files) { struct st_my_file_info *tmp; DBUG_ENTER(“my_set_max_open_files”); DBUG_PRINT(“enter”,(“files: %u my_file_limit: %u”, files, my_file_limit));

files+= MY_FILE_MIN;
files= set_max_open_files(MY_MIN(files, OS_FILE_LIMIT));
if (files <= MY_NFILE)
  DBUG_RETURN(files);

if (!(tmp= (struct st_my_file_info*) my_malloc(key_memory_my_file_info,
                                               sizeof(*tmp) * files,
                                               MYF(MY_WME))))
  DBUG_RETURN(MY_NFILE);

  /* Copy any initialized files */
  memcpy((char*) tmp, (char*) my_file_info,
         sizeof(*tmp) * MY_MIN(my_file_limit, files));
  memset((tmp + my_file_limit), 0,
        MY_MAX((int) (files - my_file_limit), 0) * sizeof(*tmp));
  my_free_open_file_info();                     /* Free if already allocated */
  my_file_info= tmp;
  my_file_limit= files;
  DBUG_PRINT("exit",("files: %u", files));
  DBUG_RETURN(files);
}

因此,如果希望max_connections=2000,requested_open_files不能小于stem:[2000+10+400 \times 2=2810].