这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
背景
随着时间和业务的发展,数据库中的数据量增长是不可控的,库和表的数据会越来越大,随之带来的是更高的资源消耗,有磁盘,IO,系统等开销,会出现性能上的瓶颈,一台服务器的资源终究是有限的,因此需要连接到不同的服务器,对数据库和表进行拆分,从而更好的提供数据服务。
一般来说,当一张表的数据达到千万级别,在做很多操作的时候会很吃力,因此当数据增长到1000万以上就需要分库分表来缓解单库单表的压力。
分库分表
将一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
垂直切分
按照不同的表(或者Schema)来切分到不同的数据库(主机上面),这种方式称之为垂直(纵向)切分。
垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低,相互影响很小,业务逻辑非常清晰的系统,在这种系统中,可以很容易的做到将不同业务模块所使用的表分拆到不同的数据库中,根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。
优点:
- 拆分后业务清晰,拆分规则明确
- 系统之间整和或扩展容易
- 数据维护简单
缺点:
- 部分业务无法join,只能通过接口方式解决,提高了系统复杂度
- 每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高
- 事务处理复杂
水平切分
根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种拆分称为数据库的水平(横向)切分。
水平切分于垂直切分相比,相对来说稍微复杂一些,因为要将同一个表中的不同数据拆分到不同的数据库中,对于不同的应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后期的数据维护也会更为复杂一些。
优点:
- 拆分规则抽象号,join操作基本可以数据库做
- 不存在单库大数据,高并发的性能瓶颈
- 应用端改造较少
- 提高了系统的稳定性跟负载能力
缺点:
- 拆分规则难以抽象
- 分片事务一致性难以解决
- 数据多次扩展难度跟维护量极大
- 跨库join性能较差
使用springboot+mybatis实现分库分表操作
首先再不同的数据库中分别创建user表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` int(25) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
创建实体类
@Data
public class User {
private int id;
private String username;
private int password;
}
创建dao,分开创建
@Mapper
public interface User1Dao {
List<User> getUser();
User getUserById(Integer id);
void update(User user);
void deleteUser(Integer id);
User queryByName(String name);
}
@Mapper
public interface User2Dao {
List<User> getUser();
}
创建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.sense.dao.mapper1.User1Dao">
<select id="getUser" resultType="com.sense.dingtalkdemo.entity.User">
select *
from user
</select>
</mapper>
<?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.sense.dao.mapper2.User2Dao" >
<select id="getUser" resultType="com.sense.dingtalkdemo.entity.User">
select * from user
</select>
</mapper>
这里为了方便,没有创建service,有需要的可自行创建
配置application.yml文件
spring:
datasource:
one:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost1:3306/monitor?useUnicode=true&useSSL=true
username: root
password: root
max-idle: 10
min-idle: 10000
initial-size: 5
two:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost2:3306/monitor?useUnicode=true&useSSL=true
username: root
password: root
max-idle: 10
min-idle: 10000
initial-size: 5
创建conf,@Primary表示默认使用的数据源
@Configuration
public class DataSourceConfig {
@Bean("dbOne")
@ConfigurationProperties(prefix = "spring.datasource.one")
@Primary
DataSource dbOne() {
return DataSourceBuilder.create().build();
}
@Bean("dbTwo")
@ConfigurationProperties(prefix = "spring.datasource.two")
@Primary
DataSource dbTwo() {
return DataSourceBuilder.create().build();
}
}
@Configuration
@MapperScan(basePackages = "com.sense.dingtalkdemo.dao.mapper1", sqlSessionFactoryRef = "sqlSessionFactory1", sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MybatisConfigOne {
@Resource(name = "dbOne")
DataSource dbOne;
@Bean
@Primary
SqlSessionFactory sqlSessionFactory1() throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dbOne);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper1/*.xml"));
return bean.getObject();
}
@Bean
@Primary
SqlSessionTemplate sqlSessionTemplate1() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory1());
}
}
@Configuration
@MapperScan(basePackages = "com.sense.dingtalkdemo.dao.mapper2", sqlSessionFactoryRef = "sqlSessionFactory2", sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MybatisConfigTwo {
@Resource(name = "dbTwo")
DataSource dbTwo;
@Bean
SqlSessionFactory sqlSessionFactory2() throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dbTwo);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper2/*.xml"));
return bean.getObject();
}
@Bean
SqlSessionTemplate sqlSessionTemplate2() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory2());
}
}
controller层,使用postman测试即可
@RestController
@RequestMapping("/get")
public class UserController {
@Resource
private User1Dao user1Dao;
@Resource
private UserService userService;
@Resource
private com.sense.dingtalkdemo.dao.mapper2.User2Dao user2Dao;
@RequestMapping("user1")
public String getUser1() {
return user1Dao.getUser().toString();
}
@RequestMapping("user2")
public String getUser2() {
return user2Dao.getUser().toString();
}
}