文档章节

Mysql 表分区

JasonO
 JasonO
发布于 2016/01/28 17:29
字数 1726
阅读 109
收藏 5

 对 于一个刚上线的互联网项目来说,由于前期活跃用户数量并不多,并发量也相对较小,所以此时企业一般都会选择将所有数据存放在一个数据库中进行访问操作。但 随着后续的市场推广力度不断加强,用户数量和并发量不断上升,这时如果仅靠一个数据库来支撑所有访问压力,几乎是在自寻死路。所以一旦到了这个阶段,大部 分Mysql DBA就会将数据库设置成读写分离状态,也就是一个Master节点对应多个Salve节点。经过Master/Salve模式的设计后,完全可以应付单 一数据库无法承受的负载压力,并将访问操作分摊至多个Salve节点上,实现真正意义上的读写分离。但大家有没有想过,单一的Master/Salve模 式又能抗得了多久呢?如果用户数量和并发量出现量级上升,单一的Master/Salve模式照样抗不了多久,毕竟一个Master节点的负载还是相对比 较高的。为了解决这个难题,Mysql DBA会在单一的Master/Salve模式的基础之上进行数据库的垂直分区(分库)。所谓垂直分区指的是可以根据业务自身的不同,将原本冗余在一个数 据库内的业务表拆散,将数据分别存储在不同的数据库中,同时仍然保持Master/Salve模式。经过垂直分区后的Master/Salve模式完全可 以承受住难以想象的高并发访问操作,但是否可以永远高枕无忧了?答案是否定的,一旦业务表中的数据量大了,从维护和性能角度来看,无论是任何的CRUD操 作,对于数据库而言都是一件极其耗费资源的事情。即便设置了索引,仍然无法掩盖因为数据量过大从而导致的数据库性能下降的事实,因此这个时候Mysql DBA或许就该对数据库进行水平分区(分表,sharding),所谓水平分区指的是将一个业务表拆分成多个子表,比如user_table0、 user_table1、user_table2。子表之间通过某种契约关联在一起,每一张子表均按段位进行数据存储,比如user_table0存储 1-10000的数据,而user_table1存储10001-20000的数据,最后user_table3存储20001-30000的数据。经过 水平分区设置后的业务表,必然能够将原本一张表维护的海量数据分配给N个子表进行存储和维护,这样的设计在国内一流的互联网企业比较常见,如图所示:

 

水平分区

 

       以上是数据库分库分表的原理,但是如果每次在数据访问层的设计中实现分库分表,将会显得非常麻烦。笔者参考了了下code google shardbatis的设计方式,发现googlecode上的shardbatis无法做到分库,只能做到分表,而且配置相当不灵活,源码很难找到。所 以笔者重新设计了一套mybatis插件,相比google code的那套,该框架可以做到完整的分库分表和友好的配置。组件结构如图所示:

组件结构图

 

使用方法:

1.准备数据库集群配置文件

 

 

Xml代码  收藏代码

 

<?xml version="1.0" encoding="UTF-8"?>  
<databases xmlns="http://fangjialong.iteye/schema/snsprod"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://www.vivo.com.cn/schema/snsprod  
    http://fangjialong.iteye/schema/snsprod  
    http://fangjialong.iteye/schema/snsprod/shardbatis-db.xsd">  
    <!-- 全局配置 -->  
    <logicName value="test"/>  
    <configs>  
        <property name="minPoolSize" value="4" />  
        <property name="minPoolSize" value="8" />  
        <property name="driverClass" value="com.mysql.jdbc.Driver" />  
        <property name="maxIdleTime" value="900" />  
        <property name="idleConnectionTestPeriod" value="1800" />  
    </configs>  
    <database suffix="_00" username="root" password=""  
        jdbcUrl="jdbc:mysql://localhost:3306/test_00?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull">  
        <property name="minPoolSize" value="4" />  
        <property name="minPoolSize" value="8" />  
        <property name="driverClass" value="com.mysql.jdbc.Driver" />  
        <property name="maxIdleTime" value="900" />  
        <property name="idleConnectionTestPeriod" value="1800" />  
    </database>  
    <database suffix="_01" username="root" password=""  
        jdbcUrl="jdbc:mysql://localhost:3306/test_01?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull" />  
</databases>


 

2.准备分库分表规则配置

 

Xml代码  收藏代码

 

<configs xmlns="http://fangjialong.iteye.com/schema/snsprod"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://fangjialong.iteye.com/schema/snsprod  
    http://fangjialong.iteye.com/schema/snsprod  
    http://fangjialong.iteye.com/schema/snsprod/shardbatis-config.xsd">  
      
    <strategy logicTable="t_student" class="com.cannon.prod.dal.strategy.StudentShardStrategy"/>  
