前言
1:Mycat是这些年国内最活跃的、彻底开源的数据库中间件(介于数据库与应用之间),Mycat命名主要简单好记,另外希望能入驻Apache,膜拜开源产品Tomcat,也是一只猫。
2:行业内数据库中间件:Atlas+Kingshard-360公司,cobar-阿里巴巴B2B,TDDL-淘宝,heisenberg-百度,Oceanus-58同城,vitess-Youtube,OneProxy-支付宝,MySQL Route-Mysql官方。
本篇博客主要分下面几个章节
第一章:不使用任何数据库中间件如何进行分表操作
第二章:Mycat全局自增主键的几种方式
第三章:分库 + 分片即分实例 + 分表实战
第四章:RULE分表规则函数详解
第五章:基于主从复制的读写分离实战
第六章:SQL关联查询及ER分片设计
第七章:Mycat监控系统介绍及使用
第八章:实战过程中踩过的坑及问题汇总
第九章:以Mycat为代表的数据库中间件优点和缺陷
第一章:不使用任何数据库中间件如何进行分表操作
《1》:分表目的减小数据库的负担,提高查询效率,可分为水平分割 和 垂直分割。
1:水平分割:数据条数可以按ID范围,时间范围等横向存放到N个表中。
优点:降低索引的层数,提高查询速度。
缺点:分页或全量查询时,Union操作会增加索引层读取复杂度,降低查询速度。
2:垂直分割:表字段横向的切分,通过主键字段来建立关系。
优点:数据量分表的同时,查询也减少了I/O次数。
缺点:需管理冗余列字段,查询所有数据需Join操作。
《2》:水平分割实战应用
1:创建表 COST00,COST01,COST02…COST18,COST19总20张表
CREATE TABLE COST00 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST01 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST02 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST03 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST04 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST05 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST06 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST07 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST08 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST09 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST10 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST11 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST12 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST13 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST14 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST15 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST16 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST17 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST18 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE COST19 (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
2:确定分表因子及方式,id或time等,hash或range等规则,获取表名进行CRUD操作。
/**
* 根据id取模获取表名,进行查询、删除、保存、修改操作。
*
* @param table 表名
* @param id 分表因子
* @return
*/
public static String tableName(String table, String id){
int mod = Integer.parseInt(id) % 20;
String tableName = table;
if (mod > -1) {
if (mod < 10) {
tableName = tableName.concat("0").concat(String.valueOf(mod));
} else {
tableName = tableName.concat(String.valueOf(mod));
}
}
return tableName;
}
3:查询全部数据时有下面两种方式解决
方式一:利用Mysql的Merge存储引擎来实现,但对创建的主表和N个分表有要求。
CREATE TABLE COST_ALL (id bigint(18) NOT NULL AUTO_INCREMENT,name varchar(100) DEFAULT NULL, PRIMARY KEY (id)) ENGINE = MERGE UNION=(COST00,COST01,COST02,COST03,COST04,COST05,COST06,COST07,COST08,COST09,COST10,COST11,COST12,COST13,COST14,COST15,COST16,COST17,COST18,COST19) INSERT_METHOD = LAST AUTO_INCREMENT=1;
Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
当遇到上面错误时候,通过下面建议去排查
a:MERGE引擎仅适用于MYISAM表
b:union了不存在的表
c:MERGE的时候引用了不在同一个数据库中的表,并且该表没有指定数据库名字
d:各个表的结构(索引、引擎、列、字符集等)不完全一致
#通过查询主表进行全数据操作。
select * from COST_ALL;
方式二:业务代码逻辑处理如下
// service层中实现如下逻辑
public List<String> listCost() {
List<String> listAll = new ArrayList<>();
for (int i = 0; i < 20; i++) {
List<String> subList = dao.listAll(String.valueOf(i));
if (!CollectionUtils.isEmpty(subList)) {
listAll.addAll(subList);
}
}
return listAll;
}
//dao层中实现如下逻辑
List<String> listAll(@Param(value = "tableNum") String tableNum);
<!-- 如果业务中你使用mybatis,则在mapping.xml中添加下面代码 -->
<select id="listCost" resultType="java.lang.String" parameterType="java.lang.String">
SELECT * FROM
<include refid="table_name"></include>
</select>
<sql id="table_name">
<choose>
<when test="tableNum!= null and tableNum=='0'.toString()">
COST00
</when>
<when test="tableNum!= null and tableNum=='1'.toString()">
COST01
</when>
<when test="tableNum!= null and tableNum=='2'.toString()">
COST02
</when>
<when test="tableNum!= null and tableNum=='3'.toString()">
COST03
</when>
<when test="tableNum!= null and tableNum=='4'.toString()">
COST04
</when>
<when test="tableNum!= null and tableNum=='5'.toString()">
COST05
</when>
<when test="tableNum!= null and tableNum=='6'.toString()">
COST06
</when>
<when test="tableNum!= null and tableNum==&