ORM框架對分表分庫的實現

 

AntData ORM框架是我維護的一個開源ORM框架

//github.com/yuzd/AntData.ORM

該框架是在Linq2db這個優秀的linq轉sql引擎基礎上進行改造而來的。

分離了linq2sql引擎和db執行邏輯。

畫一個圖的話 應該是這樣的:

image

分表分庫功能設計

框架內置目前使用比較多的兩種分片模式:

  • 取模 (mod)
  • 範圍 (range)

為了讓框架更容易擴展

  • 開放分片策略的接口 當有自定義分片規則可以繼承實現

下面我用內置的 取模 分片模式來說明 ,運行環境: netcore平台

1. DB Sharding

db配置:

  • Provider 指定為 mysql
  • Name 是邏輯名稱 可以隨意指定
  • ShardingStrategy 指定分片的邏輯處理
  • ConnectionItemList 指定一個或多個db鏈接配置

image

注意:

class=AntData.DbEngine.Sharding.ModShardingStrategy;column=ID;mod=2;shardByDB=true

的意思是:

採用內置的取模分片法,走分片的字段叫ID mod為2 也就是1分為2個 分別是 (0 和 1),所以下面的ConnectionItemList指定的 Sharding分別是0和1的數據庫連接

按照上面的配置的話,當滿足

  • 表還有字段ID
  • db搜索的條件含有id字段,或者 db更新的條件含有id字段 或者db刪除的條件含有id字段

都應該走取模算法來走對應的db。

下面來測試它

DB Sharding 使用場景舉例說明


        /// <summary>
        /// 測試mod分庫插入到testorm2數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_01()
        {
            //id查詢 1 mod 2 = 1 所以會走到 testorm2數據庫
            var id = 1;
            var odIsExist = DB.Tables.Orders.Any(r => r.ID.Equals(1));
            if (odIsExist)
            {
                return;
            }
            var order = new Order
            {
                ID = 1,
                Name = "上海大學"
            };

            var result = DB.Insert(order);
            Assert.AreEqual(result, 1);

        }
        

        /// <summary>
        /// 測試mod分庫插入到testorm1數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_02()
        {

            var id = 2;
            var odIsExist = DB.Tables.Orders.Any(r => r.ID.Equals(2));
            if (odIsExist)
            {
                return;
            }
            var order = new Order
            {
                ID = 2,
                Name = "北京大學"
            };

            var result = DB.Insert(order);
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分庫 查詢testorm2數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_03()
        {
            var id = 1;
            var tb1 = DB.Tables.Orders.FirstOrDefault(r => r.ID.Equals(1));
            Assert.IsNotNull(tb1);
        }

        /// <summary>
        /// 測試mod分庫 查詢testorm1數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_04()
        {
            var id = 2;
            var tb1 = DB.Tables.Orders.FirstOrDefault(r => r.ID.Equals(2));
            Assert.IsNotNull(tb1);
        }

        /// <summary>
        /// 測試mod分庫 不指定sharing column 查詢疊加
        /// </summary>
        [TestMethod]
        public void TestMethod6_05()
        {
            var tb1 = DB.Tables.Orders.ToList();
            Assert.IsNotNull(tb1);
            Assert.AreEqual(tb1.Count, 2);

            var odIsExist = DB.Tables.Orders.Where(r => r.ID.Equals(1) || r.ID.Equals(2)).ToList();
            Assert.AreEqual(odIsExist.Count, 2);
        }

        /// <summary>
        /// 測試mod分庫修改到testorm2數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_06()
        {
            var id = 1;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(1)).Set(r => r.Name, y => y.Name + "1").Update();
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分庫修改到testorm1數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_07()
        {
            var id = 2;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(2)).Set(r => r.Name, y => y.Name + "1").Update();
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分庫刪除到testorm2數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_08()
        {
            var id = 1;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(1)).Delete();
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分庫刪除到testorm1數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod6_09()
        {
            var id = 2;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(2)).Delete();
            Assert.AreEqual(result, 1);
        }

        [TestMethod]
        public void TestMethod7_01()
        {
            var id = 2;

            //var odIsExist = DB.Tables.Orders.Any(r => r.ID.Equals(id));

            var odIsExist = DB.Tables.Orders.Any(r => r.ID.Equals(2));
            if (odIsExist)
            {
                return;
            }


        }

        /// <summary>
        /// 測試mod分庫批量分別插入到testorm1  testorm2數據庫
        /// </summary>
        [TestMethod]
        public void TestMethod7_02()
        {
            var orderList = new List<Order>();
            orderList.Add(new Order
            {
                ID = 3,
                Name = "上海大學"
            });
            orderList.Add(new Order
            {
                ID = 4,
                Name = "上海大學"
            });
            //沒有指定 shading column的話是默認分到第一個分片
            orderList.Add(new Order
            {
                ID = null,
                Name = "上海大學"
            });
            var rows = DB.BulkCopy(orderList);
            Assert.AreEqual(rows.RowsCopied, 3);
        }

        /// <summary>
        /// 不指定sharing column 刪除會疊加
        /// </summary>
        [TestMethod]
        public void TestMethod7_03()
        {
            var odIsExist = DB.Tables.Orders.Delete();
            Assert.AreEqual(odIsExist, 3);

        }


