本文來自: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 |