Mysql主/从复制。即使读取查询也连接到主? (驱动程序在去奴隶之前“ping”主人吗?)

Posted

技术标签:

【中文标题】Mysql主/从复制。即使读取查询也连接到主? (驱动程序在去奴隶之前“ping”主人吗?)【英文标题】:Mysql master/slave replication .Connect to master even for read queries? (does Driver "ping" master before going to slave?) 【发布时间】:2014-04-25 02:23:10 【问题描述】:

我在 ReplicationDriver 中使用 mysql 主/从复制(写入主从读取)。我的连接 URL 如下:

"jdbc:mysql:replication://master:3306,slave1:3307,slave2:3308/sampledb?allowMasterDownConnections=true"  

我使用 Spring + Spring MyBatis 模块。

我已将我的交易标记为只读,如下所示:

@Override
    @Transactional(rollbackFor=Exception.class,readOnly=true)
    public Sample getSample(SampleKey sampleKey) throws SampleException 
       //Call MyBastis based DAO  with "select" queries.
    

但是当我看到事务/数据库日志时,它表明即使是“只读”事务,ReplicationDriver 也会首先命中主控。 注意“Acquired Connection”和“Releaving JDBC connection”行。

为什么会这样?

1) 不管它是只读查询,JDBC驱动是否仍然“ping”主服务器以检查它是否处于活动状态,然后转到从服务器进行实际查询?

2) 如果 readOnly=true ,Spring 不会设置底层 Connection 对象的 readOnly(true) 吗?

2014-03-19 12:32:28,280 DEBUG [http-8080-2] [AbstractPlatformTransactionManager.java:365] - Creating new transaction with name [com.rakuten.gep.foo.businesslogic.impl.SampleBusinessLogicImpl.getSample]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; '',-java.lang.Exception
2014-03-19 12:32:28,390 DEBUG [http-8080-2] [DataSourceTransactionManager.java:204] - Acquired Connection [jdbc:mysql://master:3306/, UserName=root@10.174.10.72, MySQL Connector Java] for JDBC transaction
CACHED DAO
Trying to retrive from the Cache
2014-03-19 12:32:31,334 DEBUG [http-8080-2] [Slf4jImpl.java:47] - ooo Using Connection [jdbc:mysql://slave1:3307/, UserName=root@10.174.10.72, MySQL Connector Java]
2014-03-19 12:32:31,334 DEBUG [http-8080-2] [Slf4jImpl.java:47] - ==>  Preparing: select tbl.item_id, tbl.item_name, tbl.create_time, tbl.update_time from sample_tbl tbl where tbl.item_id=? 
2014-03-19 12:32:31,335 DEBUG [http-8080-2] [Slf4jImpl.java:47] - ==> Parameters: 79bc3c80-af0a-11e3-a8e4-b8e8560f9d02(String)
Adding SampleTbl id to cache : 79bc3c80-af0a-11e3-a8e4-b8e8560f9d02
2014-03-19 12:32:31,340 DEBUG [http-8080-2] [AbstractPlatformTransactionManager.java:752] - Initiating transaction commit
2014-03-19 12:32:31,342 DEBUG [http-8080-2] [DataSourceTransactionManager.java:264] - Committing JDBC transaction on Connection [jdbc:mysql://slave1:3307/, UserName=root@10.174.10.72, MySQL Connector Java]
2014-03-19 12:32:31,382 DEBUG [http-8080-2] [DataSourceTransactionManager.java:322] - Releasing JDBC Connection [jdbc:mysql://master:3306/, UserName=root@10.174.10.72, MySQL Connector Java] after transaction

我的连接设置是:

<Context>
  <WatchedResource>WEB-INF/web.xml</WatchedResource>
  <Resource name="jdbc/sample"
            auth="Container"
            type="javax.sql.DataSource" 
            factory="org.apache.commons.dbcp.BasicDataSourceFactory"     
            username="root"
            password="root"
            driverClassName="com.mysql.jdbc.ReplicationDriver"
            url="jdbc:mysql:replication://master:3306,slave1:3307,slave2:3308/sampledb?allowMasterDownConnections=true"       
            connectionCachingEnabled="true"
            connectionCacheProperties="MaxStatementsLimit=10"
            removeAbandoned="true"
            removeAbandonedTimeout="600"
            logAbandoned="true"            
            timeBetweenEvictionRunsMillis="1000"
            minEvictableIdleTimeMillis="1000"
            testOnBorrow="false"
            testOnReturn="false"
            validationQuery="select null"
            testWhileIdle="true"
            maxActive="10"
            maxIdle="3"
            maxWait="1000"
            defaultAutoCommit="false"/>

</Context>

【问题讨论】:

【参考方案1】:

您所看到的是 MySQL JDBC 驱动程序正在管理与物理服务器(无论是主服务器还是从服务器)的连接这一事实的副作用。连接池和 Spring 事务管理器都不知道数据库连接正在与多个服务器通信这一事实。一切看起来都正常工作,但我会解释为什么它看起来像是在使用主连接。

首先,DBCP 通过 MySQL 驱动程序创建单个 JDBC 连接。此连接将指向主设备,直到设置为只读,此时它将切换到从设备。 其次,Spring 正在从池中获取连接并将其写入已获取连接的调试日志。由于连接尚未设置为只读模式,因此会将查询路由到主节点。 第三,Spring 将连接更改为只读模式,此时查询将被路由到从站。 接下来,为您的应用程序(或 iBatis 或 w/e)提供连接以对数据库执行一些工作。 将控制权交还给 Spring 后,连接上的事务将被提交。由于连接处于只读模式,您可以看到事务调试消息显示查询将被路由到从属服务器。 最后,连接在返回池之前被重置。只读模式被清除,最后一条日志消息再次反映连接会将查询路由到主服务器。

希望这会有所帮助。如果您需要更详细的信息,请告诉我。

【讨论】:

感谢详细的回答。我的问题是,当我停止'master'时,读取查询需要太多时间,因为“连接主机”和“释放主机”这两个操作耗时太长.我尝试减少context.xml中的connectionTimeouts,但仍然需要太多时间。 问题在于 MySQL 复制驱动程序将在每次连接返回到池时尝试重新连接到主服务器。清除只读模式时会发生这种情况。对于这种情况,我能看到的最简单的解决方法是为主服务器和从服务器使用单独的数据源(和事务管理器)。您可以在注释中指定(以及只读)数据源/事务管理器的名称,例如: @Transactional("master")

以上是关于Mysql主/从复制。即使读取查询也连接到主? (驱动程序在去奴隶之前“ping”主人吗?)的主要内容,如果未能解决你的问题,请参考以下文章

Mysql主从复制的实现细节

MySQL主从复制性能优化

详解MySQL读写分离

Mysql主从复制方式以及可能出现的问题

MYSQL的读写分离主从延时问题

MySQL主从复制