一、schema属性介绍
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="">
<table name="tab" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user1" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"
nameSuffix="" primaryKey="" autoIncrement="" needAddLimit="" type="" ruleRequired="" subTables=""/>
</schema>
primaryKey记录主键,用于之后路由分析,以及启用自增长主键
nameSuffix路由, 增加对动态日期表的支持
autoIncrement记录是否主键自增,默认不是,(启用全局sequence handler)
needAddLimit记录是否需要加返回结果集限制,默认需要加
type记录type,是否为global
ruleRequired记录是否绑定有分片规则
subTables分表功能
二、源码解读
private void load(String dtdFile, String xmlFile) { InputStream dtd = null; InputStream xml = null; dtd = XMLSchemaLoader.class.getResourceAsStream(dtdFile); xml = XMLSchemaLoader.class.getResourceAsStream(xmlFile); Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement(); //先加载所有的DataHost loadDataHosts(root); //再加载所有的DataNode loadDataNodes(root); //最后加载所有的Schema loadSchemas(root); } <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode=""> <table name="tab" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" /> <table name="user1" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" /> <table name="user" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" /> </schema> private void loadSchemas(Element root) { NodeList list = root.getElementsByTagName("schema"); for (int i = 0, n = list.getLength(); i < n; i++) { Element schemaElement = (Element) list.item(i); //读取各个属性 String name = schemaElement.getAttribute("name"); String dataNode = schemaElement.getAttribute("dataNode"); String checkSQLSchemaStr = schemaElement.getAttribute("checkSQLschema"); String sqlMaxLimitStr = schemaElement.getAttribute("sqlMaxLimit"); int sqlMaxLimit = -1; //读取sql返回结果集限制 if (sqlMaxLimitStr != null && !sqlMaxLimitStr.isEmpty()) { sqlMaxLimit = Integer.parseInt(sqlMaxLimitStr); } // check dataNode already exists or not,看schema标签中是否有datanode String defaultDbType = null; //校验检查并添加dataNode if (dataNode != null && !dataNode.isEmpty()) { List dataNodeLst = new ArrayList(1); dataNodeLst.add(dataNode); checkDataNodeExists(dataNodeLst); String dataHost = dataNodes.get(dataNode).getDataHost(); defaultDbType = dataHosts.get(dataHost).getDbType(); } else { dataNode = null; } //加载schema下所有tables Map<String, TableConfig> tables = loadTables(schemaElement); //判断schema是否重复 if (schemas.containsKey(name)) { throw new ConfigException("schema " + name + " duplicated!"); } // 设置了table的不需要设置dataNode属性,没有设置table的必须设置dataNode属性 if (dataNode == null && tables.size() == 0) { throw new ConfigException( "schema " + name + " didn't config tables,so you must set dataNode property!"); } SchemaConfig schemaConfig = new SchemaConfig(name, dataNode, tables, sqlMaxLimit, "true".equalsIgnoreCase(checkSQLSchemaStr)); //设定DB类型,这对之后的sql语句路由解析有帮助 if (defaultDbType != null) { schemaConfig.setDefaultDataNodeDbType(defaultDbType); if (!"mysql".equalsIgnoreCase(defaultDbType)) { schemaConfig.setNeedSupportMultiDBType(true); } } // 判断是否有不是mysql的数据库类型,方便解析判断是否启用多数据库分页语法解析 for (TableConfig tableConfig : tables.values()) { if (isHasMultiDbType(tableConfig)) { schemaConfig.setNeedSupportMultiDBType(true); break; } } //记录每种dataNode的DB类型 Map<String, String> dataNodeDbTypeMap = new HashMap<>(); for (String dataNodeName : dataNodes.keySet()) { DataNodeConfig dataNodeConfig = dataNodes.get(dataNodeName); String dataHost = dataNodeConfig.getDataHost(); DataHostConfig dataHostConfig = dataHosts.get(dataHost); if (dataHostConfig != null) { String dbType = dataHostConfig.getDbType(); dataNodeDbTypeMap.put(dataNodeName, dbType); } } schemaConfig.setDataNodeDbTypeMap(dataNodeDbTypeMap); schemas.put(name, schemaConfig); } }
private Map<String, TableConfig> loadTables(Element node) { // Map<String, TableConfig> tables = new HashMap<String, TableConfig>(); // 支持表名中包含引号[`] BEN GONG Map<String, TableConfig> tables = new TableConfigMap(); NodeList nodeList = node.getElementsByTagName("table"); for (int i = 0; i < nodeList.getLength(); i++) { Element tableElement = (Element) nodeList.item(i); String tableNameElement = tableElement.getAttribute("name").toUpperCase(); //TODO:路由, 增加对动态日期表的支持 String tableNameSuffixElement = tableElement.getAttribute("nameSuffix").toUpperCase(); if ( !"".equals( tableNameSuffixElement ) ) { if( tableNameElement.split(",").length > 1 ) { throw new ConfigException("nameSuffix " + tableNameSuffixElement + ", require name parameter cannot multiple breaks!"); } //前缀用来标明日期格式 tableNameElement = doTableNameSuffix(tableNameElement, tableNameSuffixElement); } //记录主键,用于之后路由分析,以及启用自增长主键 String[] tableNames = tableNameElement.split(","); String primaryKey = tableElement.hasAttribute("primaryKey") ? tableElement.getAttribute("primaryKey").toUpperCase() : null; //记录是否主键自增,默认不是,(启用全局sequence handler) boolean autoIncrement = false; if (tableElement.hasAttribute("autoIncrement")) { autoIncrement = Boolean.parseBoolean(tableElement.getAttribute("autoIncrement")); } //记录是否需要加返回结果集限制,默认需要加 boolean needAddLimit = true; if (tableElement.hasAttribute("needAddLimit")) { needAddLimit = Boolean.parseBoolean(tableElement.getAttribute("needAddLimit")); } //记录type,是否为global String tableTypeStr = tableElement.hasAttribute("type") ? tableElement.getAttribute("type") : null; int tableType = TableConfig.TYPE_GLOBAL_DEFAULT; if ("global".equalsIgnoreCase(tableTypeStr)) { tableType = TableConfig.TYPE_GLOBAL_TABLE; } //记录dataNode,就是分布在哪些dataNode上 String dataNode = tableElement.getAttribute("dataNode"); TableRuleConfig tableRule = null; if (tableElement.hasAttribute("rule")) { String ruleName = tableElement.getAttribute("rule"); tableRule = tableRules.get(ruleName); if (tableRule == null) { throw new ConfigException("rule " + ruleName + " is not found!"); } } boolean ruleRequired = false; //记录是否绑定有分片规则 if (tableElement.hasAttribute("ruleRequired")) { ruleRequired = Boolean.parseBoolean(tableElement.getAttribute("ruleRequired")); } if (tableNames == null) { throw new ConfigException("table name is not found!"); } //distribute函数,重新编排dataNode String distPrex = "distribute("; boolean distTableDns = dataNode.startsWith(distPrex); if (distTableDns) { dataNode = dataNode.substring(distPrex.length(), dataNode.length() - 1); } //分表功能 String subTables = tableElement.getAttribute("subTables"); for (int j = 0; j < tableNames.length; j++) { String tableName = tableNames[j]; TableRuleConfig tableRuleConfig=tableRule ; if(tableRuleConfig!=null) { //对于实现TableRuleAware的function进行特殊处理 根据每个表新建个实例 RuleConfig rule= tableRuleConfig.getRule(); if(rule.getRuleAlgorithm() instanceof TableRuleAware) { tableRuleConfig = (TableRuleConfig) ObjectUtil.copyObject(tableRuleConfig); tableRules.remove(tableRuleConfig.getName()) ; String newRuleName = tableRuleConfig.getName() + "_" + tableName; tableRuleConfig. setName(newRuleName); TableRuleAware tableRuleAware= (TableRuleAware) tableRuleConfig.getRule().getRuleAlgorithm(); tableRuleAware.setRuleName(newRuleName); tableRuleAware.setTableName(tableName); tableRuleConfig.getRule().getRuleAlgorithm().init(); tableRules.put(newRuleName,tableRuleConfig); } } TableConfig table = new TableConfig(tableName, primaryKey, autoIncrement, needAddLimit, tableType, dataNode, getDbType(dataNode), (tableRuleConfig != null) ? tableRuleConfig.getRule() : null, ruleRequired, null, false, null, null,subTables); checkDataNodeExists(table.getDataNodes()); // 检查分片表分片规则配置是否合法 if(table.getRule() != null) { checkRuleSuitTable(table); } if (distTableDns) { distributeDataNodes(table.getDataNodes()); } //检查去重 if (tables.containsKey(table.getName())) { throw new ConfigException("table " + tableName + " duplicated!"); } //放入map tables.put(table.getName(), table); } //只有tableName配置的是单个表(没有逗号)的时候才能有子表 if (tableNames.length == 1) { TableConfig table = tables.get(tableNames[0]); // process child tables 递归调用 processChildTables(tables, table, dataNode, tableElement); } } return tables; } /** * 处理动态日期表, 支持 YYYYMM、YYYYMMDD 两种格式 * * YYYYMM格式: yyyymm,2015,01,60 * YYYYMMDD格式: yyyymmdd,2015,01,10,50 * * @param tableNameElement * @param tableNameSuffixElement * @return */ private String doTableNameSuffix(String tableNameElement, String tableNameSuffixElement) { String newTableName = tableNameElement; String[] params = tableNameSuffixElement.split(","); String suffixFormat = params[0].toUpperCase(); if ( suffixFormat.equals("YYYYMM") ) { //读取参数 int yyyy = Integer.parseInt( params[1] ); int mm = Integer.parseInt( params[2] ); int mmEndIdx = Integer.parseInt( params[3] ); //日期处理 SimpleDateFormat yyyyMMSDF = new SimpleDateFormat("yyyyMM"); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, yyyy ); cal.set(Calendar.MONTH, mm - 1 ); cal.set(Calendar.DATE, 0 ); //表名改写 StringBuffer tableNameBuffer = new StringBuffer(); for(int mmIdx = 0; mmIdx <= mmEndIdx; mmIdx++) { tableNameBuffer.append( tableNameElement ); tableNameBuffer.append( yyyyMMSDF.format(cal.getTime()) ); cal.add(Calendar.MONTH, 1); if ( mmIdx != mmEndIdx) { tableNameBuffer.append(","); } } //逗号分隔的一批表名字A1,B2,C3 newTableName = tableNameBuffer.toString(); } else if ( suffixFormat.equals("YYYYMMDD") ) { //读取参数 int yyyy = Integer.parseInt( params[1] ); int mm = Integer.parseInt( params[2] ); int dd = Integer.parseInt( params[3] ); int ddEndIdx = Integer.parseInt( params[4] ); //日期处理 SimpleDateFormat yyyyMMddSDF = new SimpleDateFormat("yyyyMMdd"); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, yyyy ); cal.set(Calendar.MONTH, mm - 1 ); cal.set(Calendar.DATE, dd ); //表名改写 StringBuffer tableNameBuffer = new StringBuffer(); for(int ddIdx = 0; ddIdx <= ddEndIdx; ddIdx++) { tableNameBuffer.append( tableNameElement ); tableNameBuffer.append( yyyyMMddSDF.format(cal.getTime()) ); cal.add(Calendar.DATE, 1); if ( ddIdx != ddEndIdx) { tableNameBuffer.append(","); } } //逗号分隔的一批表名字A1,B2,C3 newTableName = tableNameBuffer.toString(); } return newTableName; }
三、动态日期表演示
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<table name="tab" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user1" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<!-- 2015年-1月即2014年的12月开始,累计到24个月-->
<table name="test" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" nameSuffix="yyyymm,2015,01,24" />
<!-- 2015年1月1号开始,累计到100天 -->
<table name="demo" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" nameSuffix="yyyymmdd,2015,01,01,100" />
</schema>
相关推荐
Mycat 1.6是其一个重要版本,提供了更稳定、更高效的服务。在本案例中,我们将探讨如何在Mycat 1.6环境下操作SQLServer数据库,实现数据的高效管理和处理。 首先,我们需要理解Mycat的核心功能。Mycat通过将大数据...
基于MyCat1.6正式版的源码修改的,支持subTables的按月分表正则配置 subTables=“ tableName_$201701-?” subTableWay="BYMONTH" 表示从201701月份开始进行分表处理,?表示当前日期的月份,是动态的。只需配置开始...
- 备份 schema 文件:`cp $MYCAT_HOME/conf/schema.xml $MYCAT_HOME/conf/schema.xml.tmp` - 编辑 schema 文件:`vim $MYCAT_HOME/conf/schema.xml` - 定义表规则,例如: ```xml <table name="employee" ...
【Mycat操作之分库分表入门】 Mycat是一款开源的分布式数据库中间件,主要功能是实现数据库的水平扩展,通过分库分表来处理大数据量的问题,减轻单机数据库的压力。它支持MySQL协议,可以透明地将一个数据库集群...
同时,文档可能还会讲解如何设置数据节点(Data Node)、逻辑库(Schema)、表(Table)以及路由规则(Rule),这些都是使用Mycat时必须了解的基本概念。 `MyCat.pdf`可能是更详细的官方手册或技术白皮书,涵盖了...
- **Table**:在Mycat中,每个表都必须属于某个Schema,表的分片规则由Schema定义。 - **Rule**:分片规则,决定了数据如何分布到各个DataNode。 3. **Mycat安装与配置** 安装Mycat通常涉及下载源码、编译、启动...
- 备份并编辑 schema 文件:`cp $MYCAT_HOME/conf/schema.xml $MYCAT_HOME/conf/schema.xml.tmp` 和 `vim $MYCAT_HOME/conf/schema.xml`。 - 清除原有的 `<mycat:schema>` 节点,并按照需求配置数据节点和数据主机...
对于初次接触 **Mycat** 的用户,建议先通过下载源码并在本地使用 Eclipse 等工具进行配置和运行,以便更深入地了解程序的运行逻辑。 - 首先需在本地安装和配置好相关环境,具体步骤可以参考“github-eclipse开发...
在大数据和分布式数据库领域,Mycat作为一款开源的数据库中间件,扮演着重要的角色。它能够将一台物理服务器上的多个数据库实例组合成一个逻辑数据库,实现数据库的分布式处理,提高系统的并发能力和数据处理能力。...
Mycat 是一个基于 MySQL 数据库的分布式数据库中间件,它能够将单一数据库扩展到多个数据库节点,实现数据的水平扩展。Mycat 的设计目标是处理大数据量、高并发的场景,它通过分库分表、读写分离等策略,有效地解决...