前言:
其实在部分公司不是很推崇使用ORM框架,说是ORM框架存在性能问题;这句话可以说是让ORM蒙受不白之冤;笔者认为:没有低性能的框架,只有不会使用框架的开发者!更多时候,是因为数据库的性能问题;ORM在开发中可以说是被殃及的池鱼!那么本篇文章就介绍如何通过EFCore如何来配合数据库提高性能;那么如何来做的,得先从数据库下手,最常规,最推崇的就是数据库读写分离;那么如何使用EFCore配合使用数据库读写分离呢?
数据库读写分离
在实际开发工作中,对数据的操作有一个二八原则,20%的操作是数据的增删改,80%的操作是数据库查询,那么把这80%的数据存储在多个数据库中,有选择性的去查询数据;必然提高性能;数据库读写分离,其实是一主多从,主库负责,增删改(20%的数据操作),从库负责查询(80%的数据查询);架构图如下图
这里有三种角色,主库,发布服务器,从库;三者的关系为:主库发布数据库结构或者数据到发布服务器,从库从发布服务器订阅;主库第一次发布,发布数据库结构;是把数据的结构,通过镜像文件发布到发布服务器。多个从库去订阅,直接通过镜像拷贝把数据库结构拷贝生成从数据库;那么后面如果主数据库中有数据更新,新增、修改、删除数据以后,数据库会生成日志,那么主数据库生成日志后把操作日志,发布到服务器服务器,多个从库在发布服务器订阅得到日志,然后通过日志恢复数据;通过日志回复数据很快,但依然有延迟;只不过延迟很小!那么数据的扭转过程为:增删改操作主库,主库同步数据到从库,查询到从库查询,这就是读写分离。
EFCore连续多数据库操作数据
EFCore的使用,是基于上下文Context来对数据完成一一系列增删改查操作,Context里面包括了对数据的各种配置,包括数据库连接,数据库映射等。一个数据库对应一个Context;那么数据库读写分离以后,是多个数据库了;怎么办呢?有两种方案,多数据库多个Context; 还是每个数据库对应一个Context; 或者一个Context多个数据库连接;如图2,图3。
图2,多个Context对应多个数据库
图3 一个Context多个连接对应不同的数据库
那么这两种方案哪种更巧妙呢?我们来分析一下,如果是多个Context对应多个连接,就需要建立多个Context类文件;数据库读写分离后其实是无法确定从库的数量的,可能随着业务的增长,从数据库可能会持续增加,那么增加一个从数据库;就需要增加一个Context文件;届时就需要修改代码,那么项目就会不稳定,这是我们开发者最忌讳的;数据库读写分离在大型系统中本为常态,如果每增加一个从库,就需要修改一次代码的话,其实很难受,代价很高;所以多个Context单独对应数据库来说,并非技术实现最佳方案。
那再来看看一个Context多个连接数据库的方案:一个Context对应多个连接;如果增删改,就使用连接主库的数据库链接;如果是查询,就使用,从库中的任何一个;因为主从数据库的结构和数据是一种的;这种方案是可行的;那我们就来实操一下!
第一步:建立一个Core环境的类库;如图4
图4
第二步:
Nuget引入程序包:Microsoft.EntityFrameworkCore 支持EFCore;Nuget引入程序包:Microsoft.EntityFrameworkCore.SqlServer支持SqlServer数据库
第三步:建立Context上下文文件:如图5
图5
第四步:就可以在上端调用;主库写入,从库读取;可以正常执行;如图6
我们来分析一下如果代码这么写有没有什么问题。如果代码这样写,上端调用方,还得决定,究竟使用哪个数据库;判断使用的数据库连接字符串;在实际开发中,数据库连接字符串肯定是配置在配置文件中的;在Core开发中像类的实例创建一定必然是通过容器来创建的;如果有多个从库呢?上端还得选择使用从库中的那个字符串;程序开发中,这样做不好,上端调用方需要处理的事情太多,知道的也太多。
其实上端调用只需要明确是需要使用主库(增删改),还是从库(查询);那么这里最后能把实例的创建和字符串的选择处理推向一个工厂来做;工厂接受一个枚举参数,知道是使用从库还是主库,然后由工厂来读取配置文件指定究竟来使用哪个数据库来做数据操作;因为从数据库的个数是未知的,工厂还可以在选择从库(查询的)的时候,适当的做一些策略来选择,在查询的时候,让多个从库尽可能平均的来分摊查询的操作;从而提高数据库的性能。Ok~ 那我们就来实操一下!
工厂创建DbContext实例:
第一步:创建工厂DbContextFactory如图7,创建枚举如图8
图7
图8
第二步:调用测试 如图9
图9
比较一下:我们发现增加了工厂以后,发现在上端调用的时候,确实工厂能够创建不同Context实例,但是我们增加了创建工厂的成本;还是不够友好。进一步思考:在创建Context实例的时候,是必须要指定就是是使用主库还是从库;这个没办法,那么工厂的创建能不能不让我们自己来写呢?当然可以,可以把工厂注入到控制器里来,看我实操。
抽象工厂+依赖注入(基于MVC,一般都这样写)
第一步:定义工厂接口:定义一个创建Context的方法;如图10
图10
第二步:工厂实现工厂接口,完善代码;如图11
图11
第三步:注册服务,构造函数注入:如图12
第四步:控制器构造函数注入:如图13
图13
第五步:调试测试;如图14,把工厂注入到控制器,让工厂创建Context实例;数据正常查询;
图14
那这样做了以后有什么好处呢?这样做了之后,无论你有多少个数据库,只要是做的读写分离(数据库结构一致);都可以通过一个工厂创建出来,工厂在创建从库对应的Context实例的时候还可以做负载均衡;因为数据库的操作80%都是在从库中查询,如果从库多,我可以把这80%的查询分散到多个从库中去查询,从而提高数据库的性能;怎么实现呢?请往下看!
查询数据(从库)库负载均衡
请看到我们定义的从库创建实例部分代码,已经增加了选择查询数据库连接的策略;如15
其实我们还可以增加另外的一些策略:比方说轮询策略,如果有十个从库,那查询操作就轮流着俩,第一次使用从库1,第二次使用从库2 第三次使用从库3.以此类推;还可以增加配置文件,通过读取配置文件来决定就是使用哪一种策略;比方说就定义三种策略:随机、轮询、权重;实操如下
第一步:增加配置文件配置究竟选择哪种策略:
第二步:定义选择从库的策略
总结
EFCore通过和SQlServer的结合,不仅可以实现数据库的读写分离,而且可以做到在查询的时候,定义不同的策略来实现不同数据库的查询,更好的降低数据库的压力,提高数据库的性能。