引言
作为一个 MySQL 数据库从业者,我们最常用的工具就是 mysql 了,然而如何让它变的更好用,你了解吗?
mysql Client Commands
作为连接 MySQL 数据库的工具,mysql 其实有很多非常有用的命令设置,有一些是我们日常使用的,比如 \g、\G、\q,也有我们不太常用的 \P、\T。今天分享一下我对这些设置的理解,希望能对大家在日常的工作中,有所帮助。
支持哪些设置
mysql 提供了 help 命令,用以说明支持哪些命令的设置,我们先看一下
Great[(none)]> help ;
For information about Percona products and services, visit:
http://www.percona.com/
Percona Server manual: http://www.percona.com/doc/percona-server/8.0/
For the MySQL Reference Manual: http://dev.mysql.com/
To buy Percona support, training, or other products, visit:
https://www.percona.com/
List of all MySQL commands:
Note that all text commands must be first on line and end with ';'
? (\?) Synonym for 'help'.
clear (\c) Clear the current input statement.
connect (\r) Reconnect to the server. Optional arguments are db and host.
delimiter (\d) Set statement delimiter.
edit (\e) Edit command with $EDITOR.
ego (\G) Send command to mysql server, display result vertically.
exit (\q) Exit mysql. Same as quit.
go (\g) Send command to mysql server.
help (\h) Display this help.
nopager (\n) Disable pager, print to stdout.
notee (\t) Don't write into outfile.
pager (\P) Set PAGER [to_pager]. Print the query results via PAGER.
print (\p) Print current command.
prompt (\R) Change your mysql prompt.
quit (\q) Quit mysql.
rehash (\#) Rebuild completion hash.
source (\.) Execute an SQL script file. Takes a file name as an argument.
source_decrypt Execute an encrypted script file. Takes file name, decrypt key as arguments.
status (\s) Get status information from the server.
system (\!) Execute a system shell command.
tee (\T) Set outfile [to_outfile]. Append everything into given outfile.
use (\u) Use another database. Takes database name as argument.
charset (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.
warnings (\W) Show warnings after every statement.
nowarning (\w) Don't show warnings after every statement.
resetconnection(\x) Clean session context.
query_attributes Sets string parameters (name1 value1 name2 value2 ...) for the next query to pick up.
For server side help, type 'help contents'
?
显示帮助信息,列出所有支持的操作命令,这个最简单,就不做进一步说明了。
clear(\c)
MySQL 手册介绍为清除当前输入的 SQL 语句,我个人更愿意理解为撤销已输入 SQL 的执行。看下面的例子:
Great[test]> select * from tt1;
+------+------+------+------+
| id | dd | c1 | c2 |
+------+------+------+------+
| 1 | aaa | NULL | NULL |
| 2 | bbb | NULL | NULL |
| 3 | NULL | NULL | NULL |
| 4 | 8 | NULL | NULL |
| 5 | NULL | NULL | NULL |
+------+------+------+------+
5 rows in set (0.01 sec)
Great[test]> delete from tt1
-> where id = 5
-> and c1 is not null
->
当我输入完上面的 SQL 时,发现逻辑未考虑全,不想执行这个 SQL 了,怎么办呢?这里有好几种解决办法:你可以 ctrl+c 终止命令的执行,也可以关闭当前的 mysql 客户端,甚至关闭操作命令的电脑。然后有一种标准且简单的方式,就是在命令的最后加上 \c,就可以实现:
Great[test]> delete from tt1
-> where id = 5
-> and c1 is not null
-> \c
Great[test]>
可以看到,在命令窗口最后输入 \c 后,SQL 不会被执行,并且重新启动新的命令行接收客户端输入。需要注意的是:\c 和需要取消的 SQL 间,不能有分隔符 (默认为分号 ;),否则会先执行分隔符前面的 SQL,然后再执行 \c,这时就达不到取消已输入 SQL 执行的效果了。
connect(\r)
重新连接数据库服务端,支持重连过程中,指定 database 名字和连接主机。
这个功能看起来好像没什么新奇的,客户端执行了 SQL,就算连接超时了,默认情况下,自动就会重新连接服务端。
但是在 MGR、主从复制场景下,如果所有实例的维护账号都具有相同的用户名、密码、端口,那么通过 \r 就很方便的切换多个实例进行维护,而不需要重新输入其他连接信息
Great[test]> select @@report_host;
+---------------+
| @@report_host |
+---------------+
| 172.16.50.82 |
+---------------+
1 row in set (0.00 sec)
Great[test]> \r test 172.16.50.81
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Connection id: 911698
Current database: test
Great[test]> select @@report_host;
+---------------+
| @@report_host |
+---------------+
| 172.16.50.81 |
+---------------+
1 row in set (0.00 sec)
Great[test]>
重连信息中的 ip 地址,也可以是在 /etc/hosts 中配置的主机名
Great[test]> select @@report_host;
+---------------+
| @@report_host |
+---------------+
| 172.16.50.81 |
+---------------+
1 row in set (0.00 sec)
Great[test]> \r test Great82
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Connection id: 2460607
Current database: test
Great[test]> select @@report_host;
+---------------+
| @@report_host |
+---------------+
| 172.16.50.82 |
+---------------+
1 row in set (0.00 sec)
Great[test]>
delimiter(\d)
自定义分隔符,在创建、修改存储过程、函数、触发器、事件、视图都会用到,替换用于替换默认的分号分隔符。
edit(\e)
官网解释说命令用于编辑当前输入 SQL 命令,默认的编辑器是 vi,也可以通过设置环境变量 EDITOR 来改变成其他的编辑器,比如调整为 vim 编辑器 export EDITOR=$(which vim)。
有这个命令后,输出错误的 SQL,就不需要再用 \c 终止了,而是直接在其后加上 \e 进行编辑,修改成正确的后,再执行。
比如我实际上想要执行的命令中 tt2 表不存在,那么只需要通过 \e 更新 SQL 语句中的表名字就可以继续执行,不需要再重新编辑整条 SQL。
Great[test]> select * from test.tt2 \e
vi 中替换表名字的操作就不再演示了,编辑后的执行情况如下:
Great[test]> select * from test.tt2 \e
-> ;
+------+------+------+------+
| id | dd | c1 | c2 |
+------+------+------+------+
| 1 | aaa | NULL | NULL |
| 2 | bbb | NULL | NULL |
| 3 | NULL | NULL | NULL |
| 4 | 8 | NULL | NULL |
| 5 | NULL | NULL | NULL |
+------+------+------+------+
5 rows in set (0.01 sec)
Great[test]>
另外在测试过程中,还发现 \e 可以对上一次执行的 SQL 进行编辑,即单独执行 \e 时,其实是对上次执行的 SQL 命令做编辑。
Great[test]> \e
select * from test.tt1
这里有人就会想到,我按向上的方向键,也能编辑上一条 SQL 呀,何必这么麻烦,这里有下面的 SQL 情况,有多次换行,或者结构更复杂的 SQL
Great[test]> select * from test.z1
-> join test.z2
-> using(id)
-> limit 3;
+------+------+------+
| id | name | name |
+------+------+------+
| 1 | 11 | 11 |
| 2 | 22 | 22 |
| 11 | 11 | 11 |
+------+------+------+
3 rows in set (0.00 sec)
如果按上的方向键,整个 SQL 语句结构就发生了变化,本来规整的 SQL 语句,变成了很长的一行,很难调整,但是使用 \e 就不会有这种情况,会保持之前的输入结构,更方便语句的调整
Great[test]> select * from test.z1
-> join test.z2
-> using(id)
-> limit 3;
+------+------+------+
| id | name | name |
+------+------+------+
| 1 | 11 | 11 |
| 2 | 22 | 22 |
| 11 | 11 | 11 |
+------+------+------+
3 rows in set (0.00 sec)
Great[test]> \e
select * from test.z1
join test.z2
using(id)
limit 3
另外,有同学经常苦恼,我昨天才输入的 SQL 命令,如果需要重新执行,又得输入一遍,其实这里也有一个小技巧,通过快捷键 CTRL+R,输入 SQL 中的关键信息 (比如表名字),就能快速翻出对应的 SQL,如果匹配出来的行不是想要的 SQL,可以继续按 CTRL+R 继续上翻,直到查找到需要的 SQL,当然,也有可能需要的 SQL 已经被清理出历史记录中,这种情况是无法被找到的。
ego(\G)
提交 SQL 语句到服务器,并且将返回的数据列式显示。
exit(\q)
退出 mysql 客户端连接。
go(\g)
提交 SQL 语句到服务器。
pager(\P)
设置 pager 规则,对查询结果执行 pager 规则后,再输出结果。这也是一个非常有用的设置,我们常常因为 processlist 结果太多而不方便查看,需要通过单行 grep 或者查询 information_schema.processlist 来实现,有这个工具后,我们看看效果
Great[test]> show processlist;
+---------+-----------------+--------------------+------+---------+---------+--------------------------------------------------------+----------------------------------+------------+-----------+---------------+
| Id | User | Host | db | Command | Time | State | Info | Time_ms | Rows_sent | Rows_examined |
+---------+-----------------+--------------------+------+---------+---------+--------------------------------------------------------+----------------------------------+------------+-----------+---------------+
| 6 | event_scheduler | localhost | NULL | Daemon | 5019248 | Waiting on empty queue | NULL | 5019247326 | 0 | 0 |
| 26 | system user | | NULL | Connect | 5018577 | waiting for handler commit | Group replication applier module | 5018576436 | 0 | 0 |
| 29 | system user | | NULL | Query | 1010 | Slave has read all relay log; waiting for more updates | NULL | 1010045 | 0 | 0 |
| 30 | system user | | NULL | Query | 1010 | Waiting for an event from Coordinator | NULL | 1010045 | 0 | 0 |
| 31 | system user | | NULL | Query | 100958 | Waiting for an event from Coordinator | NULL | 100956966 | 0 | 0 |
| 32 | system user | | NULL | Query | 100958 | Waiting for an event from Coordinator | NULL | 100956966 | 0 | 0 |
| 33 | system user | | NULL | Connect | 5018577 | Waiting for an event from Coordinator | NULL | 5018576419 | 0 | 0 |
| 34 | system user | | NULL | Connect | 5018577 | Waiting for an event from Coordinator | NULL | 5018576418 | 0 | 0 |
| 35 | system user | | NULL | Connect | 5018577 | Waiting for an event from Coordinator | NULL | 5018576417 | 0 | 0 |
| 36 | system user | | NULL | Connect | 5018577 | Waiting for an event from Coordinator | NULL | 5018576415 | 0 | 0 |
| 37 | system user | | NULL | Connect | 5018577 | Waiting for an event from Coordinator | NULL | 5018576413 | 0 | 0 |
+---------+-----------------+--------------------+------+---------+---------+--------------------------------------------------------+----------------------------------+------------+-----------+---------------+
14 rows in set (0.00 sec)
Great[test]> \P grep -vE 'system user|Sleep'
PAGER set to 'grep -vE 'system user|Sleep''
Great[test]> show processlist;
+---------+-----------------+--------------------+------+---------+---------+--------------------------------------------------------+----------------------------------+------------+-----------+---------------+
| Id | User | Host | db | Command | Time | State | Info | Time_ms | Rows_sent | Rows_examined |
+---------+-----------------+--------------------+------+---------+---------+--------------------------------------------------------+----------------------------------+------------+-----------+---------------+
| 6 | event_scheduler | localhost | NULL | Daemon | 5019255 | Waiting on empty queue | NULL | 5019255045 | 0 | 0 |
| 2460607 | Great | 172.16.50.81:59062 | test | Query | 0 | init | show processlist | 0 | 0 | 0 |
+---------+-----------------+--------------------+------+---------+---------+--------------------------------------------------------+----------------------------------+------------+-----------+---------------+
14 rows in set (0.01 sec)
Great[test]>
pager 后面可以跟很多的 shell 命令,比如 awk、grep、wc ,对结果集的处理,当 SQL 不方便处理而 shell 方便处理时,不用再使用 - e 参数每次都进行连接,然后处理,比如主从结构对 Slave_IO_Running、Slave_SQL_Running 的监控,可设置 pager grep -E 'Slave_IO_Running|Slave_SQL_Running'。
pager 的另外一个用途:经常有同学问,一些字段中是否有包含某些特定字符串,正常的 SQL 处理是需要写成 col1 like '% abc%' or col2 like '% abc%'...,写出来后 SQL 结构相对复杂,通过 pager 设置 pager grep -i abc; select * from tab 即可方便查看。
nopager(\n)
pager 的设置是整个 session 生命周期内都生效,通过执行 nopager 进行关闭设置
prompt(\R)
修改 mysql 客户端的命令行提示信息,支持显示的提示信息非常多,具体可以参见 MySQL 官网介绍。修改 mysql 客户端的命令行提示信息,也有好几种方式:
1、设置操作系统环境变量 MYSQL_PS1 export MYSQL_PS1= " \D_\h_\p_\u > ",格式为 "时间_主机_端口_用户名"
2、通过客户端的命令 prompt 修改 \R \D_\h_\p_\u >
3、通过 my.cnf 的 [mysql] 域进行配置
[mysql]
prompt="\R \D_\h_\p_\u > "
4、如果 session 中通过 \R xxx 临时修改了命令行提示信息,可以通过单独执行 \R 来恢复默认设置。
quit(\q)
退出当前 session 连接。
auto-rehash(#)
在使用 mysql 客户端连接服务器时,默认情况下会自动收集一些元数据信息,在后续输入 SQL 命令时可以通过 tab 键补齐命令,比如补齐表名字、列名字。
Great[test]> select * from tt(此时按tab键)
tt1 tt1.c1 tt1.c2 tt1.dd tt1.id ttt ttt.id
但是如果在 session 中新建了表,或者给表上新增了字段,是无法通过 tab 键补齐的,这时通过 \#命令刷新元数据信息,之后就能对新的 DDL 结构进行补齐
Great[test]> create table tt2 as select * from tt1;
Query OK, 5 rows affected (0.09 sec)
Records: 5 Duplicates: 0 Warnings: 0
Great[test]> select * from tt(此时按tab键)
tt1 tt1.c1 tt1.c2 tt1.dd tt1.id ttt ttt.id
Great[test]> \#
Great[test]> select * from tt(此时按tab键)
tt1 tt1.c1 tt1.c2 tt1.dd tt1.id tt2 tt2.c1 tt2.c2 tt2.dd tt2.id ttt ttt.id
Great[test]> select * from tt
source(.)
命令后需要跟一个文件名,\. filename 会对 filename 文件中的内容按标准 SQL 进行解析执行。
status(\s)
输出本次连接的相关信息及服务器的一些信息,如果连接时指定了 --safe-updates,还会输出查询限制相关的信息,\s 输出了很多有用的信息,可仔细阅读。
[#8#root@Great81 ~ 20:26:13]8 m5 3306 --safe-updates
greatsql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 916197
********
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
Great[(none)]> \s
--------------
********
Connection id: 916197
Current database:
Current user: Great@127.0.0.1
SSL: Cipher in use is ECDHE-RSA-AES128-GCM-SHA256
Current pager: stdout
Using outfile: ''
Using delimiter: ;
**********
Protocol version: 10
Connection: 127.0.0.1 via TCP/IP
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: utf8mb4
Conn. characterset: utf8mb4
TCP port: 3306
Binary data as: Hexadecimal
Uptime: 51 days 9 hours 5 min 13 sec
Threads: 16 Questions: 4354604 Slow queries: 339 Opens: 19188 Flush tables: 3 Open tables: 6095 Queries per second avg: 0.980
Note that you are running in safe_update_mode:
UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.
(One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)
SELECT has an automatic 'LIMIT 1000' if LIMIT is not used.
Max number of examined row combination in a join is set to: 1000000
--------------
Great[(none)]>Great
system(!)
使用默认命令解释器执行给定命令,简单的说就是返回到操作系统执行 \! 之后的命令,比如下面
Great[(none)]> \! date
2022年 11月 16日 星期三 20:32:34 CST
Great[(none)]> \! pwd
/root
Great[(none)]> \! cd /Great
Great[(none)]> \! vmstat -w
procs -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 358508 0 3117236 0 0 486 20 0 0 5 3 92 0 0
Great[(none)]>
在 8.0.19 之前,只支持在 unix 系统中使用该命令,到 8.0.19 后,在 windows 中也支持了该命令。
tee(\T)
将所有执行的 SQL 命令及输出结果保存到指定文件中。这在调测、生产维护过程中,都是非常有用的一个功能,特别是一些安全要求高的环境中,控制台只能显示几十行命令时,想要查找之前执行的命令及执行的结果比较难,此时就能用上 \T 了。
Great[(none)]> \T /root/a.log
Logging to file '/root/a.log'
Great[(none)]> select * from test.tt1;
+------+------+------+------+
| id | dd | c1 | c2 |
+------+------+------+------+
| 1 | aaa | NULL | NULL |
| 2 | bbb | NULL | NULL |
| 3 | NULL | NULL | NULL |
| 4 | 8 | NULL | NULL |
| 5 | NULL | NULL | NULL |
+------+------+------+------+
5 rows in set (0.01 sec)
Great[(none)]> \! cat /root/a.log
Great[(none)]> select * from test.tt1;
+------+------+------+------+
| id | dd | c1 | c2 |
+------+------+------+------+
| 1 | aaa | NULL | NULL |
| 2 | bbb | NULL | NULL |
| 3 | NULL | NULL | NULL |
| 4 | 8 | NULL | NULL |
| 5 | NULL | NULL | NULL |
+------+------+------+------+
5 rows in set (0.01 sec)
Great[(none)]>
如果想要记录每个人登录数据库,做了哪些操作,由于 tee 不支持根据当前时间动态产生日志文件名,我们可以这样设置
## 首先创建一个log目录
mkdir -p /Great/logs/client/
## 然后设置环境变量,为了连接安全,建议使用--login-path的方式进行登录,我这里使用的是gdb1登录。
echo "alias mlogin='mysql --login-path=gdb1 --tee /Great/logs/client/\$(date +%Y-%m-%d_%H-%M-%S).log'" >> ~/.bashrc; source ~/.bashrc
[#15#root@Great81 /Great/logs/client 20:48:53]15 echo "alias mlogin='mysql --login-path=gdb1 --tee /Great/logs/client/$(date +\"%Y-%m-%d_%H-%M\").log'" >> ~/.bashrc; source ~/.bashrc
## 通过设置的alias登录数据库
[#16#root@Great81 /Great/logs/client 20:49:43]16 mlogin
Logging to file '/Great/logs/client/2022-11-16_20-49.log'
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 916482
Great
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
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.
mysql> show tables from tpcc;
+------------------+
| Tables_in_tpcc |
+------------------+
| bmsql_config |
| bmsql_customer |
| bmsql_district |
| bmsql_history |
| bmsql_item |
| bmsql_new_order |
| bmsql_oorder |
| bmsql_order_line |
| bmsql_stock |
| bmsql_warehouse |
| datatypes1 |
| datatypes10 |
| datatypes11 |
| datatypes2 |
| datatypes3 |
| datatypes4 |
| datatypes5 |
| datatypes6 |
| datatypes7 |
| datatypes8 |
| datatypes9 |
+------------------+
21 rows in set (0.00 sec)
mysql>
我们发现指定的日志目录中,已经有了日志文件,并且记录了所有执行的 SQL 及输出结果
[#9#root@Great81 ~ 20:51:43]9 cd /Great/logs/client/
[#10#root@Great81 /Great/logs/client 20:51:48]10 ll
总用量 8
-rw-r--r-- 1 root root 627 11月 16 20:48 2022-11-16_20-48.log
-rw-r--r-- 1 root root 3214 11月 16 20:50 2022-11-16_20-49.log
[#11#root@Great81 /Great/logs/client 20:51:48]11
notee(\t)
取消 \T 设置,不再记录操作信息到日志文件中。
use(\u)
切换当前连接的 database。
warnings(\W)
在执行完 SQL 语句后,立即显示 warning 信息,不需要再手动执行 show warnings; 了。
nowarnings(\w)
在执行完 SQL 语句后,不立即显示 warning 信息,需要手动执行 show warnings; 才会显示 warning 信息。
resetconnection(\x)
以新连接的状态重新连接到服务器,并且进行一些信息的清理及复位,不需要再次进行权限验证。主要影响如下信息:
- 回滚所有活动的事务,并重置自动提交模式。
- 所有 DML 锁均已释放。
- 所有 TEMPORARY table 均已关闭 (并删除)。
- 会话系统变量将重新初始化。
- 用户变量设置丢失。
- 准备好的语句被释放。
- HANDLER 关闭。
- LAST_INSERT_ID 置为 0。
- 用 GET_LOCK 释放。
可以参考 8.0.26 的代码 libmysql.cc 中 4429~4444 行
int STDCALL mysql_reset_connection(MYSQL *mysql) {
DBUG_TRACE;
if (simple_command(mysql, COM_RESET_CONNECTION, nullptr, 0, 0))
return 1;
else {
mysql_detach_stmt_list(&mysql->stmts, "mysql_reset_connection");
/* reset some of the members in mysql */
mysql->insert_id = 0;
mysql->affected_rows = ~(uint64_t)0;
free_old_query(mysql);
mysql->status = MYSQL_STATUS_READY;
mysql_extension_bind_free(MYSQL_EXTENSION_PTR(mysql));
return 0;
}
}
query_attributes
通过 query_attributes var1 value1 var2 value2 来设置变量,然后通过 mysql_query_attribute_string('var1') 返回变量值 value1,目前来看,并没有发现特殊的使用方法,毕竟我们也可以通过 set @var1='value1' 来设置。
使用体验
mysql Client Commands 在某些场景下,可以极大的提高我们的操作效率,相对于完整的命令 ego、pager、edit 等,我更喜欢使用他们的简写命令 \G、\P、\e,因为有时候完整命令不确定如何正确使用,会导致不生效,但是简写命令,是一定会生效的。
参考文章
Enjoy GreatSQL :)
关于 GreatSQL
GreatSQL 是由万里数据库维护的 MySQL 分支,专注于提升 MGR 可靠性及性能,支持 InnoDB 并行查询特性,是适用于金融级应用的 MySQL 分支版本。