前言

我在上篇博客 “Spring Boot 的实践与思考” 中比对不同规范的 ORM 框架应用场景的时候提到过主从与读写分离,本篇随笔将针对此和分库分表进行更深入地探讨。

1. 漫谈

在进入正题之前,我想先随意谈谈对架构的拓展周期的想法(仅个人观点)。首先,我认为初期规划不该太复杂或者庞大,无论项目的中长期可能会发展地如何如何,前期都应该以灵活为优先,像分库分表等操作不应该在开始的时候就考虑进去。其次,我认为需求变更是非常正常的,这点在我等开发的圈子里吐槽的最多,其中自然有 “领导们” 在业务方面欠缺整体考虑的因素,但我们也不该局限在一个观点内,市场中变则通,不变则死,前期更是如此,因此在前几版的架构中我们必须要考虑较高的可扩展性。最后,当项目经过几轮市场的洗礼和迭代开发,核心业务趋于稳定了,此时我们再结合中长期的规划给系统来一次重构,细致地去划分领域边界,该解耦的解耦,该拆分的拆分。

2. 分库分表

2.1 概述

当数据库达到一定规模后(比如说大几千万以上),切分是必须要考虑的。一般来说我们首先要进行垂直切分,即按业务分割,比如说用户相关、订单相关、统计相关等等都可以单独成库。图片来源 →

但仅仅如此这是完全不够的,垂直切分虽然剥离了一定的数据,但每个业务还是那个数量级,因此我们还得采取水平切分进一步分散数据,这也是本节论述的重点。

分库分表的优点相信上述两图都一目了然了,一个是专库专用,业务更集中,另一个是提升数据库服务的负载能力。But there are always two sides to a coin。 从此以后你要接受你的系统复杂度将提升一个档次,迭代、迁移、运维等都不再容易。

2.2 切分策略

垂直切分在实现上就是一个多数据源的问题,没啥好讲的。以下 Demo 为水平切分,基于 Sharding-JDBC 中间件,我只做逻辑上的陈述,有关其更详细的信息和配置请移步 “官方文档”。

首先,我们得在配置文件中定义分片策略,application.yml:

