MySQL 学习心得和知识总结(五)|MySQL的一般查询日志(general log)


注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:

1、参考书籍:《PostgreSQL数据库内核分析》
2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
3、PostgreSQL数据库仓库链接,点击前往
4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
5、参考书籍:《PostgreSQL中文手册》
6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往
7、参考书籍:《事务处理 概念与技术》
8、Linux安装odbc连接mysql,点击前往


1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)




文章快速说明索引

学习目标:

做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。


学习内容:(详见目录)

1、MySQL的一般查询日志(general log)


学习时间:

2024-04-03 18:06:50 星期三


学习产出:

1、MySQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习


注:下面我们所有的学习环境是Centos8+PostgreSQL16.1+Oracle19C+MySQL8.0

postgres=# select version();
                                                  version                                                   
------------------------------------------------------------------------------------------------------------
 PostgreSQL 17devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21), 64-bit
(1 row)

postgres=#

#-----------------------------------------------------------------------------#

SQL> select * from v$version;          

BANNER        Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
BANNER_FULL	  Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production Version 19.17.0.0.0	
BANNER_LEGACY Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
CON_ID 0


#-----------------------------------------------------------------------------#

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.06 sec)

mysql>

一般查询日志简单介绍

日志简介参数配置

查询日志分为一般查询日志和慢查询日志。

通过查询是否超出如下变量指定时间的值来判定的,例如:

# 指定慢查询超时时长,超出此时长的属于慢查询,会记录到慢查询日志中

[root@127.0.0.1] mysql>show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set (0.01 sec)

[root@127.0.0.1] mysql>
# 定义一般查询日志和慢查询日志的输出格式,不指定时默认为file

# log_output={TABLE|FILE|NONE}
# TABLE:表示记录日志到表中
# FILE:表示记录日志到文件中
# NONE:表示不记录日志

[root@127.0.0.1] mysql>show variables like 'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | FILE  |
+---------------+-------+
1 row in set (0.01 sec)

[root@127.0.0.1] mysql>

在超时时间内完成的查询是一般查询,可以将其记录到一般查询日志中;而超出时间的查询是慢查询,可以将其记录到慢查询日志中。我们今天学习的重点在于一般查询日志,慢查询日志后面有机会详聊!


一般查询日志general log,记录了服务器接收到的每一个查询或是命令,无论这些查询或是命令是否正确甚至是否包含语法错误,general log 都会将其记录下来。

开启general log,MySQL服务器就会不断地记录日志,会产生一定的系统开销。因此所有都默认关闭一般查询日志。

开启general log,如下:

# 它是全局变量

[root@127.0.0.1] mysql>show variables like 'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

[root@127.0.0.1] mysql>set global general_log=on;
Query OK, 0 rows affected (0.00 sec)

[root@127.0.0.1] mysql>show global variables like 'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | ON    |
+---------------+-------+
1 row in set (0.01 sec)

[root@127.0.0.1] mysql>
# 设置日志文件路径,默认是库文件路径下 主机名加上.log

[root@127.0.0.1] mysql>show global variables like 'general_log_file';
+------------------+--------------------------------------------+
| Variable_name    | Value                                      |
+------------------+--------------------------------------------+
| general_log_file | /home/mysql/mysql-8.0.27/data/dbserver.log |
+------------------+--------------------------------------------+
1 row in set (0.00 sec)

[root@127.0.0.1] mysql>set global general_log_file='/home/mysql/general.log';
Query OK, 0 rows affected (0.00 sec)

[root@127.0.0.1] mysql>show variables like 'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | FILE  |
+---------------+-------+
1 row in set (0.00 sec)

[root@127.0.0.1] mysql>

文件生成内容分析

general logGeneral Query Log,记录了MySQL服务器的操作。当客户端连接、断开连接、接收到客户端的SQL语句等,都会向general log中写入日志。开启general_log会损失一定的性能(性能上的损失 下面详细解释),但是在开发、测试环境下开启日志,可以帮忙我们加快排查出现的问题。如下:

# 我这里开启两个连接 间隔执行SQL 如下:

