- 浏览: 237215 次
- 性别:
- 来自: 珠海
文章分类
- 全部博客 (196)
- 职业感想 (66)
- hadoop (7)
- spark (5)
- mycat (13)
- Raft (2)
- nexus (3)
- Nginx (5)
- SpringBoot (5)
- Mongodb (5)
- amq (2)
- shell (3)
- netty (3)
- spring 5.0 (2)
- 响应式编程 (1)
- Spring Cloud (4)
- pdf (1)
- docker (17)
- Kubernetes (1)
- 技术总监 (1)
- 区块链 (1)
- 大数据 (1)
- Kylin (1)
- NIO (1)
- JVM (2)
- zookeeper (1)
- Python (2)
- Docker-Compose (2)
- mysql (14)
- eclipse (1)
- spring cloud config (1)
- Redis (1)
- centos (2)
- tokudb (1)
- findbugs (1)
- HikariCP (1)
- php (1)
- ES (1)
- ZigBee (1)
- 物联网 (1)
- NLP (1)
- ionic (1)
- go (4)
- node red (3)
- 树莓派 (1)
- iot (1)
- pm2 (1)
- nodejs (1)
- Supervisor (1)
- dbus (1)
- linux (1)
- vpn (1)
- arm (1)
- debian (1)
- consul (1)
- Hystrix (1)
- InheritableThreadLocal (1)
最新评论
-
男人50:
不远啊 写道难道大多程序猿都是这样过来的吗,接着后来有一部分当 ...
刚毕业的时候 -
不远啊:
难道大多程序猿都是这样过来的吗,接着后来有一部分当了老师教着新 ...
刚毕业的时候 -
男人50:
...
ES 与关系型数据库的对比 -
liaodongdakai:
精通并发与Netty网盘地址:https://pan.baid ...
精通netty框架 -
男人50:
knight_black_bob 写道这内容怎么审核的,你好, ...
我从事技术的这些年(第12年)
请您先登录,才能继续操作
Mycat多租户方案 1、需求 1、1 需求图 这里写图片描述 1、2 环境说明 环境说明: 这里写图片描述 2 每租户一逻辑库方案 2.1实现思想 用户在用用户名登陆时,首先,需要根据用户名,查询到该用户所在的逻辑库,然后登陆成功后,将和会话信息存放在一起,方便在访问其他业务的时候,能够很方便的得到该逻辑库。与此同时,利用Mybatis 提供的 SQL拦截器机制与Mycat提供的注解,改写SQL语句为 sql = “/!mycat:schema=” + tenant + ” /” + sql; 这样Mycat在解析时,会自动路由到tenat逻辑库上执行SQL语句。 2.2 具体实现关键点 Mycat 模拟配置如下: 这里写图片描述 2.2.1、登陆接口申明 public Map login( String account, String password ); 根据用户名与密码登陆 返回值说明: { “code” : 0, “data”: { “userId” : 1, “tenant” : “h_xsgjzx” } } 接口中的返回 tenant 参数,作为其他业务接口的第一参数。 现在有个关键点,就是根据用户名account 怎么知道用户存在哪个逻辑库呢?我给出用思路是,提供一个表来记录所有数据库中表的结合,global_user,字段基本如下: ID account db_pos 然后提供一个接口,根据用户名查询出db_pos的值 然后再去实现该接口 实现1:查询刚才global_user表,获取tenent;也可以用redis缓存等。该处可以扩展。 2.2.2、控制层方法 通过成功登录系统后,就能得到 逻辑scheme : tenant。业务action的声明如下: public Map findDepts( String tenant, 其他业务参数 ) ; 为了避免 tenant 参数污染业务层,DAO层的方法声明,,故在控制器层(Control)将 tenant 参数存入到 ThreadLocal 变量中。 现在提供 Tenant工具类,申明如下: package persistent.prestige.modules.common.tenant; public class TenantContextHolder { private static ThreadLocal<String> tenanThreadLocal = new ThreadLocal<String>(); public static final void setTenant(String scheme) { tenanThreadLocal.set(scheme); } public static final String getTenant() { String scheme = tenanThreadLocal.get(); if (scheme == null) { scheme = ""; } return scheme; } public static final void remove() { tenanThreadLocal.remove(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 那控制器层代码的伪代码如下: public Map findDepts( String tenant, String businessP1 ) { Map result = new HashMap(); try { TenantContextHolder.setTenant(tenant); //调用service层代码 } catch(Throw e) { e.printStackTrace(); result.put(“msg”, “系统异常”); result.put(“code”, 1); } finally { TenantContextHolder.remove(); System.out.println(“控制器层面,,移除tenant。。。”); } } 如果每个控制器层代码,都需要用上面的模板来做,未免有点。。。 所以为了统一处理 Tenant ,目前提供一个给予Spring AOP 的拦截器。 代码如下: package persistent.prestige.modules.common.tenant; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.factory.annotation.Autowired; import persistent.prestige.modules.edu.service.UserSchemeService; public class TenantControlInteceper implements MethodInterceptor { @Autowired private UserSchemeService userScemeService; @Override public Object invoke(MethodInvocation invocation) throws Throwable { try { if("login".equals(invocation.getMethod().getName())) { return invocation.proceed(); } System.out.println("控制器层面,,计算 tenant。。。"); Object[] args = invocation.getArguments(); String tenant = ""; if( args != null && args.length > 0) { tenant = (String)args[0]; } TenantContextHolder.setTenant(tenant); return invocation.proceed(); }finally { TenantContextHolder.remove(); System.out.println("控制器层面,,移除tenant。。。"); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 统一处理Tenant 的设置为移除;此处与代码中的有点差别,是因为,,根据用户登录名获取tenant的逻辑放在了上面登录接口中。 只要遵循这样一种编码规范,action方法的第一个参数的值为 tenant 就好。 配置一下拦截器【基于Spring AOP】 这里写图片描述 2.2.3、业务承载方法 业务方法无需改变;但是要利用Mybatis 拦截器改写SQL。代码和配置如下: 1)工具类 package persistent.prestige.platform.mybatis.Interceptor; import java.lang.reflect.Field; import org.apache.commons.lang.reflect.FieldUtils; public class ReflectHelper { public static Object getFieldValue(Object obj , String fieldName ){ if(obj == null){ return null ; } Field targetField = getTargetField(obj.getClass(), fieldName); try { return FieldUtils.readField(targetField, obj, true ) ; } catch (IllegalAccessException e) { e.printStackTrace(); } return null ; } public static Field getTargetField(Class<?> targetClass, String fieldName) { Field field = null; try { if (targetClass == null) { return field; } if (Object.class.equals(targetClass)) { return field; } field = FieldUtils.getDeclaredField(targetClass, fieldName, true); if (field == null) { field = getTargetField(targetClass.getSuperclass(), fieldName); } } catch (Exception e) { } return field; } public static void setFieldValue(Object obj , String fieldName , Object value ){ if(null == obj){return;} Field targetField = getTargetField(obj.getClass(), fieldName); try { FieldUtils.writeField(targetField, obj, value) ; } catch (IllegalAccessException e) { e.printStackTrace(); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 SQL拦截类 package persistent.prestige.platform.mybatis.Interceptor; import java.sql.Connection; import java.util.Properties; import org.apache.ibatis.executor.statement.RoutingStatementHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.kahadb.page.Page; import org.springframework.beans.factory.annotation.Autowired; import persistent.prestige.modules.common.tenant.TenantContextHolder; import persistent.prestige.modules.edu.dao.TeacherUserDao; @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) }) public class TenantInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { String tenant = TenantContextHolder.getTenant(); if(tenant == null || tenant == "") { System.out.println("tenant 为空,不需要改写sql语句"); return invocation.proceed(); } if (invocation.getTarget() instanceof RoutingStatementHandler) { System.out.println("aaaaaaa"); RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation .getTarget(); StatementHandler delegate = (StatementHandler) ReflectHelper .getFieldValue(statementHandler, "delegate"); BoundSql boundSql = delegate.getBoundSql(); Object obj = boundSql.getParameterObject(); // 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性 MappedStatement mappedStatement = (MappedStatement) ReflectHelper .getFieldValue(delegate, "mappedStatement"); // 拦截到的prepare方法参数是一个Connection对象 Connection connection = (Connection) invocation.getArgs()[0]; // 获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句 String sql = boundSql.getSql(); // 给当前的page参数对象设置总记录数 System.out.println("处理之前" + sql); //对 sql 增加 mycat 注解 sql = "/*!mycat:schema=" + tenant + " */" + sql; System.out.println("加入处理后:" + sql); ReflectHelper.setFieldValue(boundSql, "sql", sql); } return invocation.proceed(); } @Override public Object plugin(Object target) { // TODO Auto-generated method stub if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 3)配置如下: 这里写图片描述 2.2.4、方案优缺点 优点: 对业务代码侵入少,开发人员无需关注数据在哪个逻辑库上,隔离性好。 缺点 如果需要对所有租户的数据进行汇聚的话,需要业务上去实现。 该方案代码:请关注如下代码: 控制层 persistent.prestige.modules.edu.action. EduControl 里面有login的模拟实现,业务方法的实现。 拦截器 persistent.prestige.modules.common.tenant. TenantControlInteceper persistent.prestige.platform.mybatis.Interceptor. TenantInterceptor 相关代码我已上传到: https://github.com/dingwpmz/Mycat-Demo 3 多租户同一逻辑库方案 3.1实现思想 每个分片对应一个集团,每个业务表中增加一个分片字段 db_pos,,类型为int型,比如制定如下字段: 0 h_xsgizx 20 h_xsyz 40 m_fhzx 60 m_mzzx Mycat 提供一个逻辑库,其中每个分片代表一个集团,,由于集团数量是固定的,故可以采用 分片枚举 进行分片。 这种方案,不是传统意义上的多租户,而是用mycat枚举分片规则。配置分片就好。 3.2.4、方案优缺点 优点: 实现简单,不需要增加额外的拦截器等。并且多库汇聚非常方便。 缺点: 在业务开发中,需要在方法参数列表中,特别是DAO层,增加 分片字段参数。
发表评论
-
zk管理mycat
2018-07-26 11:09 7121、把conf下的配置文件 复制到 zkconf下面 2、修 ... -
Unable to register shutdown hook because JVM is shutting down
2018-07-25 17:48 3178<?xml version="1.0" ... -
mycat无法创建存储过程 ,只能调用存储过程
2018-07-11 14:10 1652mycat无法创建存储过程 ,只能调用存储过程 mycat无 ... -
利用MyCAT实现MySQL的读写分离和主从切换
2018-07-10 18:29 685实验环境介绍 采用的是在同一台机器上部署MyCAT,MySQ ... -
mycat dn过多(2)
2018-06-27 13:18 533<dataNode name="multi ... -
mycat dn过多
2018-06-27 12:57 580<table name="customer&q ... -
Mycat事务源码分析
2018-06-21 14:33 1060Mycat的事务相关的代码逻辑,目前的实现方式如下: 用户会话 ... -
Mycat全局表
2018-06-11 11:14 1964如果你的业务中有些数据类似于数据字典,比如配置文件的配置,常用 ... -
分片规则
2018-06-11 11:13 16199.1 分片规则概述 在数据切分处理中,特别是水平切分中,中间 ... -
关系型数据库和NoSQL数据库
2018-06-07 12:59 1338针对上面两类系统有多种技术实现方案,存储部分的数据库主要分为两 ... -
创建MyCat的Docker镜像
2018-05-31 15:43 1187MyCat 要使用JDK1.7以上环境,因此基于openjdk ... -
mycat 原理分享
2018-02-10 09:05 988下载地址 mycat pdf下载
相关推荐
【Mycat多租户解决方案】是针对SAAS(Software as a Service)应用设计的一种高效且资源节省的数据隔离策略。在传统的多租户模式中,每个租户通常需要单独的数据库实例,这不仅消耗大量资源,还增加了管理和运维的...
### 多租户方案:Mycat在多租户环境中的应用 #### 一、多租户概述 在软件架构中,“多租户”是指一个单一的应用实例能够为多个不同的客户(租户)提供服务,并确保每个租户的数据隔离与安全性。这种方式可以有效地...
“多租户”是Mycat的另一个重要特性,它支持在一个物理数据库实例上部署多个独立的应用或客户,每个应用或客户被称为一个租户。Mycat通过租户隔离,确保了不同租户之间的数据安全性和互不影响,同时减少了硬件资源的...
- 多租户:Mycat能够支持多租户的数据库架构,每个租户可以使用相同的数据表结构,但数据相互隔离。 在“快速入门”章节中,提供了在短时间内快速上手Mycat的基本方法,包括服务安装、配置和服务启动等内容。这部分...
- 多租户:Mycat支持多租户模式,允许多个租户共享同一个Mycat实例,通过不同的逻辑库来区分。 **3. Mycat分片策略** Mycat提供分片功能,主要有垂直分片和水平分片。垂直分片是指把不同的表切分到不同的数据库中...
1. 入门篇介绍了Mycat的基本概念,如数据库中间件、逻辑库、逻辑表、分片节点、节点主机、分片规则、全局序列号以及多租户等关键术语,这些都是理解Mycat的基本构建块。 2. 快速入门章节带领读者通过简短的介绍和...
这节可能会介绍Mycat全局表、ER分片表、多对多关联、常用分片规则以及权限控制和多租户支持等高级概念。 ### 第11章 常见问题与解决方案 本章集中解答了Mycat用户可能遇到的问题,并提供了相应的解决方案。 ### 第...
单租户模式是一种传统的软件架构,其中每个...这个文档提供了一个简单的实现方案,但它只是多租户架构设计的一个起点。实际应用中,还需要考虑性能、安全性、数据隔离、扩展性等多个方面,以确保系统的稳定性和可靠性。
6. **多租户支持**:Mycat允许在同一实例上为不同应用或客户提供独立的数据空间,实现多租户环境下的资源隔离。 **Mycat工作流程:** 1. 应用程序发送SQL请求至Mycat服务器。 2. MyCat接收到请求后,解析SQL语句。...
- 支持多租户解决方案。 - 支持分布式事务(弱XA),并且在1.6.5版本中引入了对XA分布式事务的支持。 - 支持全局序列号,解决了分布式环境下的主键生成问题。 - 分片规则丰富且易于扩展,支持插件化开发。 - 强大的...
多租户支持是Mycat的重要功能之一,它允许在同一个Mycat实例中管理多个客户的数据,同时保证数据的隔离。这对于SaaS(软件即服务)提供商来说非常重要,能够在同一个数据库系统上提供多租户服务,而不需要为每个租户...
此外,Mycat支持多租户,能为不同业务提供隔离的运行环境。 ### 3. 基本概念 3.1. **数据库中间件**:数据库中间件是位于应用和数据库之间的软件层,它处理数据访问的复杂性,提供透明的数据访问服务。 3.2. **...
多租户支持是指Mycat可以在同一套硬件资源上为多个用户提供隔离的服务环境,每个租户可以有自己的逻辑库、逻辑表等。 #### 四、Mycat的配置 Mycat的配置主要包括schema.xml、server.xml等文件,它们用于定义Mycat...
- **Mycat支持**:Mycat支持多租户方案,通过动态路由的方式实现不同租户之间的数据隔离。 - **优势**: - 降低了硬件成本; - 减少了运维负担; - 提高了资源利用率。 ##### 分表大数据 - **概念**:分表是指...
- **多租户**:多租户技术允许一个MyCAT实例承载多个租户的数据,每个租户的数据可以相互独立。 3. **快速入门** Mycat入门包括快速镜像方式体验、服务安装与配置、服务启动与启动设置、使用demo等。 4. **日志...
- **多租户应用**:针对不同用户提供独立的数据库服务,确保数据隔离的同时又便于集中管理。 - **报表系统**:支持复杂的查询和数据分析,适用于构建高性能的报表系统。 - **海量数据实时查询**:通过分布式架构,...
此外,MyCat的多租户应用开发和云平台基础设施的支持,为分布式架构的适应性和灵活性提供了有力的保障。 MyCat的技术优势表现在其能够支持读写分离机制,缓解写库的压力并提高数据库的并发查询能力,从而提高数据库...