欢迎光临散文网 会员登陆 & 注册

学习日志 211223 MySQL水平扩展

2021-12-23 17:21 作者:mayoiwill  | 我要投稿

MySQL数据库横向扩展

===================


# 背景

- 解决方案的任意一层 都应该有水平扩展的能力

- 数据库水平扩展的方案很多, 但是大多是商业收费的

- sharding的优点是什么

  - 可以水平扩展

  - 基本不要钱

- sharding的缺点

  - 严重依赖于sharding key

  - 有sharding key限定的语句 和原有SQL区别不大

  - 没有sharding key限定的语句 会丧失大多数SQL的能力

    - 原子性等

  - 由于大部分的分析功能是没有sharding key限定的 所以分析需求需要另找数据仓库的解决方案支持

- sharding是穷人的数据库层水平扩展方案

  - 商业的数据库集群 往往是按装机量正比收费的

  - 所以sharding能节省的费用 实际上是数据量越大 节省的越多

- 如果看很多软件的发展趋势

  - 往往就是一个免费干爆收费的过程

  - android和ios

  - mysql


# 目标

- 选取一个解决方案

- insert sharding with key

- select sharding with key

- DDL sharding

- select sharding without key

- scale-out


# sharding的解决方案

- application aware的

  - JDBC

  - 缺点 连接比较多

  - 每个datasource只有一个application可以使用

- proxy的

  - proxy的缺点是有集群单点的问题

  - 不满足全栈可水平扩展的要求

- 基于JDBC的方案

- 首先看一下Apache的方案

  - https://shardingsphere.apache.org/


# 配置数据源

- 参考

  - https://shardingsphere.apache.org/document/current/en/user-manual/shardingsphere-jdbc/java-api/rules/sharding/


## 目标

- 两个库 不分表

- id做键, 简单取余


## 流程

- 数据库建表

  - 两个库的建表语句一致

- 配置一个基于shardingSphere的数据源

  - 配置两个Hikari数据源作为原始数据源 分别连接到两个库实例

  - 配置分表规则

    - 配置表名格式 ShardingTableRuleConfiguration 部分

  - 配置分库规则

    - 配置算法 ShardingSphereAlgorithmConfiguration 部分

    - (后续构造总规则时会把底层数据源列表传入)

  - 配置主键生成器 (未验证)

    - ShardingSphereAlgorithmConfiguration 部分

  - 其它配置

    - 显示SQL到日志

  - 构建sharding数据源

    - 底层数据源列表(实际上是map)

    - 规则列表(目前只有sharding规则)

    - 其它配置

  - 说明

    - 原始示例中还有很多其它有用的配置, 如其它规则等

    - 后续用到了再专门讲

- 配置mybatis使用这个新的数据源

  - 显示配置以下三项

  - SqlSessionFactory

  - DataSourceTransactionManager

  - SqlSessionTemplate

  - 另外, 使用 MapperScan 注解的参数 指定对应的Dao使用新数据源

    - sqlSessionTemplateRef 部分

- 完整源码如下