[mysql@dbserver ~]$ cat general.log 
/home/mysql/mysql-8.0.27/bin/mysqld, Version: 8.0.27 (MySQL Community Server - GPL). started with:
Tcp port: 3306  Unix socket: /tmp/mysql.sock
Time                 Id Command    Argument
2024-04-02T06:58:16.355675Z	   10 Query	show variables like 'log_output'
2024-04-02T07:00:20.967875Z	   10 Quit	
2024-04-02T07:00:41.942253Z	   11 Connect	root@localhost on  using SSL/TLS
2024-04-02T07:00:41.944182Z	   11 Query	select @@version_comment limit 1
2024-04-02T07:00:41.945409Z	   11 Query	select USER()
2024-04-02T07:01:11.162705Z	   11 Query	SELECT DATABASE()
2024-04-02T07:01:11.163151Z	   11 Init DB	mysql
2024-04-02T07:01:25.838190Z	   12 Connect	root@localhost on  using SSL/TLS
2024-04-02T07:01:25.838263Z	   12 Connect	Access denied for user 'root'@'localhost' (using password: YES)
2024-04-02T07:01:51.785487Z	   13 Connect	root@localhost on  using SSL/TLS
2024-04-02T07:01:51.785881Z	   13 Query	select @@version_comment limit 1
2024-04-02T07:01:51.786956Z	   13 Query	select USER()
2024-04-02T07:01:57.526667Z	   13 Query	SELECT DATABASE()
2024-04-02T07:01:57.526913Z	   13 Init DB	mysql
2024-04-02T07:02:29.853995Z	   11 Query	show tables
2024-04-02T07:02:52.531882Z	   11 Query	drop table t0401
2024-04-02T07:04:43.538680Z	   11 Query	create table t0402(id int, curtime TIMESTAMP(6))
2024-04-02T07:06:27.948203Z	   11 Query	insert into t0402 values(1, CURRENT_TIMESTAMP(6))
2024-04-02T07:06:37.420331Z	   13 Query	select * from t0402
2024-04-02T07:07:04.077164Z	   13 Query	drop table t0402
2024-04-02T07:08:04.054919Z	   13 Query	create table t0402(id int, curtime_int bigint(15))
2024-04-02T07:09:14.694347Z	   13 Query	insert into t0402 values(1, REPLACE(unix_timestamp(current_timestamp(6)),'.',''))
2024-04-02T07:09:24.407249Z	   11 Query	select * from t0402
2024-04-02T07:10:18.758216Z	   11 Quit	
2024-04-02T07:10:23.518195Z	   13 Quit	
[mysql@dbserver ~]$

[mysql@dbserver ~]$ mysql -h 127.0.0.1 -u root -p -A
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 15
Server version: 8.0.27 MySQL Community Server - GPL

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

[root@127.0.0.1] (none)>use mysql;
Database changed
[root@127.0.0.1] mysql>select * from t0402;
+------+------------------+
| id   | curtime_int      |
+------+------------------+
|    1 | 1712041754694258 |
+------+------------------+
1 row in set (0.00 sec)

[root@127.0.0.1] mysql>prepare p1 as select * from t0402 where id < $1;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'as select * from t0402 where id < $1' at line 1
[root@127.0.0.1] mysql>
[root@127.0.0.1] mysql>prepare p1 from select * from t0402 where id < $1;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select * from t0402 where id < $1' at line 1
[root@127.0.0.1] mysql>
[root@127.0.0.1] mysql>prepare p1 from 'select * from t0402 where id < $1';
ERROR 1054 (42S22): Unknown column '$1' in 'where clause'
[root@127.0.0.1] mysql>
[root@127.0.0.1] mysql>prepare p1 from 'select * from t0402 where id < ?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared

[root@127.0.0.1] mysql>SET @a = 3;
Query OK, 0 rows affected (0.00 sec)

[root@127.0.0.1] mysql>EXECUTE p1 USING @a;
+------+------------------+
| id   | curtime_int      |
+------+------------------+
|    1 | 1712041754694258 |
+------+------------------+
1 row in set (0.00 sec)

[root@127.0.0.1] mysql>prepare p2 from 'select * from t0402 where id = ?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared

[root@127.0.0.1] mysql>SET @a = 1;
Query OK, 0 rows affected (0.00 sec)

[root@127.0.0.1] mysql>EXECUTE p2 USING @a;
+------+------------------+
| id   | curtime_int      |
+------+------------------+
|    1 | 1712041754694258 |
+------+------------------+
1 row in set (0.00 sec)

