MySQL Connector .NET配合Entity Framework 实现只读数据库集群

最近有一公司项目,数据库用的是mysql,数据访问用的是entity framework 6。由于主库的读压力大了,我们弄了两个只读从库,指望着将部分读压力分担到这两个只读从库上。那么现在的问题是,怎么将读数据库的请求引导到这两个只读的数据库上,且做到均匀分布,而且万一其中一台挂了,所以有请求将自动引导到没挂的那一台上。

在网上搜了不少方案,直到看到这篇文章,才发现一直在用的MySQL Connector .NET已经能支持这些需求了,赶紧用起来:

1,在Web.config中加入只读集群的配置:

1   <configSections>
2     <section name="MySQL" type="MySql.Data.MySqlClient.MySqlConfiguration, MySql.Data" />
3   </configSections>
 1 <MySQL>
 2     <Replication>
 3       <ServerGroups>
 4         <Group name="ReadOnlyDBCluster">
 5           <Servers>
 6             <Server name="server1" IsMaster="true" connectionstring="server=server1;port=3306;uid=root;password=123456;"/>
 7             <Server name="server2" IsMaster="true" connectionstring="server=server2;port=3306;uid=root;password=123456;"/>
 8           </Servers>
 9         </Group>
10       </ServerGroups>
11     </Replication>
12   </MySQL>

上面的“connectionstring”必须全都小写。注意集群里的连接字符串都没有指定shcema,这是因为我们已经做了分库,具体连哪个schema是程序传入的。

2,在程序里使用“server=ReadOnlyDBCluster;database=x;”的连接字符串去连接只读集群。x是由程序指定的。(IsMaster是用于做读写分离的,因为不会有写的请求发送到只读集群,这个设定也没有什么用)。

满心欢喜开始调试,结果从dbContext里取数据的时候,报“No database selected”的错:

 

但是如果把schema指定放在集群的每个连接字符串上时,错误就不出现,但这不符合我的业务场景:必须把schema设定放在集群的连接字符串里。难道还有什么配置?

没有文档,直接翻MySQL Connector NET的源代码。经过一番调试,发现一次dbContext的读取,中间要触发多次取新的数据库连接的过程(ReplicationManager.GetNewConnection()),下面这3个调用栈都会出现在一次dbContext的读取中:

>	MySql.Data.dll!MySql.Data.MySqlClient.Replication.ReplicationManager.GetNewConnection(string groupName, bool master, MySql.Data.MySqlClient.MySqlConnection connection)
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(System.Data.CommandBehavior behavior)
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlCommand.ExecuteReader()
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlCommand.ExecuteScalar()
 	MySql.Data.dll!MySql.Data.MySqlClient.Driver.GetTimeZoneOffset(MySql.Data.MySqlClient.MySqlConnection con)
 	MySql.Data.dll!MySql.Data.MySqlClient.Driver.LoadServerProperties(MySql.Data.MySqlClient.MySqlConnection connection)
 	MySql.Data.dll!MySql.Data.MySqlClient.Driver.Configure(MySql.Data.MySqlClient.MySqlConnection connection)
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlConnection.Open()
>	MySql.Data.dll!MySql.Data.MySqlClient.Replication.ReplicationManager.GetNewConnection(string groupName, bool master, MySql.Data.MySqlClient.MySqlConnection connection)
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(System.Data.CommandBehavior behavior)
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlCommand.ExecuteReader()
 	MySql.Data.dll!MySql.Data.MySqlClient.Driver.LoadCharacterSets(MySql.Data.MySqlClient.MySqlConnection connection)
 	MySql.Data.dll!MySql.Data.MySqlClient.Driver.Configure(MySql.Data.MySqlClient.MySqlConnection connection)
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlConnection.Open()
>	MySql.Data.dll!MySql.Data.MySqlClient.Replication.ReplicationManager.GetNewConnection(string groupName, bool master, MySql.Data.MySqlClient.MySqlConnection connection)
 	MySql.Data.dll!MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(System.Data.CommandBehavior behavior)
 	MySql.Data.Entity.EF6.dll!MySql.Data.Entity.EFMySqlCommand.ExecuteDbDataReader(System.Data.CommandBehavior behavior)

所以,当程序运行到真正的数据库查询时(第三个调用栈),数据库连接已经切换了好几次,而且每次切换都没有选择schema,所以才会报“No database selected”。这不是配置的问题,是代码逻辑的问题。

 

解法也很简单,在获得新的数据库连接之后,做真正的数据库查询之前,选择集群连接字符串上指定的schema(如果有的话)。

代码改动位于command.cs, 方法public new MySqlDataReader ExecuteReader(CommandBehavior behavior):

问题得解,结束。

posted @ 2016-08-29 14:45  KennethYip  阅读(1017)  评论(0)    收藏  举报