表分片
表分片通常也被稱為分表,散表。 當某張表的數據量很大時,sql執行效率都會變低,這時通常會把大表拆分成多個小表,以提高sql執行效率。 我們將這種大表拆分成多個小表的策略稱之為表分片。
先來看一段mango框架中表分片的代碼:
@DB(table = "t_order") @Sharding(tableShardingStrategy = TableShardingOrderDao.OrderTableShardingStrategy.class) public interface TableShardingOrderDao { @SQL("insert into #table(id, uid, price, status) values(:id, :uid, :price, :status)") public void addOrder(@TableShardingBy("uid") Order order); @SQL("select id, uid, price, status from #table where uid = :1") public List<Order> getOrdersByUid(@TableShardingBy int uid); class OrderTableShardingStrategy implements TableShardingStrategy<Integer> { @Override public String getTargetTable(String table, Integer shardingParameter) { return table + "_" + (shardingParameter % 2); } } }
上面的代碼實現了所有的表分片邏輯,以上面的代碼為例,總結一下mango框架實現表分片的3個步驟:
- 填寫@DB注解中的table參數,這個參數將啟用 全局表名,上面代碼的全局表名是t_order
- 引入 @Sharding 注解,並填寫@Sharding注解中的tableShardingStrategy參數,這個參數的作用是定義表分片策略,上面代碼使用了自定義的表分片策略OrderTableShardingStrategy
- 使用 @TableShardingBy 注解指定對表分片策略傳入的參數。上面的代碼中,調用 addOrder(@TableShardingBy("uid")Order order) 方法時,會使用order對象中的uid屬性作為參數傳遞給第2步中的表分片策略,而調用 getOrdersByUid(@TableShardingBy int uid) 方法時,會使用uid作為參數傳遞給第2步中的表分片策略
上面的3個步驟步中,最核心的是第2步中的表分片策略。mango框架使用@Sharding注解中的tableShardingStrategy參數來指定表分片策略,tableShardingStrategy參數接受任何實現了 TableShardingStrategy 接口的類。
我們來看一下TableShardingStrategy接口的定義:
public interface TableShardingStrategy<T> { public String getTargetTable(String table, T shardingParameter); }
TableShardingStrategy接口非常簡單,只有一個getTargetTable方法,其中:
- 輸入參數table,對應的是全局表名
- 輸入參數shardingParameter,接收被@TableShardingBy注解修飾的參數,shardingParameter的類型是泛型,將由實現類根據@TableShardingBy修飾的參數確定具體類型
- 輸出則為真正的表名
以上面的OrderTableShardingStrategy表分片策略為例:
- 輸入參數table將被傳入字符串”t_order”
- 輸入參數shardingParameter則會分兩種情況,在調用 addOrder(@TableShardingBy("uid") Order order) 方法時,shardingParameter會被傳入order對象中的uid屬性,而在調用 getOrdersByUid(@TableShardingBy int uid) 方法時,shardingParameter會被傳入參數uid
- 當uid為偶數時,使用t_order_0表,當uid為奇數時,使用t_order_1表
數據庫分片
數據庫分片通常也被稱為分庫,散庫等。 當我們在某個庫中,把某張大表拆分成多個小表后還不能滿足性能要求,這時我們需要把一部分拆分的表挪到另外一個庫中,以提高sql執行效率。
先來看一段mango框架中數據庫分片的代碼:
@DB() @Sharding(databaseShardingStrategy = DatabaseShardingOrderDao.OrderDatabaseShardingStrategy.class) public interface DatabaseShardingOrderDao { @SQL("insert into t_order(id, uid, price, status) values(:id, :uid, :price, :status)") public void addOrder(@DatabaseShardingBy("uid") Order order); @SQL("select id, uid, price, status from t_order where uid = :1") public List<Order> getOrdersByUid(@DatabaseShardingBy int uid); class OrderDatabaseShardingStrategy implements DatabaseShardingStrategy<Integer> { @Override public String getDatabase(Integer shardingParameter) { return shardingParameter < 1000 ? "db1" : "db2"; } } }
上面的代碼實現了所有的數據庫分片邏輯,以上面的代碼為例,總結一下mango框架實現數據庫分片的2個步驟:
- 引入 @Sharding 注解,並填寫@Sharding注解中的databaseShardingStrategy參數,這個參數的作用是定義數據庫分片策略,上面代碼使用了自定義的數據庫分片策略OrderDatabaseShardingStrategy
- 使用 @DatabaseShardingBy 注解指定對數據庫分片策略傳入的參數。上面的代碼中,調用 addOrder(@DatabaseShardingBy("uid") Order order) 方法時,會使用order對象中的uid屬性作為參數傳遞給第1步中的數據庫分片策略,而調用 getOrdersByUid(@DatabaseShardingBy int uid) 方法時,會使用uid作為參數傳遞給第1步中的數據庫分片策略
上面的2個步驟步中,最核心的是第1步中的數據庫分片策略。mango框架使用@Sharding注解中的databaseShardingStrategy參數來指定數據庫分片策略,databaseShardingStrategy參數接受任何實現了 DatabaseShardingStrategy 接口的類。
我們來看一下DatabaseShardingStrategy接口的定義:
public interface DatabaseShardingStrategy<T> { public String getDatabase(T shardingParameter); }
DatabaseShardingStrategy接口非常簡單,只有一個getDatabase方法,其中:
- 輸入參數shardingParameter,接收被@DatabaseShardingBy注解修飾的參數,shardingParameter的類型是泛型,將由實現類根據@DatabaseShardingBy修飾的參數確定具體類型
- 輸出則為database名稱
以上面的OrderDatabaseShardingStrategy數據庫分片策略為例:
- 輸入參數shardingParameter則會分兩種情況,在調用 addOrder(@DatabaseShardingBy("uid") Order order) 方法時,shardingParameter會被傳入order對象中的uid屬性,而在調用 getOrdersByUid(@DatabaseShardingBy int uid)方法時,shardingParameter會被傳入參數uid
- 當uid小於1000時,使用的database為db1,當uid大於等於1000時,使用的database為db2
同時使用數據庫分片與表分片
我們將上面的數據庫分片策略與表分片策略一起使用,形成同時使用數據庫分片與表分片的代碼:
@DB(table = "t_order") @Sharding( databaseShardingStrategy = ShardingOrderDao.OrderDatabaseShardingStrategy.class, tableShardingStrategy = ShardingOrderDao.OrderTableShardingStrategy.class ) public interface ShardingOrderDao { @SQL("insert into #table(id, uid, price, status) values(:id, :uid, :price, :status)") public void addOrder(@DatabaseShardingBy("uid") @TableShardingBy("uid") Order order); @SQL("select id, uid, price, status from #table where uid = :1") public List<Order> getOrdersByUid(@DatabaseShardingBy @TableShardingBy int uid); class OrderDatabaseShardingStrategy implements DatabaseShardingStrategy<Integer> { @Override public String getDatabase(Integer uid) { return uid < 1000 ? "db1" : "db2"; } } class OrderTableShardingStrategy implements TableShardingStrategy<Integer> { @Override public String getTargetTable(String table, Integer uid) { return table + "_" + (uid % 2); } } }
上面的代碼中,數據庫分片策略使用了OrderDatabaseShardingStrategy,即uid小於1000時使用的database為db1,uid大於等於1000時使用的database為db2。 表分片策略則使用了OrderTableShardingStrategy,即uid為偶數時使用t_order_0表,uid為奇數時使用t_order_1表。
組合數據庫分片策略與表分片策略得到如下規則:
- uid小於1000並且uid為偶數時,使用db1中的t_order_0表
- uid小於1000並且uid為奇數時,使用db1中的t_order_1表
- uid大於等於1000並且uid為偶數時,使用db2中的t_order_0表
- uid大於等於1000並且uid為奇數時,使用db2中的t_order_1表
精簡分片代碼
下面的代碼同樣實現了同時使用數據庫分片與表分片,不過更加簡潔。
@DB(table = "t_order") @Sharding(shardingStrategy = ShardingOrder2Dao.OrderShardingStrategy.class) public interface ShardingOrder2Dao { @SQL("insert into #table(id, uid, price, status) values(:id, :uid, :price, :status)") public void addOrder(@ShardingBy("uid") Order order); @SQL("select id, uid, price, status from #table where uid = :1") public List<Order> getOrdersByUid(@ShardingBy int uid); class OrderShardingStrategy implements ShardingStrategy<Integer, Integer> { @Override public String getDatabase(Integer uid) { return uid < 1000 ? "db1" : "db2"; } @Override public String getTargetTable(String table, Integer uid) { return table + "_" + (uid % 2); } } }
上面的代碼中,引入了@ShardingBy注解,@ShardingBy=@DatabaseShardingBy+@TableShardingBy。
多維度分片策略
上面的所有的代碼我們都使用uid作為分片策略的計算參數,我們稱之為一維分片策略。
考慮下面一個問題,當我們把數據庫分片信息與表分片信息保存到order表中id字段的頭部時,我們不但能把uid作為分片策略的計算參數,也能把id作為分片策略的計算參數。但@Sharding注解放在類上時,我們只能要么選擇uid作為分片策略的計算參數,要們選擇id作為分片策略的計算參數。這時我們需要將@Sharding注解下移到方法上,不同的方法指定不同的分片策略,實現多維度分片策略。
請看下面的代碼:
@DB(table = "t_order") public interface ShardingOrder3Dao { @SQL("insert into #table(id, uid, price, status) values(:id, :uid, :price, :status)") @Sharding(shardingStrategy = ShardingOrder3Dao.OrderUidShardingStrategy.class) public void addOrder(@ShardingBy("uid") Order order); @SQL("select id, uid, price, status from #table where uid = :1") @Sharding(shardingStrategy = ShardingOrder3Dao.OrderUidShardingStrategy.class) public List<Order> getOrdersByUid(@ShardingBy int uid); @SQL("select id, uid, price, status from #table where id = :1") @Sharding(shardingStrategy = OrderIdShardingStrategy.class) public Order getOrderById(@ShardingBy String id); class OrderUidShardingStrategy implements ShardingStrategy<Integer, Integer> { @Override public String getDatabase(Integer uid) { return uid < 1000 ? "db1" : "db2"; } @Override public String getTargetTable(String table, Integer uid) { return table + "_" + (uid % 2); } } class OrderIdShardingStrategy implements ShardingStrategy<String, String> { @Override public String getDatabase(String orderId) { return "db" + orderId.substring(0, 1); } @Override public String getTargetTable(String table, String orderId) { return table + "_" + orderId.substring(1, 2); } } }
上面的代碼中,addOrder(@ShardingBy("uid") Order order) 方法與 getOrdersByUid(@ShardingBy int uid) 方法使用了以uid作為參數的分片策略OrderUidShardingStrategy,而 getOrderById(@ShardingBy String id) 方法則使用了以id作為參數的分片策略OrderIdShardingStrategy。
查看完整示例代碼和表結構
表分片與數據庫分片 的所有代碼和表結構均可以在 mango-example 中找到。