[root@127.0.0.1] mysql>
...
2024-04-02T07:40:34.865834Z	   15 Connect	root@localhost on  using SSL/TLS
2024-04-02T07:40:34.866145Z	   15 Query	select @@version_comment limit 1
2024-04-02T07:40:34.866744Z	   15 Query	select USER()
2024-04-02T07:40:47.075169Z	   15 Query	SELECT DATABASE()
2024-04-02T07:40:47.075481Z	   15 Init DB	mysql
2024-04-02T07:40:58.135514Z	   15 Query	select * from t0402
2024-04-02T07:42:26.177204Z	   15 Query	PREPARE p1 FROM ...
2024-04-02T07:42:40.265662Z	   15 Query	PREPARE p1 FROM ...
2024-04-02T07:42:40.265822Z	   15 Prepare	select * from t0402 where id < ?
2024-04-02T07:43:18.121465Z	   15 Query	SET @a = 3
2024-04-02T07:43:41.852319Z	   15 Query	EXECUTE p1 USING @a
2024-04-02T07:43:41.852422Z	   15 Execute	select * from t0402 where id < 3
2024-04-02T07:45:07.481268Z	   15 Query	PREPARE p2 FROM ...
2024-04-02T07:45:07.481445Z	   15 Prepare	select * from t0402 where id = ?
2024-04-02T07:45:19.637677Z	   15 Query	SET @a = 1
2024-04-02T07:45:26.939353Z	   15 Query	EXECUTE p2 USING @a
2024-04-02T07:45:26.939432Z	   15 Execute	select * from t0402 where id = 1
[mysql@dbserver ~]$

在这里插入图片描述

如上图所示:

  • 有语法错误的没有记入
  • 有错 但非 语法错误,则仍然记入
  • simple query记入一行 Query
  • 一个prepare/execute 记入两行:Query Prepare/Execute

如上各列说明,如下:

  • Time 日志记录的时间
  • Id 进程ID,可以通过show processlist命令查看
  • Command 执行的命令
  • Argument 命令参数

这里的命令类型,如下:

// sql\sql_parse.cc

const std::string Command_names::m_names[] = {
    "Sleep",
    "Quit",
    "Init DB", // 使用命令use选择库时的记录
    "Query",
    "Field List",
    "Create DB",
    "Drop DB",
    "Refresh",
    "Shutdown",
    "Statistics",
    "Processlist",
    "Connect",
    "Kill",
    "Debug",
    "Ping",
    "Time",
    "Delayed insert",
    "Change user",
    "Binlog Dump",
    "Table Dump",
    "Connect Out",
    "Register Replica",
    "Prepare",
    "Execute",
    "Long Data",
    "Close stmt",
    "Reset stmt",
    "Set option",
    "Fetch",
    "Daemon",
    "Binlog Dump GTID",
    "Reset Connection",
    "clone",
    "Group Replication Data Stream subscription",
    "Error"  // Last command number
};
// include\my_command.h

/**
  @enum  enum_server_command

  @brief A list of all MySQL protocol commands.

  These are the top level commands the server can receive
  while it listens for a new command in ::dispatch_command

  @par Warning
  Add new commands to the end of this list, otherwise old
  servers won't be able to handle them as 'unsupported'.
*/
enum enum_server_command {
  /**
    Currently refused by the server. See ::dispatch_command.
    Also used internally to mark the start of a session.
  */
  COM_SLEEP,
  COM_QUIT,       /**< See @ref page_protocol_com_quit */
  COM_INIT_DB,    /**< See @ref page_protocol_com_init_db */
  COM_QUERY,      /**< See @ref page_protocol_com_query */
  COM_FIELD_LIST, /**< Deprecated. See @ref page_protocol_com_field_list */
  COM_CREATE_DB, /**< Currently refused by the server. See ::dispatch_command */
  COM_DROP_DB,   /**< Currently refused by the server. See ::dispatch_command */
  COM_REFRESH,   /**< Deprecated. See @ref page_protocol_com_refresh */
  COM_DEPRECATED_1, /**< Deprecated, used to be COM_SHUTDOWN */
  COM_STATISTICS,   /**< See @ref page_protocol_com_statistics */
  COM_PROCESS_INFO, /**< Deprecated. See @ref page_protocol_com_process_info */
  COM_CONNECT,      /**< Currently refused by the server. */
  COM_PROCESS_KILL, /**< Deprecated. See @ref page_protocol_com_process_kill */
  COM_DEBUG,        /**< See @ref page_protocol_com_debug */
  COM_PING,         /**< See @ref page_protocol_com_ping */
  COM_TIME,         /**< Currently refused by the server. */
  COM_DELAYED_INSERT, /**< Functionality removed. */
  COM_CHANGE_USER,    /**< See @ref page_protocol_com_change_user */
  COM_BINLOG_DUMP,    /**< See @ref page_protocol_com_binlog_dump */
  COM_TABLE_DUMP,
  COM_CONNECT_OUT,
  COM_REGISTER_SLAVE,
  COM_STMT_PREPARE, /**< See @ref page_protocol_com_stmt_prepare */
  COM_STMT_EXECUTE, /**< See @ref page_protocol_com_stmt_execute */
  /** See  @ref page_protocol_com_stmt_send_long_data */
  COM_STMT_SEND_LONG_DATA,
  COM_STMT_CLOSE, /**< See @ref page_protocol_com_stmt_close */
  COM_STMT_RESET, /**< See @ref page_protocol_com_stmt_reset */
  COM_SET_OPTION, /**< See @ref page_protocol_com_set_option */
  COM_STMT_FETCH, /**< See @ref page_protocol_com_stmt_fetch */
  /**
    Currently refused by the server. See ::dispatch_command.
    Also used internally to mark the session as a "daemon",
    i.e. non-client THD. Currently the scheduler and the GTID
    code does use this state.
    These threads won't be killed by `KILL`

    @sa Event_scheduler::start, ::init_thd, ::kill_one_thread,
    ::Find_thd_with_id
  */
  COM_DAEMON,
  COM_BINLOG_DUMP_GTID,
  COM_RESET_CONNECTION, /**< See @ref page_protocol_com_reset_connection */
  COM_CLONE,
  COM_SUBSCRIBE_GROUP_REPLICATION_STREAM,
  /* don't forget to update const char *command_name[] in sql_parse.cc */

  /* Must be last */
  COM_END /**< Not a real command. Refused. */
};

如上文件中的时间值,转换为微秒的时间戳 如下:

[root@127.0.0.1] (none)>select REPLACE(unix_timestamp('2024-04-02T07:10:18.758216Z'),'.','');
+---------------------------------------------------------------+
| REPLACE(unix_timestamp('2024-04-02T07:10:18.758216Z'),'.','') |
+---------------------------------------------------------------+
| 1712013018758216                                              |
+---------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

[root@127.0.0.1] (none)>show variables like "%time_zone%";
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | CST    |
| time_zone        | SYSTEM |
+------------------+--------+
2 rows in set (0.00 sec)

[root@127.0.0.1] (none)>select now();
+---------------------+
| now()               |
+---------------------+
| 2024-04-02 15:29:36 |
+---------------------+
1 row in set (0.00 sec)

[root@127.0.0.1] (none)>
[root@127.0.0.1] (none)>select REPLACE(unix_timestamp('2024-04-02 07:10:18.758216'),'.','');
+--------------------------------------------------------------+
| REPLACE(unix_timestamp('2024-04-02 07:10:18.758216'),'.','') |
+--------------------------------------------------------------+
| 1712013018758216                                             |
+--------------------------------------------------------------+
1 row in set (0.00 sec)

[root@127.0.0.1] (none)>

这里的prepare操作,转换如下:

2024-04-02T07:45:07.481268Z	   15 Query	PREPARE p2 FROM ...
2024-04-02T07:45:07.481445Z	   15 Prepare	select * from t0402 where id = ?
2024-04-02T07:45:19.637677Z	   15 Query	SET @a = 1
2024-04-02T07:45:26.939353Z	   15 Query	EXECUTE p2 USING @a
2024-04-02T07:45:26.939432Z	   15 Execute	select * from t0402 where id = 1

// sql\sql_rewrite.cc

/**
  Rewrite the query for the PREPARE statement.

  @param[in,out] rlb     Buffer to return the rewritten query in.

  @retval        true    the query was rewritten
  @retval        false   otherwise
*/
bool Rewriter_prepare::rewrite(String &rlb) const {
  LEX *lex = m_thd->lex;

  if (lex->prepared_stmt_code_is_varref) return false;

  rlb.append(STRING_WITH_LEN("PREPARE "));
  rlb.append(lex->prepared_stmt_name.str, lex->prepared_stmt_name.length);
  rlb.append(STRING_WITH_LEN(" FROM ..."));
  return true;
}

内容生成源码解析

// sql\log.cc

