目录结构
注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:
1、参考书籍:《PostgreSQL数据库内核分析》
2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
3、PostgreSQL数据库仓库链接,点击前往
4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
5、参考书籍:《PostgreSQL中文手册》
6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往
7、参考书籍:《事务处理 概念与技术》
8、Linux安装odbc连接mysql,点击前往
1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
MySQL的一般查询日志 general log
- 文章快速说明索引
- 一般查询日志简单介绍
- 日志简介参数配置
- 文件生成内容分析
- odbc连接内容生成
- 一般查询日志性能影响
- 系统信息
- 禁用一般查询日志
- 启用一般查询日志
- 输出是文件
- 输出为表(CSV 表)
- 输出为表(MYISAM 表)
- 输出为表(MYISAM 表,结构发生一些变化)
- 结论
文章快速说明索引
学习目标:
做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于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 log
即General 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;
}
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一致!
一般查询日志性能影响
- IMPACT OF GENERAL QUERY LOG ON MYSQL PERFORMANCE,点击前往
有时,需要启用通用查询日志(默认情况下禁用)。如果启用了通用查询日志,则当客户端连接或断开连接时,以及从客户端接收到的每个 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_time
和argument
)上创建索引怎么样? -
- 在
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,并在
-
- 使用启用的通用查询日志并且日志目标是 MyISAM,在
event_time
列上添加索引并在argument
列上添加 FULLTEXT 索引,吞吐量降低了 85%,响应时间增加了 542%
- 使用启用的通用查询日志并且日志目标是 MyISAM,在
-
虽然使用表作为日志输出目的地有很多好处(如上所述),但与日志文件相比,它对 MySQL 性能的负面影响更大
-
增加并发运行的线程数(在 log_output=TABLE 的情况下)将增加general_log 表争用,该争用由MyISAM 或CSV ENGINES 的表锁定级别控制
-
与任何其他 MySQL 表一样 - 日志表中插入的行数越多,负面性能影响越大
-
虽然 mysqldump 不包括备份中的日志表内容,但使用 Xtrabackup 或任何其他基于物理备份的工具进行完整物理备份时,情况并非如此
-
最后,最好只在真正需要时才启用通用查询日志,不建议在生产系统中启用它。它可以(动态)启用一段时间,然后在我们获得要搜索的内容后再次禁用