</configs>


3.为mybatis配置扩展插件

Xml代码  收藏代码

 

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  
    <typeAliases>  
        <typeAlias alias="Student" type="com.cannon.prod.dal.model.Student" />  
    </typeAliases>  
    <plugins>  
        <plugin interceptor="com.cannon.prod.dal.shardbatis.ShardPlugin">  
            <property name="configsLocation" value="META-INF/mybatis/shardbatis-config.xml" />  
        </plugin>  
    </plugins>  
    <mappers>  
        <mapper resource="META-INF/mybatis/mapper/mybatis-mapper-student.xml" />  
    </mappers>  
</configuration>


4.为mybatis配置分库分表数据源

Xml代码  收藏代码

<?xml version="1.0" encoding="UTF-8"?>  
  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">  
  
    <!-- 配置数据源 -->  
    <bean id="distributeDefaultDataSource" class="com.cannon.prod.dal.shardbatis.ShardDataSource"  
        init-method="init" destroy-method="destroy">  
        <property name="configsLocation" value="META-INF/database/distributed-default-db.xml"></property>  
    </bean>  
  
    <bean id="distributeDefaultTransactionManager"  
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="distributeDefaultDataSource" />  
    </bean>  
  
    <bean id="distributeDefaultTransactionTemplate"  
        class="org.springframework.transaction.support.TransactionTemplate">  
        <property name="transactionManager" ref="distributeDefaultTransactionManager" />  
    </bean>  
  
    <bean id="distributeDefaultSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="configLocation">  
            <bean class="org.springframework.core.io.ClassPathResource">  
                <constructor-arg index="0"  
                    value="META-INF/mybatis/mybatis-config.xml" />  
            </bean>  
        </property>  
        <property name="dataSource" ref="distributeDefaultDataSource" />  
    </bean>  
  
</beans>

 