```

@Configuration

@MapperScan(basePackages = "org.kaien.springbootdemo.dao", sqlSessionTemplateRef  = "mySessionTemplate")

public class DBConfiguration {

    @Bean

    @Primary

    public DataSource shardingDataSource() throws SQLException {

        Map<String, DataSource> dataSourceMap = new HashMap<>();


        // Configure the 1st data source

        HikariDataSource dataSource0 = new HikariDataSource();

        dataSource0.setDriverClassName("com.mysql.jdbc.Driver");

        dataSource0.setJdbcUrl("jdbc:mysql://mycluster-mysql-0.mycluster-mysql:3306/test_db");

        dataSource0.setUsername("root");

        dataSource0.setPassword("Mysql123");

        dataSourceMap.put("ds_0", dataSource0);


        // Configure the 2nd data source

        HikariDataSource dataSource1 = new HikariDataSource();

        dataSource1.setDriverClassName("com.mysql.jdbc.Driver");

        dataSource1.setJdbcUrl("jdbc:mysql://mycluster-mysql-1.mycluster-mysql:3306/test_db");

        dataSource1.setUsername("root");

        dataSource1.setPassword("Mysql123");

        dataSourceMap.put("ds_1", dataSource1);


        // 表配置

        ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();

        ShardingTableRuleConfiguration table = new ShardingTableRuleConfiguration("test_doc",

                "ds_${0..1}.test_doc");

        shardingRuleConfiguration.getTables().add(table);

        shardingRuleConfiguration.getBindingTableGroups().add("test_doc");


        // 数据库配置

        Properties defaultModShardingAlgorithm = new Properties();

        defaultModShardingAlgorithm.setProperty("sharding-count", "2");

        shardingRuleConfiguration.getShardingAlgorithms().putIfAbsent("defaultModShardingAlgorithm",

                new ShardingSphereAlgorithmConfiguration("MOD", defaultModShardingAlgorithm));


        ShardingStrategyConfiguration defaultDatabaseShardingStrategy = new StandardShardingStrategyConfiguration(

                "id",

                "defaultModShardingAlgorithm"

        );

        shardingRuleConfiguration.setDefaultDatabaseShardingStrategy(defaultDatabaseShardingStrategy);


        // 主键生成配置

        Properties snowflakeProperties = new Properties();

        snowflakeProperties.setProperty("worker-id", "123"); // ? TODO

        shardingRuleConfiguration.getKeyGenerators().put("snowflake", new ShardingSphereAlgorithmConfiguration(

                "SNOWFLAKE",

                snowflakeProperties

        ));

        KeyGenerateStrategyConfiguration defaultKeyGenerateStrategy = new KeyGenerateStrategyConfiguration(

                "id",

                "snowflake"

        );

        shardingRuleConfiguration.setDefaultKeyGenerateStrategy(defaultKeyGenerateStrategy);


        // 其它配置

        Properties otherProperties = new Properties();

        otherProperties.setProperty("sql-show", "true");


        DataSource dataSource = ShardingSphereDataSourceFactory.createDataSource(

                dataSourceMap,

                Collections.singleton(shardingRuleConfiguration),

                otherProperties);

        return dataSource;

    }


    @Bean(name = "mySessionFactory")

    @Primary

    public SqlSessionFactory mySessionFactory(@Qualifier("shardingDataSource") DataSource shardingDataSource)

            throws Exception {

        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();

        bean.setDataSource(shardingDataSource);

        return bean.getObject();

    }


    @Bean(name = "myTransactionManager")

    @Primary

    public DataSourceTransactionManager myTransactionManager(@Qualifier("shardingDataSource") DataSource shardingDataSource) {

        return new DataSourceTransactionManager(shardingDataSource);

    }


    @Bean(name = "mySessionTemplate")

    @Primary

    public SqlSessionTemplate mySessionTemplate(@Qualifier("mySessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {

        return new SqlSessionTemplate(sqlSessionFactory);

    }

}


```


- 检查

  - 编写dao层 实现queryById  

    - `select ... where id = #{id}` 

  - 再编写一个controller 调用dao层

  - 由path传入id 形如 

    - `@RequestMapping(value = "/doc/{id}", method = RequestMethod.GET)`

  - 从web访问 尝试id取奇数 或 偶数 看日志分别如下

    ```

    Actual SQL: ds_1 ::: select id, gmt_create, gmt_modify, doc from test_doc where id = ? ::: [1]

    ```

    或者

    ```

    Actual SQL: ds_0 ::: select id, gmt_create, gmt_modify, doc from test_doc where id = ? ::: [2]

    ``` 


## Q&A

- Q: 报SQL错误

- A: mybatis变量写成了 #id, 实际上应为 #{id} 

- 这次特别顺利 没报什么其它的错误 如果读者有相关的错误欢迎评论补充


# insert sharding with key TODO

 


学习日志 211223 MySQL水平扩展的评论 (共 条)

分享到微博请遵守国家法律