兩種實現方式mycat多租戶,枚舉分片,注解攔截


第一種:

  優點:支持進一步分片

  缺點: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自動分片,能很大程度減少維護成本.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM