iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 26
6
Everything on Azure

三十天.NET❤️Azure漸進式開發專案系列 第 26

三十天.NET與Azure漸進式開發專案(26): 資料庫讀寫分離,實作程式

2018-10-31.23.48.27-image.png

前兩篇文章介紹如何在Azure SQL Database做自動表格同步,接著使用C#實作讀寫分離程式。

這邊為了簡單示範以 .net core 加上簡易DbConnection SQLHelper舉例,假如有大量建立連線情況,請使用連線池

【第一步】在 appsettings.json 建立連線字串的資料

{
  "DBConnection": {
    "MasterConnectionString": "Data Source=Server連接;User ID=帳號;Password=密碼;Initial Catalog=主資料庫;Encrypt=true",
    "SlaveConnectionString": [
      "Data Source=Server連接;User ID=帳號;Password=密碼;Initial Catalog=附屬資料庫;Encrypt=true;",
     "Data Source=Server連接;User ID=帳號;Password=密碼;Initial Catalog=附屬資料庫;Encrypt=true;"
    ]
  }
}

【第二步】建立連線字串的Model類別,注意因為主資料庫只有一個,附屬資料庫可以有多個所以用List來盛裝,因為有count屬性可以使用,避免後面每次建立連線都運算一次。

public class DBConnection
{
    public string MasterConnectionString { get; set; }
    public List<string> SlaveConnectionString { get; set; }
}

【第三步】在Startup類別ConfigureServices方法將資料庫物件註冊到SQLHelper

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            var dBConnection = Configuration.GetSection("DBConnection");
            services.Configure<DBConnection>(dBConnection);
            SQLHelper.dBConnection = dBConnection.Get<DBConnection>();
        }
    }

【第四步】建立抽象類別SQLHelper,為了區分讀寫資料庫的連線建立,這邊建立兩個方法:

  1. CreateMasterConnection : 負責增刪改或者即時性查詢,使用主服務器的連接字符串。
  2. CreateSlaveConnection : 負責非即時的查詢像是報表查詢,使用查詢服務器的連接字符串

其中為了應付多查詢資料庫分配與效能,我使用時間的Ticks + Mod 附屬資料庫數量 來做平均分配。因為Ticks會隨著時間一點一點遞增,再配合mod得到不超過資料庫數量的特性,取得索引使用非常好用。

public abstract class SQLHelper
{
	public static DBConnection dBConnection;/*連線字串註冊在Startup.cs*/
	private static readonly System.Data.Common.DbProviderFactory dbProviderFactory = System.Data.SqlClient.SqlClientFactory.Instance;

	private static IDbConnection CreateConnectionByConnectionString(string connectionString)
	{
		var conn = dbProviderFactory.CreateConnection();
		conn.ConnectionString = connectionString;
		conn.Open();
		return conn;
	}
	public static IDbConnection CreateMasterConnection()
	{
		return CreateConnectionByConnectionString(dBConnection.MasterConnectionString);
	}

	public static IDbConnection CreateSlaveConnection()
	{
		//使用(時間Ticks + mod 附屬資料庫數量) 方式分配 slave
		var index = (int)(DateTime.Now.Ticks % (dBConnection.SlaveConnectionString.Count));
		var connectionString = dBConnection.SlaveConnectionString[index];
		return CreateConnectionByConnectionString(connectionString);
	}
}

接著做驗證查詢動作

	//模擬兩個時間點的查詢請求,分別使用不同的slave查詢資料庫
	var slaveDBCount = SQLHelper.dBConnection.SlaveConnectionString.Count;
	using (var conn = SQLHelper.CreateSlaveConnection())
	{
		var time = DateTime.Parse("2018/11/01 12:00:00.0000000").Ticks;
		var isDBAbleStatus = conn.Query<bool>("select top 1 1 ").SingleOrDefault();
		Console.WriteLine($"{time} 時間Mod結果為{time%slaveDBCount},使用index為{time%slaveDBCount}的SlaveDB,查詢結果{isDBAbleStatus}");
	}

	using (var conn = SQLHelper.CreateSlaveConnection())
	{
		var time = DateTime.Parse("2018/11/01 12:00:00.0000001").Ticks;
		var isDBAbleStatus = conn.Query<bool>("select top 1 1 ").SingleOrDefault();
		Console.WriteLine($"{time} 時間Mod結果為{time%slaveDBCount},使用index為{time%slaveDBCount}的SlaveDB,查詢結果{isDBAbleStatus}");
	}

2018-11-01.08.27.03-image.png


讀寫分離是專案已經到一定規模,需要將數據從主資料庫同步到其他附屬資料庫,提供非即時查詢的功能,藉此分散主資料庫負擔提高併發量。當主資料庫在更新資料時,不影響附屬資料庫的查詢,不會發生阻塞lock情況。

其中非即時性特點很重要,請不要把關鍵業務邏輯,使用Slave資料庫來完成,所以它不是銀彈,不能應用在所有情況,個人經驗Slave資料庫常拿來當報表資料庫使用。


上一篇
三十天.NET與Azure漸進式開發專案(25): 資料庫讀寫分離,多資料庫同步
下一篇
三十天.NET與Azure漸進式開發專案(27): 簡單達到動態負載平衡,抵擋惡意流量攻擊
系列文
三十天.NET❤️Azure漸進式開發專案30

尚未有邦友留言

立即登入留言