2. Table Sharding

  • Provider 指定為 mysql
  • Name 是邏輯名稱 可以隨意指定
  • ShardingStrategy 指定分片的邏輯處理
  • ConnectionItemList 指定一個db鏈接配置

image

針對分表要對 dbmodels 做一個小修改

image

class=AntData.DbEngine.Sharding.ModShardingStrategy;column=ID;mod=2;tableSharding=0,1;shardByDB=false;shardByTable=true

的意思是:

採用內置的取模分片法,走分片的字段叫ID mod為2 也就是1分為2個 分別是 (0 和 1),所以下面的ConnectionItemList指定的 Sharding分別是order_0和order_1的數據

按照上面的配置的話,當滿足

  • 表還有字段ID
  • db搜索的條件含有id字段,或者 db更新的條件含有id字段 或者db刪除的條件含有id字段

都應該走取模算法來走對應的table。

下面來測試它

Table Sharding 使用場景舉例說明


        /// <summary>
        /// 測試mod分表插入到testorm3數據庫的orders_1表裏面
        /// </summary>
        [TestMethod]
        public void TestMethod1_01()
        {
            var id = 1;
            var odIsExist = DB.Tables.Orders.Any(r => r.ID.Equals(id));
            if (odIsExist)
            {
                return;
            }

            var order = new Orders
            {
                ID = id,
                Name = "訂單1"
            };

            var result = DB.Insert(order);
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分表插入到testorm3數據庫的orders_0表裏面
        /// </summary>
        [TestMethod]
        public void TestMethod1_02()
        {
            var id = 2;
            var odIsExist = DB.Tables.Orders.Any(r => r.ID.Equals(id));
            if (odIsExist)
            {
                return;
            }
            var order = new Orders
            {
                ID = id,
                Name = "訂單2"
            };

            var result = DB.Insert(order);
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分表 查詢testorm3數據庫orders_1
        /// </summary>
        [TestMethod]
        public void TestMethod1_03()
        {
            var id = 1;
            var tb1 = DB.Tables.Orders.FirstOrDefault(r => r.ID.Equals(id));
            Assert.IsNotNull(tb1);
        }

        /// <summary>
        /// 測試mod分表 查詢testorm3數據庫orders_0
        /// </summary>
        [TestMethod]
        public void TestMethod1_04()
        {
            var id = 2;
            var tb1 = DB.Tables.Orders.FirstOrDefault(r => r.ID.Equals(id));
            Assert.IsNotNull(tb1);
        }


        /// <summary>
        /// 測試mod分表 不指定sharing column 查詢疊加
        /// </summary>
        [TestMethod]
        public void TestMethod1_05()
        {
            var tb1 = DB.Tables.Orders.ToList();
            Assert.IsNotNull(tb1);
            Assert.AreEqual(tb1.Count, 2);

            var odIsExist = DB.Tables.Orders.Where(r => r.ID.Equals(1) || r.ID.Equals(2)).ToList();
            Assert.AreEqual(odIsExist.Count, 2);
        }

        /// <summary>
        /// 測試mod分表修改到testorm3數據庫orders_1
        /// </summary>
        [TestMethod]
        public void TestMethod1_06()
        {
            var id = 1;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(id)).Set(r => r.Name, y => y.Name + "1").Update();
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分表修改到testorm3數據庫orders_0
        /// </summary>
        [TestMethod]
        public void TestMethod1_07()
        {
            var id = 2;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(id)).Set(r => r.Name, y => y.Name + "1").Update();
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分表刪除到testorm3數據庫orders_1
        /// </summary>
        [TestMethod]
        public void TestMethod6_08()
        {
            var id = 1;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(id)).Delete();
            Assert.AreEqual(result, 1);
        }

        /// <summary>
        /// 測試mod分表刪除到testorm3數據庫orders_0
        /// </summary>
        [TestMethod]
        public void TestMethod6_09()
        {
            var id = 2;
            var result = DB.Tables.Orders.Where(r => r.ID.Equals(id)).Delete();
            Assert.AreEqual(result, 1);
        }
        /// <summary>
        /// 測試mod分庫批量分別插入到testorm3數據庫orders_0 orders_1
        /// </summary>
        [TestMethod]
        public void TestMethod7_01()
        {

            var orderList = new List<Orders>();
            orderList.Add(new Orders
            {
                ID = 3,
                Name = "上海大學"
            });
            orderList.Add(new Orders
            {
                ID = 4,
                Name = "上海大學"
            });
            //沒有指定 shading column的話是默認分到第一個分片
            orderList.Add(new Orders
            {
                ID = null,
                Name = "上海大學"
            });
            var rows = DB.BulkCopy(orderList);
            Assert.AreEqual(rows.RowsCopied, 3);
        }

        /// <summary>
        ///  不指定sharding 字段會刪除所有子表
        /// </summary>
        [TestMethod]
        public void TestMethod7_03()
        {
            var odIsExist = DB.Tables.Orders.Delete();
            Assert.AreEqual(odIsExist, 3);

        }

總結

目前在AntData orm中使用分庫分表其實是根據查詢的字段來匹配取模或者區間來進行分片,然後只需要配置上進行修改即可。對於實際業務上orm的寫法是不需要變化的

Tags: