文档章节

采用MyCAT的多租户方案 - 按SQL注解路由

林中漫步
 林中漫步
发布于 2017/08/21 15:43
字数 1526
阅读 558
收藏 0

1. 方案示意图

输入图片说明

2. 创建物理库

以root账号登录mariaDB, 创建三个schema: db_10001、db_10013、db_10020, 分别用来存放商户10001、10013、10020的数据:

C:\Users\HuQingmiao>mysql -uroot -p123456
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.1.16-MariaDB mariadb.org binary distribution

Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> create database db_10001;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> create database db_10013;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> create database db_10020;
Query OK, 1 row affected (0.00 sec)

在以上三个schema上分别执行建表语句(也可以在配置mycat 逻辑库后通过mycat执行):

DROP TABLE if EXISTS tenant_user;
CREATE TABLE tenant_user
(
    id      BIGINT UNSIGNED   NOT NULL,
    -- tenant_id     INT(10)         NOT NULL     , //注意,不需要此列
    name     	    VARCHAR(255)    NOT NULL     ,
    mbl_nbr       VARCHAR(15)     NOT NULL     ,
    idcard_no	    VARCHAR(15),
    gender        CHAR (1),
  PRIMARY KEY(id)
) ;

DROP TABLE  if EXISTS dict ;
CREATE TABLE dict
(
   id         BIGINT UNSIGNED AUTO_INCREMENT  NOT NULL,
   kind             VARCHAR(30) not null comment '类别',
   kind_desc        VARCHAR(45) comment '类别描述',
   code             VARCHAR(30) not null comment '编码',
   name             VARCHAR(250) not null comment '名称',
   order_by         SMALLINT not null comment '排序号',
   status           SMALLINT not null comment '0-作废   1-有效',
   PRIMARY KEY (id)
);

注意, 对于需要水平切分的表,不能设置自增主键,否则不同结点会存在相同主键值; 另外,对于global类型的表,也最好不用要自增主键,并发时会造成冲键冲突。

3. 配置逻辑库

输入图片说明

按上图,打开${MYCAT_HOME}/conf/schema.xml,创建三个逻辑库、配置相应的datanode:

<schema name="logic_10001" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn_10001">
	<table name="dict" primaryKey="id"  type="global" dataNode="dn_10001,dn_10013,dn_10020" />
</schema>
<schema name="logic_10013" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn_10013">
	<table name="dict" primaryKey="id"  type="global" dataNode=""dn_10001,dn_10013,dn_10020" />
</schema>
<schema name="logic_10020" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn_10020">
	<table name="dict" primaryKey="id"  type="global" dataNode=""dn_10001,dn_10013,dn_10020" />
</schema>

<dataNode name="dn_10001" dataHost="localhost1" database="db_10001" />
<dataNode name="dn_10013" dataHost="localhost1" database="db_10013" />
<dataNode name="dn_10020" dataHost="localhost1" database="db_10020" />

打开${MYCAT_HOME}/conf/server.xml,给用户root授权三个逻辑库的权限:

 <user name="root">
	<property name="password">123456</property>
	<property name="schemas">TESTDB,logic_10001,logic_10013,logic_10020</property>
 </user>

4. 读写测试

重启、并登录mycat:

C:\Users\HuQingmiao>mycat restart
wrapper  | Stopping the Mycat-server service...
wrapper  | Mycat-server stopped.
wrapper  | Starting the Mycat-server service...
wrapper  | Mycat-server started.

C:\Users\HuQingmiao>mysql -uroot -p123456 -hlocalhost -P8066 -DTESTDB
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.29-mycat-1.6-RELEASE-20161028204710 MyCat Server (OpenCloundDB)

Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

写入数据, 注意 要在原生sql前加上注解以告诉mycat操作哪个逻辑库,即:

       /*!mycat:schema = 逻辑库 */  原生sql

执行下列脚本:

/*!mycat:schema = logic_10001 */
insert into tenant_user (id, name, mbl_nbr, idcard_no )
values (1,  '牟森林', '13223232532', null);

/*!mycat:schema = logic_10013 */
insert into tenant_user (id, name, mbl_nbr, idcard_no )
values (2,  '牟森林', '13223232532', null);

/*!mycat:schema = logic_10020 */
insert into tenant_user (id, name, mbl_nbr, idcard_no )
values (3,  '胡三', '13520235368', null);


/*!mycat:schema = logic_10001 */
insert into dict (id, kind, kind_desc, code, name, order_by,status )
values (1,'tenant_entp_status', '企业信息状态', '1', '待认证', 1, 1);

/*!mycat:schema = logic_10001 */
insert into dict (id,kind, kind_desc, code, name, order_by,status )
values (3,'tenant_entp_status', '企业信息状态', '5', '已认证',  5,  1);

commit;

查看数据:

MySQL [TESTDB]> /*!mycat:schema = logic_10001 */
    ->  SELECT * FROM tenant_user;
+----+--------+-------------+-----------+--------+
| id | name   | mbl_nbr     | idcard_no | gender |
+----+--------+-------------+-----------+--------+
|  1 | 牟森林 | 13223232532 | NULL      | NULL   |
+----+--------+-------------+-----------+--------+
1 row in set (0.00 sec)

MySQL [TESTDB]> /*!mycat:schema =logic_10001 */
    -> SELECT * FROM dict;
+----+--------------------+--------------+------+----------+----------+--------+
| id | kind               | kind_desc    | code | name     | order_by | status |
+----+--------------------+--------------+------+----------+----------+--------+
|  1 | tenant_entp_status | 企业信息状态 | 1    | 待认证   |        1 |      1 |
|  3 | tenant_entp_status | 企业信息状态 | 5    | 已认证   |        5 |      1 |
+----+--------------------+--------------+------+----------+----------+--------+
3 rows in set (0.00 sec)
	
MySQL [TESTDB]> /*!mycat:schema =logic_10013 */
    ->  SELECT * FROM tenant_user;
+----+--------+-------------+-----------+--------+
| id | name   | mbl_nbr     | idcard_no | gender |
+----+--------+-------------+-----------+--------+
|  2 | 牟森林 | 13223232532 | NULL      | NULL   |
+----+--------+-------------+-----------+--------+
1 row in set (0.00 sec)

MySQL [TESTDB]> /*!mycat:schema =logic_10013 */
    -> SELECT * FROM dict;
+----+--------------------+--------------+------+----------+----------+--------+
| id | kind               | kind_desc    | code | name     | order_by | status |
+----+--------------------+--------------+------+----------+----------+--------+
|  1 | tenant_entp_status | 企业信息状态 | 1    | 待认证   |        1 |      1 |
|  3 | tenant_entp_status | 企业信息状态 | 5    | 已认证   |        5 |      1 |
+----+--------------------+--------------+------+----------+----------+--------+
3 rows in set (0.00 sec)
	
MySQL [TESTDB]> /*!mycat:schema = logic_10020 */
    ->  SELECT * FROM tenant_user;
+----+------+-------------+-----------+--------+
| id | name | mbl_nbr     | idcard_no | gender |
+----+------+-------------+-----------+--------+
|  3 | 胡三 | 13520235368 | NULL      | NULL   |
+----+------+-------------+-----------+--------+
1 row in set (0.00 sec)

MySQL [TESTDB]>  /*!mycat:schema = logic_10020 */
    -> SELECT * FROM dict;
+----+--------------------+--------------+------+----------+----------+--------+
| id | kind               | kind_desc    | code | name     | order_by | status |
+----+--------------------+--------------+------+----------+----------+--------+
|  1 | tenant_entp_status | 企业信息状态 | 1    | 待认证   |        1 |      1 |
|  3 | tenant_entp_status | 企业信息状态 | 5    | 已认证   |        5 |      1 |
+----+--------------------+--------------+------+----------+----------+--------+
3 rows in set (0.00 sec)

5. 验证切分效果

现在登录本地mariaDB各个物理库,检查数据分布:

C:\Users\HuQingmiao>mysql -uroot -p123456
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 614
Server version: 10.1.16-MariaDB mariadb.org binary distribution

Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> select * from db_10001.tenant_user;
+----+-----------+--------+-------------+-----------+--------+
| id | tenant_id | name   | mbl_nbr     | idcard_no | gender |
+----+-----------+--------+-------------+-----------+--------+
|  1 |     10001 | 牟森林 | 13223232532 | NULL      | NULL   |
+----+-----------+--------+-------------+-----------+--------+
1 row in set (0.00 sec)

MariaDB [(none)]> select * from db_10001.dict;
+----+--------------------+--------------+------+----------+----------+--------+
| id | kind               | kind_desc    | code | name     | order_by | status |
+----+--------------------+--------------+------+----------+----------+--------+
|  1 | tenant_entp_status | 企业信息状态 | 1    | 待认证   |        1 |      1 |
|  3 | tenant_entp_status | 企业信息状态 | 5    | 已认证   |        5 |      1 |
+----+--------------------+--------------+------+----------+----------+--------+
3 rows in set (0.00 sec)

MariaDB [(none)]> select * from db_10013.tenant_user;
+----+-----------+--------+-------------+-----------+--------+
| id | tenant_id | name   | mbl_nbr     | idcard_no | gender |
+----+-----------+--------+-------------+-----------+--------+
|  2 |     10013 | 牟森林 | 13223232532 | NULL      | NULL   |
+----+-----------+--------+-------------+-----------+--------+
1 row in set (0.00 sec)

MariaDB [(none)]> select * from db_10013.dict;
+----+--------------------+--------------+------+----------+----------+--------+
| id | kind               | kind_desc    | code | name     | order_by | status |
+----+--------------------+--------------+------+----------+----------+--------+
|  1 | tenant_entp_status | 企业信息状态 | 1    | 待认证   |        1 |      1 |
|  3 | tenant_entp_status | 企业信息状态 | 5    | 已认证   |        5 |      1 |
+----+--------------------+--------------+------+----------+----------+--------+
3 rows in set (0.00 sec)

6. 修改应用程序

接下来,要对应用程序做以下两处修改:

1) 引入类TenantContextHolder.java, 然后在受理请求的方法中, 在接收入参后、处理业务前, 加上一行代码以把租户id暂存到当前线程变量(ThreadLocal)中, 并且在处理完成、返回给调用者前, 把租户id从当前线程变量中移除,代码如下:

       public String realriskEventDecision(Long tenantId, Long appId, String event){
              // 把租户id暂存到 ThreadLocal
	         TenantContextHolder.setTenant(tenant); 

              // 处理业务逻辑
              do some business..  
        
              // 把租户id从 ThreadLocal移除
              TenantContextHolder.remove(); 
              
              return ..
       }

2) 引入TenantSQLInterceptor类, 在mybatis配置文件中,增加以下内容:

     <plugins>
          <plugin interceptor="com.github.walker.mycat.demo.common.support.TenantSQLInterceptor" />
     </plugins>

TenantSQLInterceptor将拦截sql, 向其添加头部注解, 以标明该sql应该发往哪个逻辑库执行; 它会调用TenantContextHolder.getSchema()以获得逻辑库名。

7. 方案总结

本方案适合把已有系统升级为saas服务, 只需要修改少量的代码, 即通过aop在请求入口处记录tenant_id,再通过sql拦截器获取tenant_id对应的逻辑库,然后在sql 头部添加mycat特有的注解以指明在哪个逻辑库执行。

© 著作权归作者所有

林中漫步
粉丝 102
博文 55
码字总数 33266
作品 0
深圳
架构师
私信 提问
Mycat多租户/分布数据库/集群数据库以及SAAS研究进度报告

1、mycat操作 (1)垂直切分配置 (2)水平切分配置 (3)mycat修改schema.xml之后热重启 (4)mycat注解的实现 (5)mycat高可用(搭配HAproxy等一起做集群,多个mycat节点) 2、mycat可以配...

蓝奔z
2018/08/24
385
0
数据库中间件 - XiaoMi Gaea

简介 Gaea 是小米商城/系统组研发的基于 mysql 协议的数据库中间件,目前在小米商城大陆和海外得到广泛使用,包括订单、社区、活动等多个业务。Gaea 支持分库分表、sql 路由、读写分离等基本...

匿名
05/24
19.1K
18
MySQL 数据库中间件 MyCAT 基础解析

前言 网络应用持续扩张的过程中,为了处理海量数据往往首先遇到的挑战就是数据存储的扩展 数据存储的扩展一般以切分来实现,切分的技术实现又可分为垂直切分和水平切分: 以表(或Schema)为切...

PeakFang-BOK
2018/11/17
108
0
Dubbo学习系列之十三(Mycat数据库代理)

软件界有只猫,不用我说,各位看官肯定知道是哪只,那就是大名鼎鼎的Tomcat,现在又来了一只猫,据说是位东方萌妹子,暂且认作Tom猫的表妹,本来叫OpencloudDB,后又改名为Mycat,或许Cat更亲...

甲由崽
09/29
0
0
Mycat学习实战-Mycat初识

Mycat学习实战-Mycat初识 Mycat学习实战-Mycat初识 1. Mycat是什么 2. Mycat与其他中间件的区别 3. Mycat能解决的问题 4. Mycat核心概念 5. Mycat文件夹以及文件介绍 1. Mycat是什么 Mycat是...

ygqygq2
2018/06/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

总结

一、设计模式 简单工厂:一个简单而且比较杂的工厂,可以创建任何对象给你 复杂工厂:先创建一种基础类型的工厂接口,然后各自集成实现这个接口,但是每个工厂都是这个基础类的扩展分类,spr...

BobwithB
43分钟前
4
0
java内存模型

前言 Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模...

ls_cherish
46分钟前
4
0
友元函数强制转换

友元函数强制转换 p522

天王盖地虎626
昨天
5
0
js中实现页面跳转(返回前一页、后一页)

本文转载于:专业的前端网站➸js中实现页面跳转(返回前一页、后一页) 一:JS 重载页面,本地刷新,返回上一页 复制代码代码如下: <a href="javascript:history.go(-1)">返回上一页</a> <a h...

前端老手
昨天
5
0
JAVA 利用时间戳来判断TOKEN是否过期

import java.time.Instant;import java.time.LocalDateTime;import java.time.ZoneId;import java.time.ZoneOffset;import java.time.format.DateTimeFormatter;/** * @descri......

huangkejie
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部