精华内容
下载资源
问答
  • 租户SaaS平台的数据库设计方案

    千次阅读 2019-12-08 09:45:10
    多租户技术,其实是一种软件架构技术的应用场景,是指:多个账套共用同一套系统,并且能保证各用户间数据的隔离,目的就是为了在同一套程序下实现多账套数据的隔离 SaaS平台模式:软件服务商将软件统一部署到自己的...

    多租户技术,其实是一种软件架构技术的应用场景,是指:多个账套共用同一套系统,并且能保证各用户间数据的隔离,目的就是为了在同一套程序下实现多账套数据的隔离

    SaaS平台模式:软件服务商将软件统一部署到自己的服务器上,客户可以根据自己的需求按需付费,直接连接服务器使用

    底层设计决定上层建筑,这句话说的真好

    试用版:多个公司共享一个数据库

    正式版:一个公司账套对应一个数据库

    展开全文
  • 多租户,通俗点说,多个租户共用同一套服务提供商提供系统资源,即跟现在流行的共享单车,充电宝差不多。 多租户更多跟云计算在一起,因为你有的客户需求大,付费多,那它分配的计算资源和功能更多,比如有自己独立...

     

     

    前言

    saas  软件即服务   现在的软件服务提供商提供一套页面给各个租户,通过一个申请页面

    填写租户的租户信息,点击生成,租户就可以有一套自己的系统,可以自己去新建用户,角色,授权等操作。

    其实这就是所谓的多租户技术。

    多租户,通俗点说,多个租户共用同一套服务提供商提供系统资源,即跟现在流行的共享单车,充电宝差不多。

    多租户更多跟云计算在一起,因为你有的客户需求大,付费多,那它分配的计算资源和功能更多,比如有自己独立的应用实例,数据库,硬盘空间等。

    这个跟云计算的概念就差不多,云计算就是计算资源,理论上云上的资源的无限大的。

    多租户隔离级别

    多租户主要就是数据隔离,具体来说有三种:

    1. 独立数据库    
    2. 共享数据库,独立 Schema
    3. 共享数据库,共享 Schema,共享数据表

    第一种消耗资源最多,就是租户有独立的数据库实例。

    第二种在一个数据库实例中每一个租户建立一个Schema数据库,这个也有个问题,当你租户很多的话,对应的表也更多,堆数据库性能也有影响

    第三种实例,数据库,表都共享,基于一个租户字段进行隔离,这种成本最低,隔离性也最差。

    架构图

    第三种网上有很多demo,大部分都是通过mybatis plus 在数据库操作时增加一个租户字段。

    第一和第二种差不多,都可以通过动态切换数据源的方法来达到。下面我就只要来讲第二种

    下面是一个架构图

    如上图所示:租户可以通过多租户系统申请应用和资源,审核通过后,租户信息同步到redis当中,同时根据的租户类型在对应的数据库实例中初始化库和表,

    你也可以自己手动在mysql库中自己增加几个一样的数据库,数据初始脚本在项目里。

    ps 我的应用系统项目来自 https://github.com/Heeexy/SpringBoot-Shiro-Vue  这是一个简单的springboot +vue的项目,为了增加动态数据源,我讲spring-boot-starter-parent版本升级为2.3.4.RELAERS版本。

    可以导入我修改后的代码,下载地址

    1,增加了 shrek-tanent模块,改模块是spring-boot-starter模块,通过启动类加入注解@EnableTenant启动租户模式,

    主要代码shrek-tanent代码如下:

    DynamicRoutingDataSourceHolder.java  动态数据源持有类
    public class DynamicRoutingDataSourceHolder {
    
        public static final String PRIMARY_DATASOURCE = "primaryDatasource";
    
    
        private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();
    
        public static void putKey(String name) {
            THREAD_LOCAL.set(name);
        }
    
        public static String getKey() {
            String key = THREAD_LOCAL.get();
            if (key == null) {
                key = PRIMARY_DATASOURCE;
                putKey(key);
            }
            return key;
        }
    
        public static void removeKey() {
            THREAD_LOCAL.remove();
        }
    }
    DynamicRoutingDataSource.java  动态数据源类
    public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    
    //获取当前请求线程的数据源持有
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicRoutingDataSourceHolder.getKey();
        }
    //设置多数据源map
        @Override
        public void setTargetDataSources(Map<Object, Object> targetDataSources) {
            super.setTargetDataSources(targetDataSources);
            super.afterPropertiesSet();
        }
    }
    @Configuration
    public class DatasourceConfig {
        @Value("${spring.datasource.url}")
        private String url;
        @Value("${spring.datasource.username}")
        private String username;
        @Value("${spring.datasource.password}")
        private String password;
        @Value("${spring.datasource.driver-class-name}")
        private String driverClassName;
    
        /**
         * 设置默认数据源
         * @return
         */
        @Primary
        @Bean(name = "datasource")
        public DynamicRoutingDataSource dynamicRoutingDataSource() {
            DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
            Map<Object, Object> targetDataSources = new HashMap<>(16);
            HikariDataSource hikariDataSource = new HikariDataSource();
            hikariDataSource.setJdbcUrl(url);
            hikariDataSource.setUsername(username);
            hikariDataSource.setPassword(password);
            hikariDataSource.setDriverClassName(driverClassName);
            targetDataSources.put(DynamicRoutingDataSourceHolder.PRIMARY_DATASOURCE, hikariDataSource);
            dataSource.setTargetDataSources(targetDataSources);
            //设置动态数据源的默认数据源,也就是配置文件里的数据源
            dataSource.setDefaultTargetDataSource(hikariDataSource);
            return dataSource;
        }
    
        /**
         * 加入事务管理? 没试过
         * @return
         */
        @Bean
        public PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dynamicRoutingDataSource());
        }
    }
    @Configuration
    @Slf4j
    public class DynamicSourceConfig implements ApplicationContextAware {
    
    
        @Autowired
        private DynamicRoutingDataSource dynamicRoutingDataSource;
        public static ApplicationContext applicationContext;
        public static List<TenantUser> tenantUsers = new ArrayList();
    
        //演示用  静态初始化,  可以考虑一个定时任务从redis通过,跟我上面的图形一样
        static {
            tenantUsers.add(new TenantUser(1,"aaa","jdbc:mysql://127.0.0.1:3306/shrek_example_1348915432900841474?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai","root","root"));
            tenantUsers.add(new TenantUser(2,"bbb","jdbc:mysql://127.0.0.1:3306/shrek_example_1349659685910249474?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai","root","root"));
        }
    
        @PostConstruct
        public void init() {
            List<TenantUser> list = tenantUsers;
    
            ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) DynamicSourceConfig.applicationContext;
            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
            Map<Object, DataSource> dataSourceMap = dynamicRoutingDataSource.getResolvedDataSources();
            Map<Object, Object> map = new HashMap<>(dataSourceMap);
            for (TenantUser user : list) {
                BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(HikariDataSource.class);
                beanDefinitionBuilder.addPropertyValue("username", user.getDatasourceUsername());
                beanDefinitionBuilder.addPropertyValue("password", user.getDatasourcePassword());
                beanDefinitionBuilder.addPropertyValue("jdbcUrl", user.getDatasourceUrl());
                beanDefinitionBuilder.addPropertyValue("driverClassName", "com.mysql.cj.jdbc.Driver");
                String beanName = user.getPrefix();
                beanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
                DataSource dataSource = DynamicSourceConfig.applicationContext.getBean(beanName, DataSource.class);
                map.put(beanName, dataSource);
            }
            dynamicRoutingDataSource.setTargetDataSources(Collections.unmodifiableMap(map));
            log.info("dynamic datasource init success");
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            DynamicSourceConfig.applicationContext = applicationContext;
        }
    }

    还有一个拦截类,就是拦截前端的请求,获取分域名,存入的ThredLocal中,这里可以做个判断,可以判断为空,直接返回租户未注册,再根据租户类型是基于字段还是库的,这里我没判断

    @Component
    public class RequestHander implements HandlerInterceptor {
        //请求前
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    
            //获取域名,从本地缓存对象(从redis同步过来)判断是否过期,租户ID和多租户类型,存入ThreadLocal
            URL url = new URL(httpServletRequest.getRequestURL().toString());
            DynamicRoutingDataSourceHolder.putKey(url.getHost().split("\\.")[0]);
    
            return true;
        }
    
    }

    修改你的host文件。

    127.0.0.1  aaa.shrek.com
    127.0.0.1  bbb.shrek.com
    127.0.0.1  ccc.shrek.com

    启动项目后,不同的域名就会操作不同的数据库了,这样就差不多是可以实现多租户了,一套应用,多个租户共用,可以基于多种数据隔离模式。

     

     

    展开全文
  • 书中内容主要集中在大多数企业常见的问题之上,如安装和升级到oracle database 11g数据库软件、创建数据库、导出和导入数据、数据库的备份与恢复、性能调优,等等。  本书还提供了dba完成本职工作必备的基本的uniix...
  • 在Oracle公司,Kyte专门负责Oracle数据库,他的任务是帮助使用Oracle数据库客户,并与他们共同设计和构建系统,或者对系统进行重构和调优。在进入Oracle公司之前,Kyte是一名系统集成人员,主要为美国军方和政府...
  • 10g数据库体系结构的权威图书,涵盖了所有最重要的Oracle体系结构特性,包括文件、内存结构和进程,锁和闩,事务、并发和版本,表和索引,数据类型,以及分区和并行,并利用具体的例子来充分介绍每特性,不仅...
  • 云端CRM客户管理系统

    2016-05-05 08:48:52
    云端CRM客户管理系统是基于网络共享一套解决方案,系统注重于多用户使用,可以适用于各类中小型企业,特别是各地有分公司的企业,本系统是一不错的选择。在企业局域网内,通过本客户管理系统,企业可以为每位...
  • 网络版适合在单位内部局域网上运行,除具有单机版的全部功能外,实现了数据文件共享功能,并具远程和本地登陆选择,远程登陆时,各个工作站上的不同用户可以同时对服务器上的个数据库文件进行各种不同的数据...
  • 系统结合了四千家用户的使用经验,经过数十位外贸专家的归纳、总结而提炼出的一套综合业务管理系统。系统涵盖了业务进程中的产品管理、客户管理、供应商管理、船公司管理、报价管理、合同进程管理、信用证管理、...
  • 云端CRM客户管理系统 是基于网络共享一套解决方案,系统注重于多用户使用,可以适用于各类中小型企业,特别是各地有分公司的企业,本系统是一不错的选择。在企业局域网内,通过本客户管理系统,企业可以为每位...
  • 2、对原有客户资料信息进行精简,可支持一个客户多个配送地址; 3、删除联系人的管理; 4、录入客户资料时,系统会对电话号码或手机号码查重,有重复会提示; 5、增加送餐明细报表,可查询任一时间段,任一配送员...
  • vc++ 应用源码包_1

    热门讨论 2012-09-15 14:22:12
    多个VC++加密解密算法库(CRYPT++) 详细讲解了Crypt++的加密解密的使用以及其它的加密解密方法(例如base64加解密、哈希加解密以及其它的文件加解密),分静态库和动态库方法。 JSCalls_demo js调用的演示源码 树...
  • vc++ 应用源码包_6

    热门讨论 2012-09-15 14:59:46
    多个VC++加密解密算法库(CRYPT++) 详细讲解了Crypt++的加密解密的使用以及其它的加密解密方法(例如base64加解密、哈希加解密以及其它的文件加解密),分静态库和动态库方法。 JSCalls_demo js调用的演示源码 树...
  • vc++ 开发实例源码包

    2014-12-16 11:25:17
    多个VC++加密解密算法库(CRYPT++) 详细讲解了Crypt++的加密解密的使用以及其它的加密解密方法(例如base64加解密、哈希加解密以及其它的文件加解密),分静态库和动态库方法。 JSCalls_demo js调用的演示源码 树...
  • vc++ 应用源码包_2

    热门讨论 2012-09-15 14:27:40
    多个VC++加密解密算法库(CRYPT++) 详细讲解了Crypt++的加密解密的使用以及其它的加密解密方法(例如base64加解密、哈希加解密以及其它的文件加解密),分静态库和动态库方法。 JSCalls_demo js调用的演示源码 树...
  • vc++ 应用源码包_5

    热门讨论 2012-09-15 14:45:16
    多个VC++加密解密算法库(CRYPT++) 详细讲解了Crypt++的加密解密的使用以及其它的加密解密方法(例如base64加解密、哈希加解密以及其它的文件加解密),分静态库和动态库方法。 JSCalls_demo js调用的演示源码 树...
  • vc++ 应用源码包_4

    热门讨论 2012-09-15 14:38:35
    多个VC++加密解密算法库(CRYPT++) 详细讲解了Crypt++的加密解密的使用以及其它的加密解密方法(例如base64加解密、哈希加解密以及其它的文件加解密),分静态库和动态库方法。 JSCalls_demo js调用的演示源码 树...
  • vc++ 应用源码包_3

    热门讨论 2012-09-15 14:33:15
    多个VC++加密解密算法库(CRYPT++) 详细讲解了Crypt++的加密解密的使用以及其它的加密解密方法(例如base64加解密、哈希加解密以及其它的文件加解密),分静态库和动态库方法。 JSCalls_demo js调用的演示源码 树...
  • FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
    第三步:在登陆后的界面文本框输入文本,然后发送 可以同时启动多个客户端 实现群聊。 浮动的广告 嵌套在html中 各种EJB之间的调用示例 7个目标文件 摘要:Java源码,初学实例,EJB调用实例  各种EJB之间的调用源码...
  • 对网站管理员的权限也可通过此模块来控制,大型网站的维护需要多个管理员,一定需要对众多的栏目分管理员进行管理权限的分配,只有最高管理员分配给其的管理权限才会生效,其他未经授权的管理权会将其拒之门外。...
  • 工会财务管理系统

    2011-12-20 17:20:11
    远程版:适用于集团单位或连锁单位,下属有N个机构,由总部统一管理的企业,可以同时在Internet上运行分布在多个办公地点的单位使用,方便财务统一管理。 北京润衡公司客服部 工会软件 财务软件 进销存软件 基本建设...
  • 慧创POS软件连锁版

    2011-01-02 11:01:54
    总部(配送中心):可在总部局域网中安装一个或多个后台进销存系统,通过各部门操作人员的数据录入和维护,将整个企业的商品进销存流程纳入良性的运转状态,在后台完成商品、客商、客户等信息维护,进货、存货和销货...
  • 慧创连锁版超市POS收银系统

    热门讨论 2009-07-29 09:01:00
    总部(配送中心):可在总部局域网中安装一个或多个后台进销存系统,通过各部门操作人员的数据录入和维护,将整个企业的商品进销存流程纳入良性的运转状态,在后台完成商品、客商、客户等信息维护,进货、存货和销货...
  • 总部(配送中心):可在总部局域网中安装一个或多个后台进销存系统,通过各部门操作人员的数据录入和维护,将整个企业的商品进销存流程纳入良性的运转状态,在后台完成商品、客商、客户等信息维护,进货、存货和销货...
  • 网络版适合在单位内部局域网上运行,除具有单机版的全部功能外,实现了数据文件共享功能,并具远程和本地登陆选择,远程登陆时,各个工作站上的不同用户可以同时对服务器上的个数据库文件进行各种不同的数据...
  • 7、使用内部发布的好处:将复杂项目分解为多个可管理的任务;易于实现对计划的变更;提高了解决方案的整体质量;提供了一个相对容易实现的短期目标 8、团队模型的环形结构中的六个角色是什么?程序管理;开发;测试...

空空如也

空空如也

1 2
收藏数 39
精华内容 15
关键字:

多个客户共享同一套数据库