MySql系列- 分库分表原则 & 哈希取模算法实现 &主从复制原理
分表分库
垂直拆分
垂直拆分就是要把表按模块划分到不同数据库表中(当然原则还是不破坏第三范式),这种拆分在大型网站的演变过程中是很常见的。当一个网站还在很小的时候,只有小量的人来开发和维护,各模块和表都在一起,当网站不断丰富和壮大的时候,也会变成多个子系统来支撑,这时就有按模块和功能把表划分出来的需求。其实,相对于垂直切分更进一步的是服务化改造,说得简单就是要把原来强耦合的系统拆分成多个弱耦合的服务,通过服务间的调用来满足业务需求看,因此表拆出来后要通过服务的形式暴露出去,而不是直接调用不同模块的表,淘宝在架构不断演变过程,最重要的一环就是服务化改造,把用户、交易、店铺、宝贝这些核心的概念抽取成独立的服务,也非常有利于进行局部的优化和治理,保障核心模块的稳定性垂直拆分用于分布式场景。
水平拆分
上面谈到垂直切分只是把表按模块划分到不同数据库,但没有解决单表大数据量的问题,而水平切分就是要把一个表按照某种规则把数据划分到不同表或数据库里。例如像计费系统,通过按时间来划分表就比较合适,因为系统都是处理某一时间段的数据。而像SaaS应用,通过按用户维度来划分数据比较合适,因为用户与用户之间的隔离的,一般不存在处理多个用户数据的情况,简单的按user_id范围来水平切分通俗理解:水平拆分行,行数据拆分到不同表中, 垂直拆分列,表数据拆分到不同表中
下面我们来做一个根据哈希取模的方式进行分表
-- 新建三张表
create table user0(
id int unsigned primary key,
name varchar (32) not null default '',
pwd varchar (32) not null default '')
engine=myisam charset utf8;
create table user1 (
id int unsigned primary key,
name varchar (32) not null default '',
pwd varchar (32) not null default '')
engine=myisam charset utf8;
create table user2 (
id int unsigned primary key,
name varchar (32) not null default '',
pwd varchar (32) not null default '')
engine=myisam charset utf8;
-- 创建uuid表,该表的作用就是提供自增的id。
create table uuid (
id int unsigned primary key auto_increment)
engine=myisam charset utf8;
新建一个springboot项目
在这里我就用原生的jdbcTemplate来写了
package com.df.strantion.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
/**
* @author 神域~
* @version 1.0
* @date 2020/6/14 14:02
*/
@Slf4j
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public String insertUser(String userName,String password){
//1. 获取到自增的ID
String idInsertSQL = "INSERT INTO uuid VALUES (NULL);";
jdbcTemplate.update(idInsertSQL);
Long aLong = jdbcTemplate.queryForObject("select last_insert_id()", Long.class);
//2. 判断存储表的名称
String tableName = "user"+aLong%3;
//3.注册数据
String insertUserSql = "INSERT INTO " + tableName + " VALUES ('" + aLong + "','" + userName + "','" + password+ "');";
System.out.println("insertUserSql:" + insertUserSql);
//4.持久化
jdbcTemplate.update(insertUserSql);
return tableName;
}
public String get(Long aLong){
//判断所在库表
String tableName = "user"+aLong%3;
String sql = "SELECT name FROM "+tableName+" where id ="+aLong;
String name = jdbcTemplate.queryForObject(sql, String.class);
return name;
}
}
其实可以看到 当我们对表进行取余数的时候就是为了保证能使数据能均匀的分布在各表中。3就是指我们分了多少个表。
接下来我们运行一下
可以看到我们第一次添加 id值是1的时候 1%3取余 余数是1
接着我们再运行一次 初步预期值 2%3取余 余数是2
再运行一次 预期值 3%3取余 余数是0
主从复制
概念
影响MySQL-A数据库的操作,在数据库执行后,都会写入本地的日志系统A
中。假设,实时的将变化了的日志系统中的数据库事件操作,在MYSQL-A的
3306端口,通过网络发给MYSQL-B。
MYSQL-B收到后,写入本地日志系统B,然后一条条的将数据库事件在数据库中完成。那么,MYSQL-A的变化,MYSQL-B也会变化,这样就是所谓的MYSQL的复制,即MYSQLreplication。在上面的模型中,MYSQL-A就是主服务器,即master,MYSQL-B就是从服务器,即slave。
日志系统
A,其实它是MYSQL的日志类型中的二进制日志,也就是专门用来保存修改数据库表的所有动作,即bin log。【注意MYSQL会在执行语句之后,释放锁之前,写入二进制日志,确保事务安全】日志系统
B,并不是二进制日志,由于它是从MYSQL-A的二进制日志复制过来的,并不是自己的数据库变化产生的,有点接力的感觉,称为中继日志,即relay log。可以发现,通过上面的机制,可以保证MYSQL-A和MYSQL-B的数据库数据一致,但是时间上肯定有延迟,即MYSQL-B的数据是滞后的。
解决问题
数据如何不被丢失
备份
读写分离
数据库负载均衡
高可用
下一篇再来说下具体的步骤