server:port: 8001mybatis:config-location: classpath:mybatis/mybatis-config.xmlmapper-locations: classpath:mybatis/mappers/*.xmlsharding:jdbc:datasource:names: youclk_0,youclk_1youclk_0:type: org.apache.commons.dbcp.BasicDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://mysql:3306/youclk_0?useSSL=falseusername: rootpassword: youclkyouclk_1:type: org.apache.commons.dbcp.BasicDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://mysql:3306/youclk_1?useSSL=falseusername: rootpassword: youclkconfig:sharding:default-database-strategy:inline:sharding-column: numberalgorithm-expression: youclk_${number % 2}tables:user:actual-data-nodes: youclk_${0..1}.user

具体每个参数的含义在官方文档有详细解释,其实看名称也能理解个大概了,我定义将 number 为偶数的数据存入 youclk_0,奇数存入 youclk_1。

User:

@Data
public class User {private String id;private Integer number;private Date createTime;
}

UserRepository:

@Mapper
public interface UserRepository {void insert(User user);
}

UserMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.youclk.data.repository.UserRepository"><resultMap id="BaseResultMap" type="com.youclk.data.entity.User"><id column="id" property="id" jdbcType="CHAR"/><result column="number" property="number" jdbcType="INTEGER"/><result column="createTime" property="create_time" jdbcType="DATE"/></resultMap><sql id="Base_Column_List">id, number, createTime</sql><insert id="insert">INSERT INTO user (id, number)VALUES (uuid(),#{number,jdbcType=INTEGER})</insert>
</mapper>

UserService:

@Service
public class UserService {@Resourceprivate UserRepository userRepository;public void insert() {for (int i = 0; i < 10; i++) {User user = new User();user.setNumber(i);userRepository.insert(user);}}
}

Result:

以上做了一个简单的循环插入,可以看到数据已经按策略分库存储,结果符合我们的预期。

分库之后在查询方面要比之前更加谨慎,既然按策略去切了,那最好就是按策略去查,否则...比如我水平切分了 100个库,若不按策略去查询 LIMIT 100000, 10 这么一组数据,那最后扫描的数量级别是 100 * (100000 + 10), 这是比较恐怖的,虽然 Sharding-JDBC 做了一些优化,比如他不是一次性去查询到内存中,而是采用流式处理 + 归并排序的方式,但仍然比较耗资源,能避免还是尽量去避免吧。

2.3 分布式事务

在任何系统中事务都是顶要紧的事情,面对已分库的系统更是如此,保证夸库事务的安全从来不容易。分布式事务的场景有两种,一个是在分布式服务中,这个后续有机会再探讨,本节重点关注夸库事务。

Sharding-JDBC 自动包含了弱XA事务支持,即能够保证逻辑上的事务安全,但因网络或硬件导致的异常无法回滚,实现上与一般事务无异:

@Test
@Transactional
public void insertTest() {userService.insert();int error = Integer.parseInt("I want error");userService.insert();
}

可以看到夸库事务已回滚,除此之外 Sharding-JDBC 还提供了最大努力送达型柔性事务(将执行过程记录到日志中,失败重试,成功后删除,若最终还是失败则保留事务日志,供人工干预),虽然安全性更高,但无法保证时效,限制也很多,这里留个待续吧,后续有空再深入探讨(主要是比较晚了,想早点写完休息?)。

3. 主从与读写分离

3.1 概述

为什么要做主从?我们先来探讨以下这几个场景:

  • 我们知道每台数据库服务器有他的最大连接数和 IOPS,若有一天他无法再满足我们的业务需求,那相比于在单台服务器上去做性能堆叠,是是否横向去扩展几台 Slave 去分担 Master 的压力更加合理。
  • 如果服务对数据库的需求是 IO 密集型的,那可能会经常遇到行锁等待等问题,若要鱼与熊掌兼得,读写分离是否是更好的选择。
  • 如果我们的系统需要做很多报表,或者统计和数据分析,这些业务往往相当地耗费资源但又不是很重要,那针对此,我们是否应该开几台 Slave,让他们去小黑屋里慢慢执行,别来影响我处理核心业务的效率。

我大致能想到这么几点,欢迎各位继续留言补充。

3.2 主从部署

我以 MySQL 为例,一般部署架构为一台 Master 和 n 台 Slave,Master 的主责为写,并将数据同步至 Slave,Slave 主要提供查询功能。

为了测试方便,我直接使用 Docker 来部署,首先创建主从的配置文件,master.cnf:

[mysqld]
server_id = 1character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default-storage-engine=INNODB#Optimize omitsql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLESlog-bin = /var/lib/mysql/binlog
log_bin_trust_function_creators=1
binlog_format = ROW
expire_logs_days = 99
sync_binlog = 0slow-query-log=1
slow-query-log-file=/var/log/mysql/slow-queries.log
long_query_time = 3
log-queries-not-using-indexes

slave.cnf:

[mysqld]
server_id = 2character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default-storage-engine=INNODB#Optimize omitsql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLESlog-bin = /var/lib/mysql/binlog
log_bin_trust_function_creators=1
binlog_format = ROW
expire_logs_days = 99
sync_binlog = 0relay_log=slave-relay-bin
log-slave-updates=1
slave-skip-errors=allslow-query-log=1
slow-query-log-file=/var/log/mysql/slow-queries.log
long_query_time = 3

然后进行 compose 编排,加入 warm 集群,docker-compose.yml:

version: '3.5'services:mysql-master:image: mysqlports:- 3301:3306networks:- proxy- youclkvolumes:- /Users/Jermey/Documents/data/db/cluster/master/mysql:/var/lib/mysql- /Users/Jermey/Documents/data/db/cluster/master/conf.d:/etc/mysql/conf.denvironment:MYSQL_ROOT_PASSWORD: youclkmysql-slave:image: mysqlports:- 3302:3306networks:- proxy- youclkvolumes:- /Users/Jermey/Documents/data/db/cluster/slave/mysql:/var/lib/mysql- /Users/Jermey/Documents/data/db/cluster/slave/conf.d:/etc/mysql/conf.denvironment:MYSQL_ROOT_PASSWORD: youclknetworks:proxy:external: trueyouclk:external: true

再次感激 Docker, 从编排配置文件到最后启动服务整个过程不到一分钟:

接下来就是配置主从关系:

docker exec -it cluster_mysql-master mysql -pCREATE USER 'reader'@'%' IDENTIFIED BY 'youclk';
GRANT REPLICATION SLAVE ON *.* TO 'reader'@'%';show master status\G
docker exec -it cluster_mysql-slave mysql -pCHANGE MASTER TO \
MASTER_HOST='mysql-master',\
MASTER_PORT=3306,\
MASTER_USER='reader',\
MASTER_PASSWORD='youclk',\
MASTER_LOG_FILE='binlog.000004',\
MASTER_LOG_POS=154;start slave;show slave status\G

Test:

上图中左边连的是 Master, 右边为 Slave, 我在 Master 中执行 create database youclk_0; 可以看到 Slave 中也生成了 youclk_0,至此主从配置测试完成。

3.3 读写分离

基于 Sharding-JDBC 的读写分离实现非常简单,改一下配置文件,其余几乎是无感知的,application.yml:

server:port: 8001mybatis:config-location: classpath:mybatis/mybatis-config.xmlmapper-locations: classpath:mybatis/mappers/*.xmlsharding:jdbc:datasource:names: ds_master,ds_slaveds_master:type: org.apache.commons.dbcp.BasicDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://mysql:3301/youclk_0?useSSL=falseusername: rootpassword: youclkds_slave:type: org.apache.commons.dbcp.BasicDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://mysql:3302/youclk_0?useSSL=falseusername: rootpassword: youclkconfig:masterslave:load-balance-algorithm-type: round_robinname: ds_msmaster-data-source-name: ds_masterslave-data-source-names: ds_slavesharding:props:sql.show: true

Test:

@Test
public void selectAndInsertTest() {userService.selectAll();userService.insert();
}

Result:

跟踪 MySQL 的日志可以发现主从库分别执行了插入与查询,实现了读写分离。

结语

帘外雨潺潺,清明念不安,小弟系拙笔,盼君心悦喃(呀,一股子骚气?)。


我的公众号《有刻》,我们共同成长!

转载于:https://www.cnblogs.com/youclk/p/8685666.html

DBA 小记 — 分库分表、主从、读写分离相关推荐

  1. 开源分布式数据库中间件MyCat架构简介(二)——基于MyCat的分库分表,读写分离,水平切分和垂直切分实现原理

    目录 前言 基于MyCat的分库分表,读写分离,水平切分和垂直切分实现原理 一.关于Mycat 二.Mycat 实现原理 三.MyCat 应用场景 四.MyCat 未来展望 五.Mycat 中相关概念 ...

  2. 数据库分库分表、读写分离的原理和实现,以及使用场景

    2019独角兽企业重金招聘Python工程师标准>>> 为什么要分库分表和读写分离? 类似淘宝网这样的网站,海量数据的存储和访问成为了系统设计的瓶颈问题,日益增长的业务数据,无疑对数 ...

  3. 面试精讲之面试考点及大厂真题 - 分布式专栏 16 数据库如何做分库分表,读写分离

    16 数据库如何做分库分表,读写分离 宝剑锋从磨砺出,梅花香自苦寒来. --佚名 引言 2016年第一次接触分布式微服务项目后,我在简历上写了我使用了微服务.分库分表技术,那么问题来了,面试官说接下来 ...

  4. 【Spring Boot 实战】数据库千万级分库分表和读写分离实战

    一. 前言 前几天时间写了如何使用Sharding-JDBC进行分库分表和读写分离的例子,相信能够感受到Sharding-JDBC的强大了,而且使用配置都非常干净.官方支持的功能还很多功能分布式主键. ...

  5. springboott整合mybatis-plus和sharding-jdbc实现分库分表和读写分离(含完整项目代码)

    springboott整合mybatis-plus和sharding-jdbc实现分库分表和读写分离(含完整项目代码) 一.整合sharding-jdbc 关于springboot整合sharding ...

  6. 主从复制MySQL的安装和用数据库中间件MyCat实现分库分表、读写分离

    1.MySql主从复制 1.1.安装mysql 1.1.1.下载 下载地址:https://dev.mysql.com/downloads/mysql/ 1.1.2.卸载预装mysql #查看已安装: ...

  7. 使用 Sharding-Jdbc 实现分库分表、读写分离(未完待续)

    先贴代码, 回头讲解.未完待续 规划 实现分库分表.读写分离. 准备两张表:svc_order, svc_order_item 结构如下,可以想象成是未实施分库分表之前的数据库结构: CREATE D ...

  8. MyCat分库分表和读写分离

    文章目录 1.MyCat 1.1.MyCat简介 1.2.Mycat对多数据库的支持 1.3.MyCAT架构 1.4.MyCat分库分表 1.5.MyCat下载与安装 1.5.1.下载mycat 1. ...

  9. mycat分库分表与读写分离

    mycat分库分表与读写分离 Dockerfile搭建mycat 1.创建mycat的配置文件 #新建目录 mkdir /docker/mycat#切换目录 cd /docker/mycat#下载my ...

  10. 利用Mycat中间件实现RDS MySQL的分库分表及读写分离功能

    https://aws.amazon.com/cn/blogs/china/mycat-rds-mysql/ 随着移动互联网的兴起和大数据的蓬勃发展,系统的数据量正呈几何倍数增长,系统的压力也越来越大 ...

最新文章

  1. .net 常用的插件列表
  2. 安防行业标准规范大全
  3. Vue.js 自定义事件
  4. Calendar的那些神坑
  5. tensorflow学习笔记七----------卷积神经网络
  6. linux下查看cpu信息
  7. 面试机器学习、数据挖掘、大数据岗位时遇到的各种问题
  8. [tensorflow]tensorflow 2.1 函数API(The Functional API)
  9. JavaScript常见封装方法
  10. 将 Android* x86 NDK 供 Eclipse* 而移植 NDK 演示示例应用程序
  11. 陆军装备环境可靠性试验|GJB150A-2009
  12. 让Google chrome支持迅雷
  13. Linux性能测试(UnixBench)(bench)一键脚本
  14. ULC2平台CTA音频调试记
  15. Java实现简单日期计算功能
  16. 白浩然计算机学院,信息数理学院2015年上半年团员推优汇总表.doc-附件:.doc
  17. 哪个软件能代替斐讯路由_斐讯路由(老版本无需登录)
  18. SF1006-ASEMI超快恢复二极管SF1006
  19. VBS奇葩操作 “我是猪”
  20. 如何生成qq邮箱的授权码

热门文章

  1. kodi 媒体库插件_如何使用Kodi管理媒体
  2. spss 描述性分析
  3. 爱快软路由在VMware上安装过程分享,基于多网卡的本机+带多机上网,考研计算机网络实战
  4. 大话functional编程语言
  5. ps快捷键常用表,ps快捷键大全!最全面的PS快捷键使用指南(图文演示)
  6. html anki编辑器,打造你的专属单词本——Anki 初探(无痛入门)
  7. wps页眉偶数页不同怎么设置_WPS文字应技巧—如何使奇数和偶数页的页眉页脚不同...
  8. 算法设计技巧与分析 答案整理
  9. 汉字转拼音源码的两个类
  10. 教程 Re:Zero ROS (五)—— 导入模型,关节控制器