如今,我们操作数据库一般用ORM框架
现在用.NET Core + EFCore + SqlServer 实现数据读写分离
介绍
为什么要读写分离?
降低数据库服务器的压力
如何实现读写分离?
1.一个主库多个从库
2.配置主库复制数据到从库
为什么一个主库多个从库?
一般查询多于增删改,这就是我们常说的二八原则,20%操作是增删改,80%操作是查询
是否有缺点?
有延迟
如何解决延迟问题?
比较及时性的数据还是通过主库查询
具体如何实现?
通过发布服务器,主库发布,而从库订阅,从而实现主从库
实现
SqlServer 实现
使用SqlServer 2019,新建一个主库,创建表,再通过本地发布创建发布,
然后通过本地订阅订阅主库,创建两个从库
.NET Core MVC项目实现
项目结构
首先,在appsettings.json配置数据库连接字符串
- {
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- },
- "AllowedHosts": "*",
- "ConnectionStrings": {
- "EFCoreTestToRead": "Server=GREAMBWANG-DC\\MSSQLSERVER2019;Database=EFCoreTestToRead01;Trusted_Connection=True;,Server=GREAMBWANG-DC\\MSSQLSERVER2019;Database=EFCoreTestToRead02;Trusted_Connection=True;",
- "EFCoreTestToWrite": "Server=GREAMBWANG-DC\\MSSQLSERVER2019;Database=EFCoreTest;Trusted_Connection=True;"
- }
- }
Models层实现
创建模型
- public class UserInfo
- {
- [Key]
- public int Id { get; set; }
- public string Name { get; set; }
- public int Age { get; set; }
- }
创建上下文
- public class EFCoreContext : DbContext
- {
- public EFCoreContext(string connectionString)
- {
- ConnectionString = connectionString;
- //创建数据库
- //Database.EnsureCreated();
- }
- private string ConnectionString { get; }
- public DbSet<UserInfo> UserInfo { get; set; }
- /// <summary>
- /// 配置连接数据库
- /// </summary>
- /// <param name="optionsBuilder"></param>
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- {
- //base.OnConfiguring(optionsBuilder);
- optionsBuilder.UseSqlServer(ConnectionString);
- }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- //base.OnModelCreating(modelBuilder);
- //初始化数据
- modelBuilder.Entity<UserInfo>().HasData(new List<UserInfo>()
- {
- new UserInfo() { Id = 1, Name = "哈哈", Age = 17 },
- new UserInfo() { Id = 2, Name = "呵呵", Age = 18 },
- new UserInfo() { Id = 3, Name = "嘻嘻", Age = 19 }
- });
- }
- }
创建上下文工厂
读写枚举
- public enum WriteAndReadEnum
- {
- Write,
- Read
- }
接口
- public interface IDbContextFactory
- {
- EFCoreContext CreateContext(WriteAndReadEnum writeAndRead);
- }
实现
在实现数据查询上,可以使用不同的策略,一般有随机策略,权重策略,轮询策略
随机策略:随机选择一个从库进行查询
权重策略:根据权重比例选择从库查询
轮询策略:根据顺序选择从库查询
- public class DbContextFactory : IDbContextFactory
- {
- private IConfiguration Configuration { get; }
- private string[] ReadConnectionStrings;
- public DbContextFactory(IConfiguration configuration)
- {
- Configuration = configuration;
- ReadConnectionStrings = Configuration.GetConnectionString("EFCoreTestToRead").Split(",");
- }
- public EFCoreContext CreateContext(WriteAndReadEnum writeAndRead)
- {
- string connectionString = string.Empty;
- switch (writeAndRead)
- {
- case WriteAndReadEnum.Write:
- connectionString = Configuration.GetConnectionString("EFCoreTestToWrite");
- break;
- case WriteAndReadEnum.Read:
- connectionString = GetReadConnectionString();
- break;
- default:
- break;
- }
- return new EFCoreContext(connectionString);
- }
- private string GetReadConnectionString()
- {
- /*
- * 随机策略
- * 权重策略
- * 轮询策略
- */
- //随机策略
- string connectionString = ReadConnectionStrings[new Random().Next(0, ReadConnectionStrings.Length)];
- return connectionString;
- }
- }
Models层完成
在Web层中
在Startup的ConfigureServices方法添加依赖注入
services.AddScoped<IDbContextFactory, DbContextFactory>();
操作
- public class HomeController : Controller
- {
- private readonly ILogger<HomeController> _logger;
- public IDbContextFactory DbContextFactory { get; }
- public HomeController(ILogger<HomeController> logger,IDbContextFactory dbContextFactory)
- {
- _logger = logger;
- DbContextFactory = dbContextFactory;
- }
- public IActionResult Index()
- {
- //写入操作
- EFCoreContext writeContext = DbContextFactory.CreateContext(WriteAndReadEnum.Write);
- writeContext.UserInfo.Add(new UserInfo() { Name = "AA", Age = 20 });
- writeContext.SaveChanges();
- //查询操作
- EFCoreContext readContext = DbContextFactory.CreateContext(WriteAndReadEnum.Read);
- UserInfo userInfo = readContext.UserInfo.OrderByDescending(u => u.Id).FirstOrDefault();
- return View();
- }
- }