一个mysql jdbc待解之谜 关于jdbc url参数 allowMultiQuerieshtml
以下的一个普通JDBC示例:java
String user ="root"; String password = "root"; String url = "jdbc:mysql://localhost:3306"; Connection conn = java.sql.DriverManager.getConnection(url , user, password); Statement stmt = conn.createStatement(); String sql = "select 'hello';select 'world'"; stmt.execute(sql);
执行时会报错:mysql
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 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 'world'' at line 1 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
若一个sql中经过分号分割(或包含)了多个独立sql的话,如:sql
select 'hello';select 'world'
默认就会报上述错误,当若显式设置allowMultiQueries为true的话,就能够正常执行不会报错.以下所示:shell
String url = "jdbc:mysql://localhost:3306?allowMultiQueries=true";
官方文档解释:ubuntu
allowMultiQueries Allow the use of ';' to delimit multiple queries during one statement (true/false), defaults to 'false', and does not affect the addBatch() and executeBatch() methods, which instead rely on rewriteBatchStatements. Default: false Since version: 3.1.1
想要看看当该参数设置为true和false时,源码中到底哪里有不一样.通过断点调试发现了在下面代码处会有不一样的结果:服务器
//所属类和方法:void java.net.SocketOutputStream.socketWrite(byte[] b, int off, int len) throws IOException socketWrite0(fd, b, off, len); bytesWritten = len;
当设置为true时,执行完socketWrite0方法后查询query log,会看到这样的输出:socket
23 Query select 'hello'; 23 Query select 'world'
当设置为false时,执行完socketWrite0后.查询query log没有任何输出.工具
但奇怪的是它们的参数都同样,为何一个有输出一个就没有呢?以下所示:ui
设置为true参数信息
设置为false时的参数信息:
补充: 即便当allowMultiQueries为false时,服务端也收到了查询请求,只不过没有在query log中输出而已.
以下抓包工具(Wireshark)所示:
又通过进一步调试,发现以下几处代码可能比较关键,以下所示:
/** * * 所属: void com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(String user, String * password, String database, Buffer challenge) throws SQLException */ // We allow the user to configure whether // or not they want to support multiple queries // (by default, this is disabled). if (this.connection.getAllowMultiQueries()) { //1701行 this.clientParam |= CLIENT_MULTI_STATEMENTS; } last_sent = new Buffer(packLength); last_sent.writeLong(this.clientParam); //1876行
执行完上述语句后,对应的query log为:
150507 21:23:16 19 Connect root@localhost on
彷佛是在这一交互过程当中通知了服务器端客户端容许一次查询多条语句.经过抓包工具(wireshark)能够看到在建立链接过程当中更多的交互信息,以下所示:
但在调试过程当中又遇到了一个新问题,若在上述代码处加上了断点,就会出现以下异常:
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost. at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3161) at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3615) ... 43 more
还没有找到该问题缘由(如在何处设置的超时时间).可是实验发现,在建立链接过程当中不能有debug调试(即不能有暂停),不然就会有fin包(不知这是Mysql的协议仍是TCP的协议?).以下所示:
关于fin包的描述见wiki
但在成功创建链接后进行断点调试没有问题.
补充: 经过telnet来模拟上述现象.
# telnet 127.0.0.1 3306 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. J 5.6.17 <4/-7j1S- � 18fwG:-nh=66mysql_native_passwordConnection closed by foreign host.
对应的抓包信息:
因未能及时输入密码,致使服务端发出FIN包中断了链接.
或者也能够经过以下Java代码来模拟此现象:
Socket socket = new Socket("127.0.0.1",3306); System.in.read();
此时看到的抓包信息以下所示:
补充:
1. 如何查看query log:
mysql> show variables like 'general_log%'; +------------------+--------------------------------------------------------------------+ | Variable_name | Value | +------------------+--------------------------------------------------------------------+ | general_log | OFF | | general_log_file | /opt/mysql/server-5.6/data/zhuguowei-Presario-CQ43-Notebook-PC.log | +------------------+--------------------------------------------------------------------+ mysql> set global general_log = 'on' ; #开启另外一终端 tail -f /opt/mysql/server-5.6/data/zhuguowei-Presario-CQ43-Notebook-PC.log
2. 关于使用抓包工具(Wireshark)
参考文档:
http://www.maketecheasier.com/using-wireshark-ubuntu/
http://floss.zoomquiet.io/data/20120511105248/index.html
注意:
本地调试的话Interface(网卡)选择any(或lo), 过滤条件以下所示:
其余问题:
1. 为何在mysql交互终端中执行的命令不能被Wireshark捕捉到?
须要显式指定-h参数 如
mysql -u root -p -h 127.0.0.1
注意若为-h localhost的话,仍不能被Wireshark捕捉到,不知二者有什么区别?经过StackOverFlow中提问获得解答.
On Unix, MySQL programs treat the host name localhost specially, in a way that is likely different from what you expect compared to other network-based programs. For connections to localhost, MySQL programs attempt to connect to the local server by using a Unix socket file. This occurs even if a --port or -P option is given to specify a port number. To ensure that the client makes a TCP/IP connection to the local server, use --host or -h to specify a host name value of 127.0.0.1, or the IP address or name of the local server. You can also specify the connection protocol explicitly, even for localhost, by using the --protocol=TCP option.
摘自: https://dev.mysql.com/doc/refman/5.0/en/connecting.html
2. 在mysql交互终端中是否默认设置了allowMultiQueries=true, 能有办法将其改成false吗?
确实默认设置了allowMultiQueries=true, 以下所示:
上图是经过Wireshark抓取终端mysql登陆时(mysql -u root -p -h 127.0.0.1)抓取的包.
彷佛没有修改的办法.
3. 如何在telent 127.0.0.1 3306后输入密码? 或者如何设置使得不用输入密码,试过使用--skip-grant-tables 启动mysql服务,但不起做用.