惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
Cyberwarzone
Cyberwarzone
L
LINUX DO - 最新话题
N
News and Events Feed by Topic
T
Troy Hunt's Blog
Help Net Security
Help Net Security
S
Security @ Cisco Blogs
Google DeepMind News
Google DeepMind News
Security Archives - TechRepublic
Security Archives - TechRepublic
M
MIT News - Artificial intelligence
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V2EX - 技术
V2EX - 技术
Y
Y Combinator Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
大猫的无限游戏
大猫的无限游戏
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Security Blog
Microsoft Security Blog
Cisco Talos Blog
Cisco Talos Blog
T
Threatpost
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
SegmentFault 最新的问题
I
InfoQ
H
Hacker News: Front Page
D
Docker
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Blog — PlanetScale
Blog — PlanetScale
人人都是产品经理
人人都是产品经理
博客园 - 叶小钗
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
Netflix TechBlog - Medium
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
博客园 - 【当耐特】
T
Tor Project blog
U
Unit 42
H
Heimdal Security Blog
Microsoft Azure Blog
Microsoft Azure Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
P
Privacy & Cybersecurity Law Blog
PCI Perspectives
PCI Perspectives
美团技术团队
O
OpenAI News
T
Tailwind CSS Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog
GbyAI
GbyAI
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
MyScale Blog
MyScale Blog

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角
基于 EF 的数据库主从复制、读写分离实现 - 元视角
飞鸿踏雪 · 2018-10-18 · via 元视角

  各位朋友,大家好,欢迎大家关注我的博客,我是 Psyne,我的博客地址是https://blog.yuanpei.me。在上一篇博客中,我们提到了通过 DbCommandInterceptor 来实现 EF 中 SQL 针对 SQL 的“日志”功能。我们注意到,在这个拦截器中,我们可以获得当前数据库的上下文,可以获得 SQL 语句中的参数,更一般地,它具备“AOP”特性的扩展能力,可以在执行 SQL 的前后插入相应的动作,这就有点类似数据库中触发器的概念了。今天,我们主要来说一说,基于 EF 实现数据库主从复制和读写分离,希望这个内容对大家有所帮助。

主从复制 & 读写分离

  首先,我们先来了解一个概念:主从复制。那么,什么是主从复制呢?通常,在只有一个数据库的情况下,这个数据库会被称为主数据库。所以,当有多个数据库存在的时候,数据库之间就会有主从之分,而那些和主数据库完全一样的数据库就被称为从数据库,所以,主从复制其实就是指建立一个和主库完全一样的数据库环境

  那么,我们为什么需要主从复制这种设计呢?我们知道,主数据库一般用来存储实时的业务数据,因此如果主数据库服务器发生故障,从数据库可以继续提供数据服务,这就是主从复制的优势之一,即作为数据提供灾备能力。其次,从业务扩展性上来讲,互联网应用的业务增长速度普遍较高,随着业务量越来越大,I/O 的访问频率越来越高,在单机磁盘无法满足性能要求的情况下,通过设置多个从数据库服务器,可以降低磁盘的 I/O 访问频率,进而提高单机磁盘的读写性能。从业务场景上来讲,数据库的性能瓶颈主要在读即查询上,因此将读和写分离,能够让数据库支持更大的并发,这对优化前端用户体验很有意义

  通常来讲,不同的数据库都在数据库层面上实现了主从复制,各自的实现细节上可能会存在差异,譬如 SQLServer 中可以通过“发布订阅”来配置主从复制的策略,而 Oracle 中可以通过 DataGurd 来实现主从复制,甚至你可以直接把主库 Dump 出来再导入到从库。博主没有能力详细地向大家介绍它们的相关细节,可博主相信“万变不离其宗”的道理,这里我们以 MySQL 为例,因为它在互联网应用中更为普遍,虽然坑会相应地多一点:)……

  MySQL 中有一种最为重要的日志 binlog,即二进制日志,它记录了所有的 DDL 和 DML(除查询以外)语句,通过这些日志,不仅可以作为灾备时的数据恢复,同样可以传递给从数据库来达到数据一致的目的。具体来讲,对于每一个主从复制的连接,都有三个线程,即拥有多个从库的主库为每一个从库创建的binlog 输出线程,从库自身的IO 线程SQL 线程

  • 当从库连接到主库时,主库就会创建一个线程然后把 binlog 发送到从库,这是 binlog 输出线程。
  • 当从库执行 START SLAVER 以后,从库会创建一个 I/O 线程,该线程连接到主库并请求主库发送 binlog 里面的更新记录到从库上。从库 I/O 线程读取主库的 binlog 输出线程发送的更新并拷贝这些更新到本地文件(其中包括 relay log 文件)。
  • 从库创建一个 SQL 线程,这个线程读取从库 I/O 线程写到 relay log 的更新事件并执行。