bool File_query_log::write_general(ulonglong event_utime,
                                   my_thread_id thread_id,
                                   const char *command_type,
                                   size_t command_type_len,
                                   const char *sql_text, size_t sql_text_len) {
  char buff[32];
  size_t length = 0;

  mysql_mutex_lock(&LOCK_log);
  assert(is_open());

  /* Note that my_b_write() assumes it knows the length for this */
  char local_time_buff[iso8601_size];
  int time_buff_len = make_iso8601_timestamp(local_time_buff, event_utime,
                                             iso8601_sysvar_logtimestamps);

  if (my_b_write(&log_file, pointer_cast<uchar *>(local_time_buff),
                 time_buff_len))
    goto err;

  if (my_b_write(&log_file, pointer_cast<const uchar *>("\t"), 1)) goto err;

  length = snprintf(buff, 32, "%5u ", thread_id);

  if (my_b_write(&log_file, pointer_cast<uchar *>(buff), length)) goto err;

  if (my_b_write(&log_file, pointer_cast<const uchar *>(command_type),
                 command_type_len))
    goto err;

  if (my_b_write(&log_file, pointer_cast<const uchar *>("\t"), 1)) goto err;

  /* sql_text */
  if (my_b_write(&log_file, pointer_cast<const uchar *>(sql_text),
                 sql_text_len))
    goto err;

  if (my_b_write(&log_file, pointer_cast<const uchar *>("\n"), 1) ||
      flush_io_cache(&log_file))
    goto err;

  mysql_mutex_unlock(&LOCK_log);
  return false;

err:
  check_and_print_write_error();
  mysql_mutex_unlock(&LOCK_log);
  return true;
}

odbc连接内容生成

SQL> select version();
+----------+
| version()|
+----------+
| 8.0.27   |
+----------+
SQLRowCount returns 1
1 rows fetched
SQL> 
[mysql@dbserver ~]$ sudo yum install unixODBC unixODBC-devel libtool-ltdl libtool-ltdl-devel
已加载插件:ulninfo
软件包 unixODBC-2.3.1-14.0.1.el7.x86_64 已安装并且是最新版本
软件包 unixODBC-devel-2.3.1-14.0.1.el7.x86_64 已安装并且是最新版本
软件包 libtool-ltdl-2.4.2-22.el7_3.x86_64 已安装并且是最新版本
软件包 libtool-ltdl-devel-2.4.2-22.el7_3.x86_64 已安装并且是最新版本
无须任何处理
[mysql@dbserver ~]$ 

## https://downloads.mysql.com/archives/c-odbc/
[mysql@dbserver odbc_mysql8]$ wget https://cdn.mysql.com/archives/mysql-connector-odbc-8.0/mysql-connector-odbc-8.0.20-1.el7.x86_64.rpm
--2024-04-03 16:09:12--  https://cdn.mysql.com/archives/mysql-connector-odbc-8.0/mysql-connector-odbc-8.0.20-1.el7.x86_64.rpm
正在解析主机 cdn.mysql.com (cdn.mysql.com)... 2402:4f00:4002:19d::1d68, 2402:4f00:4002:190::1d68, 23.7.220.59
正在连接 cdn.mysql.com (cdn.mysql.com)|2402:4f00:4002:19d::1d68|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:4302344 (4.1M) [application/x-redhat-package-manager]
正在保存至: “mysql-connector-odbc-8.0.20-1.el7.x86_64.rpm”

100%[=========================================================================================================================================================================>] 4,302,344   3.47MB/s 用时 1.2s   

2024-04-03 16:09:16 (3.47 MB/s) - 已保存 “mysql-connector-odbc-8.0.20-1.el7.x86_64.rpm” [4302344/4302344])

[mysql@dbserver odbc_mysql8]$ ls
mysql-connector-odbc-8.0.20-1.el7.x86_64.rpm
[mysql@dbserver odbc_mysql8]$ 
[mysql@dbserver odbc_mysql8]$ sudo rpm -ivh mysql-connector-odbc-8.0.20-1.el7.x86_64.rpm 
警告:mysql-connector-odbc-8.0.20-1.el7.x86_64.rpm: 头V3 DSA/SHA1 Signature, 密钥 ID 5072e1f5: NOKEY
准备中...                          ################################# [100%]
正在升级/安装...
   1:mysql-connector-odbc-8.0.20-1.el7################################# [100%]
Success: Usage count is 1
Success: Usage count is 1
[mysql@dbserver odbc_mysql8]$

测试unixODBC安装是否成功,如下:

[mysql@dbserver ~]$ odbcinst -j
unixODBC 2.3.1
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /home/mysql/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
[mysql@dbserver ~]$ odbcinst -q -d
[PostgreSQL]
[MySQL]
[MySQL ODBC 8.0 Unicode Driver]
[MySQL ODBC 8.0 ANSI Driver]
[mysql@dbserver ~]$

配置 odbc.ini,如下:

