DBA 小记 — 分库分表、主从、读写分离
前言
我在上篇博客 “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: 8001
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mappers/*.xml
sharding:
jdbc:
datasource:
names: youclk_0,youclk_1
youclk_0:
type: org.apache.commons.dbcp.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://mysql:3306/youclk_0?useSSL=false
username: root
password: youclk
youclk_1:
type: org.apache.commons.dbcp.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://mysql:3306/youclk_1?useSSL=false
username: root
password: youclk
config:
sharding:
default-database-strategy:
inline:
sharding-column: number
algorithm-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 {
@Resource
private 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 还提供了最大努力送达型柔性事务(将执行过程记录到日志中,失败重试,成功后删除,若最终还是失败则保留事务日志,供人工干预),虽然安全性更高,但无法保证时效,限制也很多,这里留个待续吧,后续有空再深入探讨(主要是比较晚了,想早点写完休息
DBA 小记 — 分库分表、主从、读写分离的更多相关文章
- 阿里P8架构师谈:数据库分库分表、读写分离的原理实现,使用场景
本文转载自:阿里P8架构师谈:数据库分库分表.读写分离的原理实现,使用场景 为什么要分库分表和读写分离? 类似淘宝网这样的网站,海量数据的存储和访问成为了系统设计的瓶颈问题,日益增长的业务数据,无疑对 ...
- Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离
结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表.读写分离. 一.Sharding-JDBC介绍 1.这里引用官网上的介 ...
- 使用ShardingSphere-JDBC完成Mysql的分库分表和读写分离
1. 概述 老话说的好:选择比努力更重要,如果选错了道路,就很难成功. 言归正传,之前我们聊了使用 MyCat 实现Mysql的分库分表和读写分离,MyCat是服务端的代理,使用MyCat的好处显而易 ...
- Mycat使用--分库分表和读写分离
Mycat分库分表读写分离 1. 模拟多数据库节点 2. 配置文件 具体操作参看: https://blog.csdn.net/vbirdbest/article/details/83448757 写 ...
- ShardingSphere-proxy-5.0.0企业级分库分表、读写分离、负载均衡、雪花算法、取模算法整合(八)
一.简要说明 以下配置实现了: 1.分库分表 2.每一个分库的读写分离 3.读库负载均衡算法 4.雪花算法,生成唯一id 5.字段取模 二.配置项 # # Licensed to the Apache ...
- 分库分表、读写分离——用Sql和ORM(EF)来实现
分库:将海量数据分成多个库保存,比如:2017年的订单库——Order2017,2018年的订单库——Order2018... 分表:水平分表(Order拆成Order1.....12).垂直分表(O ...
- 基于ShardingJDBC的分库分表及读写分离整理
ShardingJDBC的核心流程主要分成六个步骤,分别是:SQL解析->SQL优化->SQL路由->SQL改写->SQL执行->结果归并,流程图如下: sharding ...
- go分库分表 主从分离例子
网上有很多介绍分库分表的文章,方法很多: 分区表切分 垂直切分 水平切分 区间切分 取模切分 这里不细说 分库分表简单,但后期会带来一系列的难题: 事务 Join 分页 数据库: master和sla ...
- sharing-jdbc实现读写分离及分库分表
需求: 分库:按业务线business_id将不同业务线的订单存储在不同的数据库上: 分表:按user_id字段将不同用户的订单存储在不同的表上,为方便直接用非分片字段order_id查询,可使用基因 ...
随机推荐
- djang-tastypie学习整理
quick start: 配置环境(pip install: Python 2.7+ or Python 3.4+ Django 1.8+ python-mimeparse 0.1.4+ (http: ...
- XAMPP搭建PHP
在学习一些前后端交互时,经常会有跟PHP作为后端(服务器)的交互,不能将php文件放在本地进行请求,必须将PHP运行在Apache环境中.但是对一些新手来说,学习搭建一个Apache环境也并非易事,所 ...
- JavaScript 变量屏蔽
不同作用域中相同名称的变量就会触发变量屏蔽: { let x = {color:"blue"}; let y = x; let z = 3; { //重复定义x,所以对全局变量x进 ...
- 设计模式——状态模式(C++实现)
/////////context.cpp #include "context.h" void STContext::ChangeState(STState* pstState) { ...
- 线程池ThreadPoolExecutor源码解读研究(JDK1.8)
一.什么是线程池 为什么要使用线程池?在多线程并发开发中,线程的数量较多,且每个线程执行一定的时间后就结束了,下一个线程任务到来还需要重新创建线程,这样线程数量特别庞大的时候,频繁的创建线程和销毁线程 ...
- PHP中文关键词匹配
关键词匹配是比较常见的需求,如留言.弹幕及游戏聊天中的敏感词过滤,都需要对一段文字进行关键词匹配.提取到关键词后,再做进一步处理. 本类借助PHP高效的数组和mbstring扩展,来实现对中文关键词的 ...
- Elasticsearch就这么简单
一.前言 最近有点想弄一个站内搜索的功能,之前学过了Lucene,后来又听过Solr这个名词.接着在了解全文搜索的时候就发现了Elasticsearch这个,他也是以Lucene为基础的. 我去搜了几 ...
- 边做边学入门微信小程序之仿豆瓣评分
微信小程序由于适用性强.逻辑简要.开发迅速的特性,叠加具有海量活跃用户的腾讯公司背景,逐渐成为了轻量级单一功能应用场景的较佳承载方式,诸如电影购票.外卖点餐.移动商城.生活服务等场景服务提供商迅速切入 ...
- struts2基于注解配置action
如果使用struts2,就需要配置文件或者注解,关于struts2的配置文件struts.xml非常熟悉,对于注解可能spring使用的比较多.配置文件的繁琐衬托出了注解的简洁方便,一条或者几条注解解 ...
- webpack安装入门
这两天心血来潮,突然想研究下webpack,花了一天时间,看网上有很多教程,不过发现安装的都是比较老的版本.后来发现还是我太年轻了,找技术播客还不如找官方网站.下面是一个webpack的官方网站,里面 ...