EF 中主从复制的实现

  虽然从数据库层面上做主从复制会更简单一点,可在很多时候,这些东西其实更贴近 DBA 的工作,而且不同数据库在操作流程上还都不一样,搞这种东西注定不能成为“通用”的知识领悟。对开发人员来说,EF 和 Dapper 这样的 ORM 更友好一点,如果可以在 ORM 层面上做触发器和存储过程,可能 SQL 看起来就没有那么讨厌了吧!博主的公司因为要兼顾主流的数据库,所以,不可能在数据库层面上去做主从复制,最终我们是通过 EF 来实现主从复制。

  其实,讲了这么多主从复制的原理,对我们来说,这篇文章的实现则是非常简单的。因为通过 DbCommandInterceptor 我们能拦截到 SQL 命令,所以,只要是 Select 命令全部走从库,Insert/Update/Delete 全部走主库,这样就实现了读写分离。怎么样,是不是感觉相当简单啊!当然,前提是要准备好主从库的屋里环境,这些就让 DBA 去折腾吧(逃。好了,下面一起来看具体代码,首先我们定义一个主从库管理类 MasterSlaveManager:

public static class MasterSlaveManager
{
    private static MasterSalveConfig _config => LoadConfig();

    /// <summary>
    /// 加载主从配置
    /// </summary>
    /// <param name="fileName">配置文件</param>
    /// <returns></returns>
    public static MasterSalveConfig LoadConfig(string fileName = "masterslave.config.json")
    {
        if (!File.Exists(fileName)) throw new Exception(string.Format("配置文件{0}不存在", fileName));
        return JsonConvert.DeserializeObject<MasterSalveConfig>(File.ReadAllText(fileName));
    }

    /// <summary>
    /// 切换到主库
    /// </summary>
    /// <param name="command">DbCommand</param>
    public static void SwitchToMaster(DbCommand command, string serverName = "")
    {
        var masterServer = string.IsNullOrEmpty(serverName) ? 
            _config.Masters.FirstOrDefault() : _config.Masters.FirstOrDefault(e => e.ServerName == serverName);
        if (masterServer == null) throw new Exception("未配置主库服务器或者服务器名称不正确");
        //切换数据库连接
        ChangeDbConnection(command, masterServer);
    }

    /// <summary>
    /// 切换到从库
    /// </summary>
    /// <param name="command">DbCommand</param>
    public static void SwitchToSlave(DbCommand command, string serverName = "")
    {
        var salveServer = string.IsNullOrEmpty(serverName) ?
             _config.Slaves.FirstOrDefault() : _config.Slaves.FirstOrDefault(e => e.ServerName == serverName);
        if (salveServer == null) throw new Exception("未配置从库服务器或者服务器名称不正确");
        //切换数据库连接
        ChangeDbConnection(command, salveServer);
    }

    /// <summary>
    /// 切换数据库连接
    /// </summary>
    /// <param name="command"></param>
    /// <param name="dbServer"></param>
    private static void ChangeDbConnection(DbCommand command, DbServer dbServer)
    {
        var conn = command.Connection;
        if (conn.State == System.Data.ConnectionState.Open) conn.Close();
        conn.ConnectionString = dbServer.ConnectionString;
        conn.Open();
    }
}

接下来,和之前关于 EF 中的 SQL 拦截器类似,我们定义一个名为 MasterSlaveDbInterceptor 的拦截器:

public class MasterSlaveDbInterceptor : DbCommandInterceptor
{
    public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        //Insert/Update(写操作)走主库
        MasterSlaveManager.SwitchToMaster(command);
        base.NonQueryExecuting(command, interceptionContext);
    }

     public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        //Select(读操作)走从库
        var sqlText = command.CommandText;
        if (!sqlText.ToUpper().StartsWith("INSERT") || !sqlText.ToUpper().StartsWith("UPDATE"))
            MasterSlaveManager.SwitchToSlave(command);
        base.ScalarExecuting(command, interceptionContext);
    }

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        //Select(读操作)走从库
        var sqlText = command.CommandText;
        if (!sqlText.ToUpper().StartsWith("INSERT") || !sqlText.ToUpper().StartsWith("UPDATE"))
            MasterSlaveManager.SwitchToSlave(command);
        base.ReaderExecuting(command, interceptionContext);
    }
}

至此,我们就实现了基于 EF 的数据库主从复制、读写分离。其实,更严谨的说法是,主从复制是在数据层面上完成的,而读写分离则是在代码层面上完成。当然,实际应用中需要考虑事务、数据库连接等因素,这里我们仅仅提供一种思路。这里我们的配置文件中,对主、从数据库进行了简单配置,即一主一从。在实际应用中,可能我们会遇到一注多从的情况,在这个基础上,我们又可以延申出新的话题,譬如在存在多个从库的情况下,通过心跳检测来检查从库服务器的健康状态,以及如何为不同的从库服务器设置权重,实现多个从库服务器的负载均衡等等。我们在微服务中提出的**“健康检查”“负载均衡”**等概念,其实都可以映射到这里来,我想这是真正值得我们去深入研究的地方。

本文小结

  并没有,いじょう