5.使用逻辑表配置mybatis mapper

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.cannon.prod.dal.mapper.Student">  
    <resultMap type="Student" id="StudentResultMap">  
        <id property="no" column="no" />  
        <result property="name" column="name" />  
        <result property="sex" column="sex" />  
    </resultMap>  
    <!-- 查询学生,根据id -->  
    <select id="getByNo" parameterType="String" resultType="Student"  
        resultMap="StudentResultMap">    
        <![CDATA[  
            SELECT * FROM `t_student` WHERE `no` = #{no}   
        ]]>  
    </select>  
    <delete id="deleteByNo" parameterType="String">  
        <![CDATA[  
            DELETE FROM `t_student` WHERE `no` = #{no}   
        ]]>  
    </delete>  
    <insert id="create" parameterType="Student">  
        INSERT INTO t_student(no, name, sex) VALUES(#{no}, #{name}, #{sex})  
    </insert>  
    <update id="update" parameterType="Student">  
        UPDATE t_student  
        <trim prefix="SET" suffixOverrides=",">  
            <if test="name != null">name=#{name},</if>  
        </trim>  
        WHERE no=#{no}  
    </update>  
</mapper>

 

 

6.编写分库分表规则

Java代码  收藏代码

public class StudentShardStrategy implements ShardStrategy {  
    @Override  
    public ShardCondition parse(Map<String, Object> params) {  
        String no = (String) params.get("no");  
        char c = no.charAt(0);  
        ShardCondition condition = new ShardCondition();  
        if (c == '1') {  
            condition.setDatabaseSuffix("_01");  
            condition.setTableSuffix("_01");  
        } else {  
            condition.setDatabaseSuffix("_00");  
            condition.setTableSuffix("_00");  
        }  
        return condition;  
    }  
}


 

 

全部配置好了之后就可以按照以前使用mybatis的方式将数据库集群当做一个逻辑表来操作了。剩余的转换操作就交给中间扩展层插件来转换吧。

以下是转换结果:

Shard Original SQL:SELECT * FROM `t_student` WHERE `no` = ?

 

Shard Convert SQL:SELECT * FROM `test_00`.`t_student_00` WHERE `no` = ?

 

Shard Original SQL:DELETE FROM `t_student` WHERE `no` = ?

 

Shard Convert SQL:DELETE FROM `test_00`.`t_student_00` WHERE `no` = ?

 

Shard Original SQL:INSERT INTO t_student(no, name, sex) VALUES(?, ?, ?)

 

Shard Convert SQL:INSERT INTO test_00.t_student_00 (no, name, sex) VALUES (?, ?, ?)

 

Shard Original SQL:UPDATE t_student SET name=?  WHERE no=?

 

Shard Convert SQL:UPDATE test_00.t_student_00 SET name = ? WHERE no = ?

 

注意:分库分表从原则上是不支持跨库事物的,如果需要使用事务必须保证在多个表在同一个库中。

以下是事物支持的测试用例:

 

Java代码  收藏代码

/** 
 * @author fangjialong 
 * @date 2015年9月5日 下午4:27:50 
 */  
public class StudentDAOTransactionTest {  
    private DalSpringContext context;  
    private StudentDAO studentDAO;  
    private TransactionTemplate tt;  
    @Test  
    public void test() {  
        LogFactory.useNoLogging();  
        this.context = new DalSpringContext();  
        this.context.refresh();  
        this.studentDAO = context.getBean(StudentDAO.class);  
        this.tt = context.getBean("distributeDefaultTransactionTemplate", TransactionTemplate.class);  
        tt.execute(new TransactionCallbackWithoutResult() {  
            @Override  
            protected void doInTransactionWithoutResult(TransactionStatus status) {  
                Student s = new Student();  
                s.setNo("0001");  
                s.setName("房佳龙");  
                studentDAO.update(s);  
                status.setRollbackOnly();  
            }  
        });  
        context.close();  
    }  
}


本文转载自:http://fangjialong.iteye.com/blog/2240880

上一篇: nginx url重写
JasonO
粉丝 7
博文 30
码字总数 6403
作品 0
深圳
高级程序员
私信 提问
MySQL下的表分区简述

MySQL表分区就是把一张表根据设定好的条件下把表切分成若干个小表相互之间,在MySQL的5.1版本以后就开始支持表分区的功能,在使用表的分区后会使MySQL中大表在平时查询统计时性能提升。使用M...

往事_Jim_遗
2017/11/10
0
0
MySQL学习——分区表

MySQL存储引擎现状及发展趋势 MySQL官网有这样一句话: As of MySQL 5.7.17, the generic partitioning handler in the MySQL server is deprecated, and is removed in MySQL 8.0, when th......

沈渊
2017/09/03
0
0
MySQL表分区优势及分类简析 MySQL DBA学习

表分区是将一个表的数据按照一定规则水平划分成不同的逻辑块,并分别进行物理存储,这个规则就叫做分区函数,可以有不同的分区规则。通过show plugins语句查看当前MySQL是否支持表分区功能。...

mo默瑶
2018/05/24
0
0
MySQL表分区类型简析 数据库周末提升

  表分区是将一个表的数据按照一定规则水平划分成不同的逻辑块,并分别进行物理存储,这个规则就叫做分区函数,可以有不同的分区规则。通过show plugins语句查看当前MySQL是否支持表分区功...

zhouzhou2018
2018/05/24
0
0
mysql分表,分区的区别和联系

一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看mysql分表的3种方法 什么是分区,分区呢就是把一张表的数据分成N多个区块,这些区块可以在同...

haorizi
2013/03/11
0
1

没有更多内容

加载失败,请刷新页面

加载更多

如何使用 rsync 备份 Linux 系统的一些介绍

备份一直是 Linux 世界的热门话题。回到 2017,David Both 为 Opensource.com 的读者在使用 rsync 备份 Linux 系统方面提了一些建议,在这年的更早时候,他发起了一项问卷调查询问大家,在 ...

xiangyunyan
27分钟前
0
0
二进制位操作

单片机,或者一些模块的设置操作,都是由一个字节数据来完成,每位各有定义。就需进行位操作来组合需要的数字结果。 以JavaScript为例,编写位操作。 我们期望得到这样一个二进制数:0101101...

format
40分钟前
3
0
聊聊中国的通信行业:从“七国八制”到“中华”脊梁

本期文章和大家一起来聊一聊我曾经从事过的通信行业吧。最近各方面信息的泛滥,包括和华为的同学聊天,自己确实也感慨颇多。想想我自己本科主修通信工程,研究生再修信息与通信工程,从本科开...

CodeSheep
今天
7
0
MDK:ARM M451M:exceed the range of code meory, continue to erase or not?

问题: 代码空间超限 几天前就遇到:exceed the range of code meory, continue to erase or not? 如下所示: 解决过程 开始以为中MDK软件的128KB限制,如是就不能生成HEX文件,应该链接时有提...

SamXIAO
今天
1
1
OSChina 周六乱弹 —— 因违反《中华人民共和国治安管理处罚法》第四十四条之规定

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :#今日歌曲推荐# 惊艳分享谷微的单曲《安守本份》(@网易云音乐) 《安守本份》- 谷微 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
628
13

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部