本文来自:DanielLin07的博客《初探分库分表》php
分库分表是企业开发数据存储中很是常见的一项优化工做,但以前一直没有去认真了解过,直到最近接触了一个Spark表日同步千万数据到MySQL表的工做,才对分库分表有了一个初步的认识。 本文就是对此次分库分表初步学习的一个记录总结。html
在数据量较小的时候,数据可能是以单表的形式存储。但随着业务量的扩大存储数据量的增长,单表的操做性能也会大大下降,影响正常的业务工做。 这时就须要考虑使用分库分表,通常而言,在单表数据量达到1000万左右(公司DBA建议)时,就能够考虑使用分库分表。java
用简单的话来讲,垂直切分就是将一个表中涉及的多个字段切分到不一样的表甚至是库中存储。以下图所示:node
咱们经常使用的 数据库三大范式 设计,其实也是一种垂直切分。 另外一种经常使用的垂直切分,则是将热门访问字段与冷门访问字段进行切分,从而让数据库能够以更少的字段缓存更多的行,进而带来性能的提高。mysql
用简单的话来讲,水平切分就是将一个表中存储的数据依照某种策略存储到不一样的表上。以下图所示:git
水平切分的第一种方式就是Range,即根据必定的范围进行分发。 如:根据时间范围,一个月的数据存储一张表,或者是根据用户ID这种自增序列,用户ID在000000至100000范围的存一张表,100001至200000范围的存一张表等。 根据Range分发的好处就是数据扩容时方便。缺点就是容易产生数据热点问题。github
水平切分的第二种方式就是Hash,即经过一次哈希运算而后取余分表数量-1的方式肯定数据要存的表的位置。 如:根据用户姓名进行Hash分发。用户姓名小明,计算hashcode,获得754703,预先肯定分表数量为8,再取余7,获得3,即分发到索引为3的数据表上。 根据Hash分发的好处就是数据分发均匀,不会产生数据热点问题,可是扩容的时候很是不方便,还须要从新计算数据的哈希值。web
ShardingJDBC是ShardingSphere的子项目,在Java的JDBC层提供的额外服务。具体可见ShardingPhere官方文档。spring
现有用户信息须要存储,分别有五个字段:uid、name、mobile、credit_id、create_time。 如今的分库分表策略是:sql
DROP TABLE IF EXISTS `t_user_0`;
CREATE TABLE `t_user_0` (
`uid` int(6) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`credit_id` varchar(16) NOT NULL,
`create_time` datetime(0) NULL,
PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
复制代码
在sharding0db与sharding1db都创建了数据表后,结构以下图所示:
本项目使用的是Spring-Boot 2.0.3.RELEASE
,在项目中导入如下Maven
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
复制代码
在application.yml
中进行配置:
spring:
shardingsphere:
datasource:
names: sharding0db,sharding1db
sharding0db:
type: com.zaxxer.hikari.HikariDataSource
jdbc-url: jdbc:mysql://localhost:3306/sharding0db?useUnicode=true&useSSL=false&useAffectedRows=true&characterEncoding=utf8
driver-class-name: com.mysql.jdbc.Driver
username: root
password:
sharding1db:
type: com.zaxxer.hikari.HikariDataSource
jdbc-url: jdbc:mysql://localhost:3306/sharding1db?useUnicode=true&useSSL=false&useAffectedRows=true&characterEncoding=utf8
driver-class-name: com.mysql.jdbc.Driver
username: root
password:
sharding:
# 分库分表策略
default-database-strategy:
inline:
# 分片的列
sharding-column: uid
# 分片的表达式,groovy语言,这里是对uid进行取余,若是为结果为0则分到sharding0db,结果为1则分到sharding1db
algorithm-expression: sharding$->{uid % 2}db
tables:
t_user:
actual-data-nodes: sharding$->{0..1}db.t_user_$->{0..1}
table-strategy:
inline:
sharding-column: uid
# 分片的表达式,对uid倒数第二位取余,若是为结果为0则分到t_user_0,结果为1则分到t_user_1
algorithm-expression: t_user_$->{uid.intdiv(10) % 2}
# MyBatis配置
mybatis:
# Mapper映射文件的位置
mapper-locations: classpath:mapper/*.xml
# 包下全部类的别名,配置别名为了在对象映射文件中接收参数类型和返回参数类型时省略包路径
type-aliases-package: com.daniellin.demosharding.entity
复制代码
准备UserDAO
文件:
@Mapper
@Repository
public interface UserDAO {
/** * 获取全部用户 * * @return 全部用户 */
List<User> queryList();
/** * 添加新用户 * * @param user 新用户 */
void insert(User user);
}
复制代码
准备UserDAO
的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.daniellin.demosharding.dao.UserDAO">
<resultMap id="UserMapping" type="User">
<id column="uid" property="uid" javaType="int"/>
<result column="name" property="name" javaType="String"/>
<result column="mobile" property="mobile" javaType="long"/>
<result column="credit_id" property="creditId" javaType="long"/>
<result column="create_time" property="createTime"/>
</resultMap>
<!-- 获取全部用户 -->
<select id="queryList" resultMap="UserMapping">
SELECT * FROM t_user
</select>
<!-- 添加新用户 -->
<insert id="insert" keyProperty="uid" parameterType="User" >
INSERT INTO t_user(uid, name, mobile, credit_id, create_time)
VALUES (#{uid},#{name},#{mobile},#{creditId},#{createTime})
</insert>
</mapper>
复制代码
准备User
实体:
@Data
public class User {
private Integer uid;
private String name;
private String mobile;
private String creditId;
private Date createTime;
}
复制代码
编写单元测试插入数据,这里是经过随机生成100个用户的uid进行测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoShardingApplicationTests {
@Autowired
private UserDAO userDAO;
@Test
public void testInsert() {
System.out.println(("----- sharding insert method test ------"));
for (int i = 0; i < 100; i++) {
User userData = new User();
userData.setUid(new Random().nextInt(999999));
userData.setName(UUID.randomUUID().toString().replaceAll("-", ""));
userData.setCreditId("1234567890");
userData.setMobile("1234567890");
userData.setCreateTime(new Date());
userDAO.insert(userData);
}
}
}
复制代码
查看数据结果,能够看到数据已成功插入到指定的数据库表中。 最后一位为奇数,倒数第二位为偶数的,被插入到sharding1db.t_user_0:
最后一位为偶数,倒数第二位为奇数的,被插入到sharding0db.t_user_1:
# | 文章连接 | 做者 |
---|---|---|
1 | sharding:谁都能读懂的分库、分表、分区 | 骏马金龙 |
2 | 一次可贵的分库分表实践 | crossoverjie |
3 | advanced-java | doocs |