回到目录,完整代码请查看(https://github.com/cjw0511/NDF.Infrastructure)中的目录:
src\ NDF.Data.EntityFramework\MasterSlaves

上一回中(http://www.cnblogs.com/cjw0511/p/4398267.html),我们简单讲述了基于 EF 来实现数据库读写分离的原理。当然,这只是一个 demo 级别的简单实现,实际上,在我们工作环境中,碰到的情况远比这复杂多了,例如数据库连接的配置是通过 config 文件来存储、在进行数据库操作时还需要附带很多事务操作功能等等。今天我们就来聊聊如何处理这些问题。
首先,我们来解决数据库连接字符串存储与配置文件的问题
代码如下:
 1  public class DbMasterSlaveCommandInterceptor : DbCommandInterceptor
 2     {
 3         private Lazy<string> masterConnectionString = new Lazy<string>(() => ConfigurationManager.AppSettings["masterConnectionString"]);
 4         private Lazy<string> slaveConnectionString = new Lazy<string>(() => ConfigurationManager.AppSettings["slaveConnectionString"]);
 5
 6         public string MasterConnectionString
 7         {
 8             get { return this.masterConnectionString.Value; }
 9         }
10
11         public string SlaveConnectionString
12         {
13             get { return this.slaveConnectionString.Value; }
14         }
15
16
17         public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
18         {
19             this.UpdateConnectionStringIfNeed(interceptionContext, this.SlaveConnectionString);
20         }
21
22         public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
23         {
24             this.UpdateConnectionStringIfNeed(interceptionContext, this.SlaveConnectionString);
25         }
26
27         public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
28         {
29             this.UpdateConnectionStringIfNeed(interceptionContext, this.MasterConnectionString);
30         }
31
32
33         private void UpdateConnectionStringIfNeed(DbInterceptionContext interceptionContext, string connectionString)
34         {
35             foreach (var context in interceptionContext.DbContexts)
36             {
37                 this.UpdateConnectionStringIfNeed(context.Database.Connection, connectionString);
38             }
39         }
40
41         /// <summary>
42         /// 此处改进了对连接字符串的修改判断机制,确认只在 <paramref name="conn"/> 所使用的连接字符串不等效于 <paramref name="connectionString"/> 的情况下才需要修改。
43         /// </summary>
44         /// <param name="conn"></param>
45         /// <param name="connectionString"></param>
46         private void UpdateConnectionStringIfNeed(DbConnection conn, string connectionString)
47         {
48             if (this.ConnectionStringCompare(conn, connectionString))
49             {
50                 ConnectionState state = conn.State;
51                 if (state == ConnectionState.Open)
52                     conn.Close();
53
54                 conn.ConnectionString = connectionString;
55
56                 if (state == ConnectionState.Open)
57                     conn.Open();
58             }
59         }
60
61         private bool ConnectionStringCompare(DbConnection conn, string connectionString)
62         {
63             DbProviderFactory factory = DbProviderFactories.GetFactory(conn);
64
65             DbConnectionStringBuilder a = factory.CreateConnectionStringBuilder();
66             a.ConnectionString = conn.ConnectionString;
67
68             DbConnectionStringBuilder b = factory.CreateConnectionStringBuilder();
69             b.ConnectionString = connectionString;
70
71             return a.EquivalentTo(b);
72         }
73     }

再者,我们来聊聊数据库操作中的事务处理。

我们都知道,数据库操作中的事务处理重要包括两大类:
1、普通数据库操作事务处理,该类型由 DbTransaction 事务基类来控制;
2、分布式事务,这类操作主要由组件 System.Transactions 来控制,最常用的类型包括 Transaction 和 TransactionScope。
具体涉及到普通数据库事务和分布式事务的意义和区别、普通事务如何会提升为分布式事务等知识点,这里就不赘述了,有兴趣的同学可以另行补课。
这里需要说明的是,在数据库的事务操作中,很多 dbms 是不支持同一个事务操作不同的数据库或服务器的。另外某些 dbms 支持同一个事务操作多个数据库或服务器(自动提升为分布式事务),但是需要 msdtc 的支持。
所以在这里,我改进的方案是,凡是所有的事务操作,不管是普通数据库事务,还是分布式事务,都“禁用”读写分离,即将所有的在事务内的数据库操作(不管是读还是写,虽然这一定程度上不符合“完全的读写分离”的本意,但是解决了数据库事务兼容性的问题,而且大多数项目开发中,包含事务的操作不占多数),都指向 Master 服务器。实际上基于我们前面对数据库服务器连接字符串的封装,要实现这一点,只需要改动少量代码,如下:
 1 public class DbMasterSlaveCommandInterceptor : DbCommandInterceptor
 2     {
 3         private Lazy<string> masterConnectionString = new Lazy<string>(() => ConfigurationManager.AppSettings["masterConnectionString"]);
 4         private Lazy<string> slaveConnectionString = new Lazy<string>(() => ConfigurationManager.AppSettings["slaveConnectionString"]);
 5
 6         public string MasterConnectionString
 7         {
 8             get { return this.masterConnectionString.Value; }
 9         }
10
11         public string SlaveConnectionString
12         {
13             get { return this.slaveConnectionString.Value; }
14         }
15
16
17         public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
18         {
19             this.UpdateToSlave(interceptionContext);
20         }
21
22         public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
23         {
24             this.UpdateToSlave(interceptionContext);
25         }
26
27         public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
28         {
29             this.UpdateToMaster(interceptionContext);
30         }
31
32
33         private void UpdateToMaster(DbInterceptionContext interceptionContext)
34         {
35             foreach (var context in interceptionContext.DbContexts)
36             {
37                 this.UpdateConnectionStringIfNeed(context.Database.Connection, this.MasterConnectionString);
38             }
39         }
40
41         private void UpdateToSlave(DbInterceptionContext interceptionContext)
42         {
43             // 判断当前会话是否处于分布式事务中
44             bool isDistributedTran = Transaction.Current != null && Transaction.Current.TransactionInformation.Status != TransactionStatus.Committed;
45             foreach (var context in interceptionContext.DbContexts)
46             {
47                 // 判断该 context 是否处于普通数据库事务中
48                 bool isDbTran = context.Database.CurrentTransaction != null;
49
50                 // 如果处于分布式事务或普通事务中,则“禁用”读写分离,处于事务中的所有读写操作都指向 Master
51                 string connectionString = isDistributedTran || isDbTran ? this.MasterConnectionString : this.SlaveConnectionString;
52
53                 this.UpdateConnectionStringIfNeed(context.Database.Connection, connectionString);
54             }
55         }
56
57
58         /// <summary>
59         /// 此处改进了对连接字符串的修改判断机制,确认只在 <paramref name="conn"/> 所使用的连接字符串不等效于 <paramref name="connectionString"/> 的情况下才需要修改。
60         /// <para>同时,在必要的情况下才会连接进行 Open 和 Close 操作以及修改 ConnectionString 处理,减少了性能的消耗。</para>
61         /// </summary>
62         /// <param name="conn"></param>
63         /// <param name="connectionString"></param>
64         private void UpdateConnectionStringIfNeed(DbConnection conn, string connectionString)
65         {
66             if (this.ConnectionStringCompare(conn, connectionString))
67             {
68                 this.UpdateConnectionString(conn, connectionString);
69             }
70         }
71
72         private void UpdateConnectionString(DbConnection conn, string connectionString)
73         {
74             ConnectionState state = conn.State;
75             if (state == ConnectionState.Open)
76                 conn.Close();
77
78             conn.ConnectionString = connectionString;
79
80             if (state == ConnectionState.Open)
81                 conn.Open();
82         }
83
84         private bool ConnectionStringCompare(DbConnection conn, string connectionString)
85         {
86             DbProviderFactory factory = DbProviderFactories.GetFactory(conn);
87
88             DbConnectionStringBuilder a = factory.CreateConnectionStringBuilder();
89             a.ConnectionString = conn.ConnectionString;
90
91             DbConnectionStringBuilder b = factory.CreateConnectionStringBuilder();
92             b.ConnectionString = connectionString;
93
94             return a.EquivalentTo(b);
95         }
96     }

关于上面的代码,需要说明的一点是,因为要获取 EF DbContext 的普通数据库事务状态,必须得拿到 DbContext.Database.CurrentTransaction 属性,所以将 UpdateConnectionString 方法拆分成 UpdateToMaster 和 UpdateToSlave 了。

转载于:https://www.cnblogs.com/cjw0511/p/4398502.html

基于 EntityFramework 的数据库主从读写分离架构(2)- 改进配置和添加事务支持...相关推荐

  1. 基于 EntityFramework 的数据库主从读写分离架构(1) - 原理概述和基本功能实现...

    回到目录,完整代码请查看(https://github.com/cjw0511/NDF.Infrastructure)中的目录: src\ NDF.Data.EntityFramework\Maste ...

  2. ef mysql 读写分离_基于 EntityFramework 的数据库主从读写分离服务插件

    基于 EntityFramework 的数据库主从读写分离服务插件 1. 版本信息和源码 1.1版本信息 v1.01 beta(2015-04-07),基于 EF 6.1 开发,支持 EF 6.1 之 ...

  3. django给mysql配主从_django中的mysql主从读写分离:一、配置mysql主从分离

    一.配置mysql主从同步的步骤: (1) 在主服务器上,必须开启二进制日志机制和配置一个独立的ID (2) 在每一个从服务器上,配置一个唯一的ID,创建一个用来专门复制主服务器数据的账号 (3) 在 ...

  4. Django项目配置mysql主从数据库实现读写分离

    1.在配置文件中添加slave数据库的配置 DATABASES = {     'default': {         'ENGINE': 'django.db.backends.mysql',   ...

  5. 数据库之架构:主备+分库?主从+读写分离?

    数据库之架构:主备+分库?主从+读写分离? 原文:数据库之架构:主备+分库?主从+读写分离? 一.数据库架构原则 高可用 高性能 一致性 扩展性 二.常见的架构方案 方案一:主备架构,只有主库提供读写 ...

  6. 数据库读写分离架构详解

    RD:数据量太大,数据库扛不住了,帮忙申请一个从库,读写分离. DBA:数据量多少? RD:5000w左右. DBA:读写吞吐量呢? RD:读QPS约200,写QPS约30左右. 额,数据库读写分离虽 ...

  7. mysql查询并设置高亮_Thinkphp3.2.3设置MySql主从读写分离后,简单调用主数据库查询

    图/文:迷神 Thinkphp是一款不错的国产框架,使用范围广,应用也比较多.随着网站访问增大往往需要使用mysql主从同步功能,本身Thinkphp自带了主从读写分离的功能了. 但是我们经常有一个场 ...

  8. MySQL数据库:读写分离

    一.读写分离的原理: 1.实现原理: 读写分离解决的是,数据库的写操作,影响了查询的效率,适用于读远大于写的场景.读写分离的实现基础是主从复制,主数据库利用主从复制将自身数据的改变同步到从数据库集群中 ...

  9. 基于mycat的mysql_基于Mycat中间件的MySQL读写分离

    基于Mycat中间件的MySQL读写分离 简述 mycat是国内开源的数据库中间件,可以实现mysql读写分离和主备热切换,容灾,数据分片等功能. 详情:http://www.mycat.io/ 架构 ...

最新文章

  1. 【bzoj2423】最长公共子序列[HAOI2010](dp)
  2. Spring---------ThreadLocal(线程变量副本)
  3. php多文件上传存储到表,PHP 实现一种多文件上传的方法
  4. BugkuCTF-MISC题粗心的佳佳
  5. 音频放大电路_低音升压功率放大器电子电路的完整设计
  6. Python 基础 —— from __future__ import
  7. Vue基本指令(详细,好理解,示例代码)
  8. android接支付宝授权和支付功能
  9. 模拟生成随机四位GB2312的汉字验证码(python3.X)
  10. 人人都喜欢用的十大python标准库
  11. windows 7 RC(7106.0.090408)下载另附windows7驱动收集整合(5月31日更新)
  12. C++“准”标准库Boost学习指南(3):Boost.Utility
  13. Unity+Kinect 开发脚本介绍
  14. 【老九学堂】【初识C语言】编译过程
  15. 时统ptp_IEEE1588对时系统,PTP校时模块,PTP时钟服务器
  16. 计算机查看配置的快捷键,剪映电脑版快捷键在哪里设置? 剪映查看快捷键的技巧...
  17. html 仿word页面,HTML+CSS入门 HTML页面仿WORD样式详解
  18. 全球名校AI课程库(1)| 深度学习专项课程『Deep Learning Specialization』
  19. 微信内置浏览器无法打开APP下载链接的解决方案
  20. 研发管理--测试绩效管理

热门文章

  1. Ant 条件判断 if
  2. Python实现tab文件操作
  3. DocRepair 3.0
  4. Win XP远程桌面双管理员同时登录
  5. 已知bug列表——Solidity中文文档(12)
  6. hibernate笔记(三) Hibernate标识符属性(主键)生成策略全析
  7. 企业云桌面-02-安装第2台域控制器和第2台DNS服务器-012-DC02
  8. php - 数组倒序
  9. 分页功能 (包含增删改查)工具类
  10. 解决方法:ORA-24324 未初始化服务句柄