第一種:
優點:支持進一步分片
缺點:schema配置繁瑣
注解式 /*!mycat:schema=[schemaName] */ 注意:這在navicat 里面是會報錯的,請用命令行登陸mycat 來測試
mysql> explain /*!mycat:schema=USER1 */ select * from order;
可以在每個sql語句前面添加此注解,Mybatis 可以重寫 MappedStatement 的 getBoundSql
來添加。
不管使用什么方式,感覺這都很搓。還要手動在Mycat schema.xml 中添加很冗長的 配置。
<mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="USER1" checkSQLschema="false" sqlMaxLimit="10000">
.....
</schema>
<schema name="USER2" checkSQLschema="false" sqlMaxLimit="10000">
......
</schema>
以下省略無數個 schema
第二種:使用枚舉分片實現多租戶
優點:schema.xml 配置相對簡潔
缺點:不可進一步分片
explain /*!mycat:schema=[schema] */ /*!mycat:dataNode=dn1 */ select * from order;
創建分片枚舉規則文件.
cat sharding-by-enum.txt 0=0 1=1 2=2
修改rule.xml的 function
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap"> <property name="mapFile">sharding-by-enum.txt</property> <property name="type">0</property> <!-- <property name="mapFile">partition-hash-int.txt</property> --> </function>
測試枚舉分片命中率,這就可以使用枚舉分片,達到多租戶效果
枚舉分片,解決查詢分片命中問題 mysql> explain select * from order a left join detail b on a.id = b.orderId where a.sharding_id = 0; +-----------+----------------------------------------------------------------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+----------------------------------------------------------------------------------------------------------------------+ | dn1 | select * from order a left join detail b on a.id = b.orderId where a.sharding_id = 0 | +-----------+----------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) 兩個表,都有枚舉分片字段 mysql> explain select * from order a left detail b on a.id = b.orderId where a.sharding_id and b.sharding_id = 0; +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | dn1 | select * from order a left detail b on a.id = b.orderId where a.sharding_id and b.sharding_id = 0 | +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> 沒有命中條件,造成全盤掃描 mysql> explain select * from order a left join detail b on a.id = b.orderId; +-----------+----------------------------------------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+----------------------------------------------------------------------------------------------+ | dn1 | select * from rder a left join detail b on a.id = b.orderId | | dn2 | select * from rder a left join detail b on a.id = b.orderId | | dn3 | select * from rder a left join detail b on a.id = b.orderId | +-----------+----------------------------------------------------------------------------------------------+ 3 rows in set (0.00 sec) UPDATE Database changed mysql> explain update detail set itemNum='100' where id = 8079 -> ; +-----------+--------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+--------------------------------------------------------------+ | dn1 | update detail set itemNum='100' where id = 8079 | | dn2 | update detail set itemNum='100' where id = 8079 | | dn3 | update detail set itemNum='100' where id = 8079 | +-----------+--------------------------------------------------------------+ 3 rows in set (0.02 sec) 分片字段不能被更新 mysql> explain update detail set itemNum='100',shardingId=0 where id = 8079; ERROR 1064 (HY000): Sharding column can't be updated DETAIL->SHARDINGID 加上分片字段 mysql> explain update order set itemNum='100' where id = 8079 and shardingId = 0; +-----------+---------------------------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+---------------------------------------------------------------------------------+ | dn1 | update order set itemNum='100' where id = 8079 and shardingId = 0 | +-----------+---------------------------------------------------------------------------------+ 1 row in set (0.00 sec) 刪除,同樣全盤掃描 mysql> explain delete detail where id = 8079; +-----------+--------------------------------------------+ | DATA_NODE | SQL | +-----------+--------------------------------------------+ | dn1 | delete detail where id = 8079 | | dn2 | delete detail where id = 8079 | | dn3 | delete detail where id = 8079 | +-----------+--------------------------------------------+ 3 rows in set (0.00 sec) 強制命中條件 mysql> explain delete detail where id = 8079 and shardingId = 0; +-----------+---------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+---------------------------------------------------------------+ | dn1 | delete detail where id = 8079 and shardingId = 0 | +-----------+---------------------------------------------------------------+ 1 row in set (0.00 sec) 全局表與分配表 inner join 以及 left jion right jion 都可以命中枚舉 mysql> explain select * from user a inner join order b where a.id=b.userId and b.shardingId = 0; +-----------+----------------------------------------------------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+----------------------------------------------------------------------------------------------------------+ | dn1 | select * from user a inner join order b where a.id=b.userId and b.shardingId = 0| +-----------+----------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> explain select * from user a right join order b on a.id = b.userId where b.shardingId = 0; +-----------+------------------------------------------------------------------------------------------------------------+ | DATA_NODE | SQL | +-----------+------------------------------------------------------------------------------------------------------------+ | dn1 |select * from user a right join order b on a.id = b.userId where b.shardingId = 0| +-----------+------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
無論是用哪一種方式,而枚舉方式的schema還簡單一點
只需要添加dataNode,即可,沒有第一種的方式那么膨脹XML
<dataNode name="dn1" dataHost="localhost1" database="db1" />
關於這個分片標識可以記錄再session里面(配合redis session ,不再考慮session 導致的內存溢出問題,萬一溢出,那非常值得開心,用戶量已經那么高了)
/** * Created by laizhenwei */ @Component("sessionAttributes") @Scope(scopeName = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public class SessionAttributes implements Serializable { private static final long serialVersionUID = -8521804511291179982L; //用戶分片標識 private Integer shardId; /** * 獲取當前用戶的分片標識 */ public Integer getCurrentShardId() { Optional<Integer> shardIdOptional = Optional.ofNullable(shardId); return shardIdOptional.orElseThrow(() -> new RuntimeException("無法獲取當分片標識!")); }
以上方式不支持多線程.請不要讓 SessionAttributes 離開Controller 作用域.如果整個程序都不涉及多線程,那么隨意在那一層注入
支持多線程方式,借助 InheritableThreadLocal然后寫一個過濾器
/** * Created by laizhenwei */ public class ShardContextFilter implements Filter { @Autowired private SessionAttributes sessionAttributes; @Override public void destroy() { // Do nothing } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ContextHolder.create(createContext()); chain.doFilter(request, response); } @Override public void init(FilterConfig config) throws ServletException { // Do nothing } private Context createContext() { //.... return context; } }
當然可以使用注解來過濾需要分片命中的請求的規則
@WebFilter(filterName="contextFilter",urlPatterns = {"/addOrder/*","/whatever/*"})
我喜歡在Security需要驗證的資源添加到過濾器鏈,這樣我不需要考慮資源規則問題
http.addFilterAfter(contextFilter(), FilterSecurityInterceptor.class);
最后總結一下:
如果單個用戶的數據還需要進一步分片,那么只能使用schema的注解攔截實現,
如果單個用戶數據,不需要再進一步分片,那么使用枚舉分片會簡單一些
但是,無論使用哪種方式,都難免需要人工處理schema.xml,除非開始就預建了很多schema或者dataNode,否則再添加的時候,mycat不能像nginx一樣reload配置文件,必須停機重啟讀取配置文件
mongodb也有類似的多租戶分片規則[tag]分片,但是這樣無法處理分片數據不均衡,所以還是不建議使用tag分片.
定制業務適合的片鍵,讓mongodb自動分片,能很大程度減少維護成本.