[mysql@dbserver ~]$ odbcinst -j
unixODBC 2.3.1
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /home/mysql/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
[mysql@dbserver ~]$ odbcinst -q -d
[PostgreSQL]
[MySQL]
[MySQL ODBC 8.0 Unicode Driver]
[MySQL ODBC 8.0 ANSI Driver]
[mysql@dbserver ~]$ 
[mysql@dbserver ~]$ sudo vim /etc/odbc.ini
[mysql@dbserver ~]$ 
[mysql@dbserver ~]$ cat /etc/odbc.ini
[mysql]
Description     = Data source sampleMySQL for accessing MySQL database sampleMySQL
Driver          = MySQL ODBC 8.0 Unicode Driver
Server          = 127.0.0.1
Host            = 127.0.0.1
Database        = mysql
Port            = 3306
User            = root
Password        =123456
[mysql@dbserver ~]$ 
[mysql@dbserver ~]$ isql -v mysql
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL>

我这里使用odbc进行操作,日志内容 如下:

[mysql@dbserver ~]$ cat general.log | grep '12 '
2024-04-03T08:26:37.163240Z	   12 Connect	root@localhost on mysql using SSL/TLS
2024-04-03T08:26:37.163849Z	   12 Query	SET NAMES utf8
2024-04-03T08:26:37.164332Z	   12 Query	SET character_set_results = NULL
2024-04-03T08:26:37.164538Z	   12 Query	SET SQL_AUTO_IS_NULL = 0
2024-04-03T08:27:18.292965Z	   12 Query	set @@sql_select_limit=DEFAULT
2024-04-03T08:27:18.293298Z	   12 Query	desc t0402
2024-04-03T08:27:35.010027Z	   12 Query	select * from t0402
2024-04-03T08:28:04.549888Z	   12 Query	PREPARE odbcp1 FROM ...
2024-04-03T08:28:04.550654Z	   12 Prepare	select * from t0402 where id < ?
2024-04-03T08:28:32.707333Z	   12 Query	SET @a = 3
2024-04-03T08:28:58.102807Z	   12 Query	EXECUTE odbcp1 USING @a
2024-04-03T08:28:58.102929Z	   12 Execute	select * from t0402 where id < 3
2024-04-03T08:29:12.346332Z	   12 Quit	
[mysql@dbserver ~]$

可以看出使用odbc的日志记录(格式/内容)上,和上面直接使用client一致!


一般查询日志性能影响


有时,需要启用通用查询日志(默认情况下禁用)。如果启用了通用查询日志,则当客户端连接或断开连接时,以及从客户端接收到的每个 SQL 语句时,服务器都会写入此日志信息。问题是:

  • 启用通用查询日志会影响MySQL性能吗?
  • 另外,可以将该日志的输出记录到mysql数据库(mysql.general_log)中的文件或表中,每种记录对性能有何影响?

让我们针对这些场景做一些简单的基准测试来衡量对 mysql 性能的实际影响。


系统信息

硬件配置:

  • CPU: Intel® Core™ i7-3520M Processor (4M Cache, up to 3.60 GHz).
    • 2 cores, 4 threads, HT enabled.
  • Memory: 8GB RAM (1600).
  • Storage: HDD 1TB/ 5400RPM.

软件配置:

  • OS: Ubuntu 12.04
  • MySQL Server: 5.6.17
  • Sysbench: 0.4.12

测试信息:

  • Sysbench命令:
sysbench --num-threads=1 --max-requests=1000 --db-driver=mysql --test=oltp --mysql-table-engine=InnoDB --mysql-user=root run 
  • sysbench 测试使用的表结构
mysql> show create table sbtest.sbtest\G

CREATE TABLE `sbtest` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `k` int(10) unsigned NOT NULL DEFAULT '0',
  `c` char(120) NOT NULL DEFAULT '',
  `pad` char(60) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `k` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=8574 DEFAULT CHARSET=latin1

注意:

该测试针对 1、2、4、8、16 和 32 个线程进行,每个测试用例的每个线程数的每个吞吐量/响应时间值是由十 (10) 次执行的平均值生成的。


禁用一般查询日志

要确保禁用常规查询日志:

mysql> show global variables like'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | OFF   |
+---------------+-------+

测试结果,如下:

在这里插入图片描述

启用一般查询日志

通用查询日志是一个动态变量,这意味着它可以在线启用或禁用,而无需重新启动 MySQL(自 MySQL 5.1 起):

mysql> SET GLOBAL general_log=ON;

我们可以选择将此日志输出到日志文件(默认情况下)或 MySQL 表 (mysql.general_log) 中。如果我们将日志输出存储在表而不是文件中,我们可能会得到什么好处?

  • 我们可以使用普通的 SQL 语句访问日志内容来获取有关特定条件的信息(例如使用 WHERE 条件),这在处理文件时有点困难
  • 如果有人可以连接到 MySQL 服务器,则可以远程访问日志内容
  • 日志条目的标准格式
  • 如果日志表使用 CSV 引擎,则可以轻松将 CSV 文件导入到电子表格中
  • 只需 TRUNCATE 日志表即可轻松使日志过期
  • 通过使用 RENAME TABLE 语句可以进行日志轮转
  • 日志条目不会复制到从属服务器,因为它们不会写入二进制日志
  • 即使使用了 --all-databases 备份选项,mysqldump 也不在备份中包含日志表内容(general_log 或 Slow_log)

那么,让我们检查一下每个日志输出对性能的影响。

输出是文件

要检查通用查询日志的输出目的地,应使用以下命令:

mysql> show global variables like'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | FILE  |
+---------------+-------+

测试结果,如下:

在这里插入图片描述

输出为表(CSV 表)

要将通用查询日志的输出目的地从文件更改为表(默认为 CSV),应使用以下命令:

mysql> SET GLOBAL log_output='TABLE';
mysql> show global variables like'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | TABLE |
+---------------+-------+

测试结果,如下:

在这里插入图片描述

输出为表(MYISAM 表)

也许由于 CSV 存储引擎的性质,我们在之前的案例中遇到了性能问题。是否可以更改general_log表的表引擎?

答案是肯定的,但不幸的是,我们被限制只能使用 MyISAM 存储引擎,不允许使用 CSV 或 MyISAM 以外的引擎。检查此链接以获取更多信息。

要更改日志表,您必须首先禁用日志记录:

mysql> alter table mysql.general_log engine=MYISAM;
ERROR 1580 (HY000): You cannot 'ALTER' a log table if logging is enabled
mysql> SET GLOBAL general_log=OFF;

mysql> alter table mysql.general_log engine=MYISAM;

mysql> SET GLOBAL general_log=ON;

测试结果,如下:

在这里插入图片描述

输出为表(MYISAM 表,结构发生一些变化)

一般来说,为了使任何 SQL 查询运行得更快,我们可能需要优化表结构、添加索引、重写查询等。

通用查询日志表结构如下:

mysql> show create table mysql.general_log\G

