双重密码,MySQL 8.0创新特性
sql_mode兼容性,MySQL 8.0 升级踩过的坑
警惕参数变化,MySQL 8.0 升级避免再次踩坑
上一篇博客sql_mode兼容性,MySQL 8.0 升级踩过的坑,提到了MySQL 8.0版本中sql_mode默认值的变化,导致升级之后业务访问报错。我们知道MySQL 8.0相对于MySQL 5.7加入了很多新特性,在功能和安全性上做了很多的优化和调整,这就不可避免地会修改一些参数或者函数。我们现有业务数据从MySQL 5.7及之前的版本升级到8.0,需要特别警惕这些参数的变化,这些变化可能会导致业务行为发生一些意料之外的结果!!!
最近MySQL 8.0 升级的过程中,我梳理了一些业务可能用到的、重要的参数变化,包括一些MySQL 8.0中已经废弃的参数和部分常见参数默认值的变化。对于一些innodb底层的参数变化,业务并无感知,就不用单独列出讨论。
查询缓存功能,在8.0.3版本中被整体移除,相关参数随之全部移除。
tx_isolation 和 tx_read_only 在8.0.3中移除,使用参数transaction_isolation 和transaction_read_only 替代
expire_logs_days 设置binlog保留天数,从MySQL 8.0.11开始已经废弃,并把默认值修改为0,转而使用参数binlog_expire_logs_seconds替代。在MySQL 5.7中习惯使用expire_logs_days参数设置binlog保留时间的需要注意一下。
date_format 和 datetime_formate 和 time_format 时间格式化函数,在MySQL 8.0.3中被移除。注意业务程序中使用这几个函数,在8.0中很可能会报错。
sql_mode参数在MySQL 8.0中当然还是保留的,但是,要注意NO_AUTO_CREATE_USER 模式在8.0中被移除。
一套java程序的后端使用的数据库版本为MySQL 5.7。数据库升级到MySQL 8.0之后,业务代码连接数据库失败。报错信息为“java.sql.SQLException: Cannot create PoolableConnectionFactory (Could not create connection to database server.)”
get connetion from pool error:ConnectionInfo{jdbcUrl='jdbc:mysql://9.9.9.9:3306/db?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8', host='9.9.9.9', port='3306', dbName='db', userName='appuser', userPasswd='appuser'} for 3 times
[WARN] 2022-03-29 09:56:28,389 [4c927ecf695a46d9a78afafa309885c6] com.tenpay.risk.ditto.core.common.data.ExceptionWarper.warper(ExceptionWarper.java:71) --> Exception=============com.tenpay.risk.ditto.core.common.exception.DbException
com.tenpay.risk.ditto.core.common.exception.DbException: java.sql.SQLException: Cannot create PoolableConnectionFactory (Could not create connection to database server.)
我们第一反应,这是不是jdbc的MySQL连接驱动版本不兼容?
首先,查看JAVA程序使用的MySQL连接驱动版本为5.1.38。如图所示:
然后,网上查询了mysql-connecter-java的版本兼容性,发现Connector/J version 5.1是可以部分兼容MySQL 8.0的。
接着,我们对MySQL Connector/J version 5.1的小版本(从5.1.1到5.1.49)都进行了测试,发现确实是部分兼容的。其中,5.1.44 - 5.1.49版本是支持MySQL 8.0的。也就是说5.1.44之后的版本都是支持MySQL 8.0的,而5.1.44之前的版本只有部分小版本是兼容,大部分不兼容。
最后,我们查阅一下mysql-connector-java-5.1.44版本的release note,发现这个版本修复了一个重要bug。使用参数transaction_isolation 和transaction_read_only 替代 tx_isolation 和 tx_read_only 。(参考官方文档:https://dev.mysql.com/doc/relnotes/connector-j/5.1/en/news-5-1-44.html)
Important Change: Following the changes in MySQL Server 8.0.3, the system variables tx_isolation and tx_read_only have been replaced with transaction_isolation and transaction_read_only in the code of Connector/J. Users should update Connector/J to this latest release in order to connect to MySQL 8.0.3. They should also make the same adjustments to their own applications if they use the old variables in their codes. (Bug #26440544)
所以,解决java程序连接MySQL 8.0失败的方式,就是升级MySQL驱动版本到5.1.44以上版本,建议直接升级驱动到8.0版本。
MySQL 8.0中很多参数的默认值,相较于5.7来说发生了变化。
Option/Parameter | Old Default | New Default |
---|---|---|
character_set_server | latin1 | utf8mb4 |
collation_server | latin1_swedish_ci | utf8mb4_0900_ai_ci |
explicit_defaults_for_timestamp | OFF | ON |
optimizer_trace_max_mem_size | 16KB | 1MB |
validate_password_check_user_name | OFF | ON |
back_log | -1 (autosize) changed from : back_log = 50 + (max_connections / 5) | -1 (autosize) changed to : back_log = max_connections |
max_allowed_packet | 4194304 (4MB) | 67108864 (64MB) |
max_error_count | 64 | 1024 |
event_scheduler | OFF | ON |
table_open_cache | 2000 | 4000 |
log_error_verbosity | 3 (Notes) | 2 (Warning) |
innodb_undo_tablespaces | 0 | 2 |
innodb_undo_log_truncate | OFF | ON |
innodb_flush_method | NULL | fsync (Unix), unbuffered (Windows) |
innodb_autoinc_lock_mode | 1 (consecutive) | 2 (interleaved) |
innodb_flush_neighbors | 1 (enable) | 0 (disable) |
innodb_max_dirty_pages_pct_lwm | 0 (%) | 10 (%) |
innodb_max_dirty_pages_pct | 75 (%) | 90 (%) |
performance-schema-instrument=‘wait/lock/metadata/sql/%=ON’ | OFF | ON |
performance-schema-instrument=‘memory/%=COUNTED’ | OFF | COUNTED |
performance-schema-consumer-events-transactions-current=ON | OFF | ON |
performance-schema-consumer-events-transactions-history=ON | OFF | ON |
performance-schema-instrument=‘transaction%=ON’ | OFF | ON |
log_bin | OFF | ON |
server_id | 0 | 1 |
log-slave-updates | OFF | ON |
expire_logs_days | 0 | 30 |
master-info-repository | FILE | TABLE |
relay-log-info-repository | FILE | TABLE |
transaction-write-set-extraction | OFF | XXHASH64 |
slave_rows_search_algorithms | INDEX_SCAN, TABLE_SCAN | INDEX_SCAN, HASH_SCAN |
slave_pending_jobs_size_max | 16M | 128M |
gtid_executed_compression_period | 1000 | 0 |
group_replication_autorejoin_tries | 0 | 3 |
group_replication_exit_state_action | ABORT_SERVER | READ_ONLY |
group_replication_member_expel_timeout | 0 | 5 |
sql_mode='ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_ENGINE_SUBSTITUTION'
。数据库版本MySQL 5.7时,业务方一直使用LOAD DATA LOCAL INFILE的方式导入数据;升级到MySQL 8.0.23之后,反馈导数命令报错ERROR 1148 (42000)。
# 导数命令
MYSQL> LOAD DATA LOCAL INFILE 'test.csv' INTO TABLE sbtest1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' (id,name);
# 报错信息
ERROR 1148 (42000): The used command is not allowed with this MySQL version
LOAD DATA LOCAL INFILE导入数据的权限是由参数 local_infile 控制的。在MySQL 5.7中,这个参数的默认值是ON,即允许客户端使用LOAD DATA LOCAL INFILE方式导入数据;而在MySQL 8.0中出于安全考虑,将这个参数的默认值设置成了OFF,即默认是禁止使用LOAD DATA LOCAL INFILE方式导入数据。
这里的可选解决方案有两种:
在MySQL 8.0中显式设置local_infile=ON,保持与MySQL 5.7之前的默认配置一致;
业务侧使用其他方式导入数据,譬如在shell中使用管道导入文件,或者在MySQL 客户端使用source导入sql文件。
很明显,方案1对于解决问题更简单直接;但是我更推荐使用方案2,客户端使用load data local infile本身是存在安全风险的,一般情况下还是禁用为好。
MySQL 8.0增加了很多新的很有用的特性,相较于5.7的版本也有很多的变化。现有业务数据从MySQL 5.7及之前的版本升级到MySQL 8.0时,需要特别警惕一些参数的变化,这些变化可能会导致业务行为发生一些意料之外的结果。同时,规范数据库使用方式,也是避免踩坑中雷的有效方式。