CREATE TABLE `general_log` (
  `event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `user_host` mediumtext NOT NULL,
  `thread_id` bigint(21) unsigned NOT NULL,
  `server_id` int(10) unsigned NOT NULL,
  `command_type` varchar(64) NOT NULL,
  `argument` mediumtext NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='General log'

我们来看看如何优化general_log表结构(欢迎其他建议):

  • 我们可以在该表上创建分区来提高我们的搜索吗?虽然这是一个普通的 MyISAM 表,但日志表不允许分区(顺便说一句,CSV 表也不允许分区)
  • 我们可以将user_host列的数据类型从mediumtext更改为例如 varchar(100)?(我的机器上该列数据的最大长度不超过50个字符)虽然它在语法方面已被接受,但此后表中不会存储任何日志,并且错误日志文件中将打印以下错误:
2014-03-06 18:44:21 6987 [ERROR] Failed to write to mysql.general_log:
2014-03-06 18:44:23 6987 [ERROR] Incorrect definition of table mysql.general_log: expected column 'user_host' at position 1 to have type mediumtext, found type varchar(100).
  • 在我们将用于大多数搜索的列(event_timeargument)上创建索引怎么样?

    • event_time 列上添加索引
mysql> SET GLOBAL general_log=OFF;

mysql> alter table mysql.general_log add index ev_tm_idx(`event_time`);

mysql> show create table mysql.general_log\G

CREATE TABLE `general_log` (
  `event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `user_host` mediumtext NOT NULL,
  `thread_id` bigint(21) unsigned NOT NULL,
  `server_id` int(10) unsigned NOT NULL,
  `command_type` varchar(64) NOT NULL,
  `argument` mediumtext NOT NULL,
  KEY `ev_tm_idx` (`event_time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='General log'

mysql> SET GLOBAL general_log=ON;

测试结果,如下:

在这里插入图片描述

    • argument列上添加全文索引
mysql> SET GLOBAL general_log=OFF;

mysql> alter table mysql.general_log add fulltext index (`argument`);

mysql> show create table mysql.general_log\G

CREATE TABLE `general_log` (
  `event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `user_host` mediumtext NOT NULL,
  `thread_id` bigint(21) unsigned NOT NULL,
  `server_id` int(10) unsigned NOT NULL,
  `command_type` varchar(64) NOT NULL,
  `argument` mediumtext NOT NULL,
  KEY `ev_tm_idx` (`event_time`),
  FULLTEXT KEY `argument` (`argument`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='General log'

mysql> SET GLOBAL general_log=ON;

测试结果,如下:

在这里插入图片描述
为了更清楚地说明,以下是将所有结果组合在一张图表中,然后进行响应时间比较:

在这里插入图片描述
在这里插入图片描述
每秒事务数的原始结果可能有用:

Threads 1 2 4 8 16 32
General Query Log disabled 383.996 814.759 1421.288 1674.733 1414.985 1071.189
General Query Log enabled (File) 281.642 521.39 1230.743 1406.127 1095.896 923.986
General Query Log enabled (CSV Table) 231.659 447.173 787.578 507.846 426.324 439.992
General Query Log enabled (MyISAM Table) 249.47 536.379 933.304 532.912 476.454 454.015
General Query Log enabled (MyISAM Table + index) 238.508 430.05 875.209 465.464 465.464 395.063
General Query Log enabled (MyISAM Table + Fulltext index) 157.436 236.156 210.968 212.273 218.617 220.701

结论

  • 在上述所有测试用例中,最好的MySQL性能可以通过禁用常规查询日志来实现,例如,如果我们比较上述4并发线程情况的结果(大多数情况下的最高值),我们会发现:

    • 使用启用的通用查询日志 (general_log = ON) 且日志目标为文件 (log_output = FILE) 可使吞吐量降低 13.4%,并使响应时间增加 17.5%
    • 使用启用的通用查询日志并且日志目标是 CSV 表,吞吐量降低了 44.6%,响应时间增加了 90%
    • 使用启用的通用查询日志并且日志目标是 MyISAM 表,吞吐量降低了 34.3%,响应时间增加了 59%
    • 使用启用的通用查询日志并且日志目标是 MyISAM,并在event_time列上添加索引,吞吐量降低了 38.4%,响应时间增加了 73%
    • 使用启用的通用查询日志并且日志目标是 MyISAM,在event_time列上添加索引并在argument列上添加 FULLTEXT 索引,吞吐量降低了 85%,响应时间增加了 542%
  • 虽然使用表作为日志输出目的地有很多好处(如上所述),但与日志文件相比,它对 MySQL 性能的负面影响更大

  • 增加并发运行的线程数(在 log_output=TABLE 的情况下)将增加general_log 表争用,该争用由MyISAM 或CSV ENGINES 的表锁定级别控制

  • 与任何其他 MySQL 表一样 - 日志表中插入的行数越多,负面性能影响越大

  • 虽然 mysqldump 不包括备份中的日志表内容,但使用 Xtrabackup 或任何其他基于物理备份的工具进行完整物理备份时,情况并非如此

  • 最后,最好只在真正需要时才启用通用查询日志,不建议在生产系统中启用它。它可以(动态)启用一段时间,然后在我们获得要搜索的内容后再次禁用

相关推荐

  1. 总结MySQL 一些知识点:MySQL 运算符

    2024-04-07 20:56:02       47 阅读
  2. mysql查询日志使用步骤

    2024-04-07 20:56:02       40 阅读
  3. 总结vue3 一些知识点:MySQL 排序

    2024-04-07 20:56:02       41 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-07 20:56:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-07 20:56:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-07 20:56:02       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-07 20:56:02       20 阅读

热门阅读

  1. Mongodb 入门

    2024-04-07 20:56:02       23 阅读
  2. Windows SDK(五)按钮静态文本与编辑框控件

    2024-04-07 20:56:02       25 阅读
  3. 用tkinter来实现扫雷游戏

    2024-04-07 20:56:02       48 阅读
  4. 4月06日,每日信息差

    2024-04-07 20:56:02       15 阅读
  5. Circuits--Sequential--Registers_1

    2024-04-07 20:56:02       17 阅读
  6. MongoDB

    2024-04-07 20:56:02       19 阅读
  7. 应用开发:python解析斗鱼弹幕

    2024-04-07 20:56:02       18 阅读
  8. 【蓝桥杯第十届省赛B】(部分详解)

    2024-04-07 20:56:02       18 阅读