package com.taobao.tddl.rule; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.taobao.tddl.rule.Rule.RuleColumn; import com.taobao.tddl.rule.impl.VirtualNodeGroovyRule; import com.taobao.tddl.rule.model.AdvancedParameter; import com.taobao.tddl.rule.model.Field; import com.taobao.tddl.rule.model.MatcherResult; import com.taobao.tddl.rule.model.TargetDB; import com.taobao.tddl.rule.model.sqljep.Comparative; import com.taobao.tddl.rule.model.sqljep.ComparativeMapChoicer; import com.taobao.tddl.rule.utils.RuleUtils; import com.taobao.tddl.rule.utils.sample.Samples; import com.taobao.tddl.rule.utils.sample.SamplesCtx; /** * <pre> * 1.在分库列和分表列没有交集的情况下,各自计算,结果做笛卡尔组合。 * * 2.在分库列和分表列有交集,公共列的附加参数完全相同的情况下,公共列库表规则的描点总是相同的。 * 表的描点要经过库规则计算后按库分类,在每个库内,只对产生该库的描点值进行表规则计算来确定表 * 否则可能会产生不在本库的表。例如库%2表%8: * id%2=0--db0:t0,t2,t4,t6 * id%2=1--db1: t1,t3,t5,t7 * 表的描点0-7需要先经过库规则计算,按库结果分成两类db0:0,2,4,6和db1:1,3,5,7再分表进行表规则计算 * * 3.在分库列和分表列 列名相同 类型不同 的情况下,各自描点取并集,再按2的方式计算。比如库1_month 表1_day, * 单月db0,双月db1;一天一张表。则time in(2月5日 3月8日 4月20日)正确结果应该是: * db0: t_5,t_20 * db1: t_8 * 上述例子是描点相同的情况。对于描点不同的情况例如 5月31日<=time<=6月2日。 * 库的描点是5月31日,6月1日共2个;表是5月31日、6月1日、6月2日共3个,正确的结果应该是: * db0: t_1,t_2 * db1: t_31 * 上述列子是表的描点多于库,同时包含了库的描点。表的描点必须经过库规则分类再计算。 * 对于表的描点不包含全部库描点的情况,例如:库描点a,b,c表描点b,c,d .... * * 4.不考虑库表规则的公共列(参数)在类型相同的情况下,迭代次数的不同。 * 列名和类型相同,配不同的迭代次数是意义不大的,例如库%2表%8,计算库的时候2个描点,计算表需要8个描点, * 但是由于是同一个列,表的8个描点需要全部走一遍库规则,进行按库分离聚合再计算表,才能保证结果的正确: * 如果库的迭代次数大于表的,例如库id%8 表id%3, 统一按库的描点表规则会多计算几次 * DB0:t0,t1,t2; DB1:t0,t1,t2 * 忽略这种情况,所以公共列(参数)在类型相同时,要求应用按最大迭代次数配成一致。 * </pre> * * @author linxuan */ public class VirtualTableRuleMatcher { private static final boolean isCrossInto; static { // 提供一个可配置的机会,但是在绝大多数默认的情况下,不想影响接口层次和代码结构 String str = System.getProperty("com.taobao.tddl.rule.isCrossIntoCalculate", "false"); isCrossInto = Boolean.parseBoolean(str); } /** * <pre> * 基本思路: * 1. 根据参数,选择匹配的{@linkplain Rule}. * a. 如果同时匹配多个,优先返回第一个 * b. 如果没有匹配,检查是否允许全表扫描 * 2. 根据db和table rule的情况进行计算 * a. db rule不存在或者不匹配,单独计算table后 + 所有db * b. tb rule不存在或者不匹配,单独计算db后 + 所有table * c. db/tb都存在 * i. rule中不存在交集字段,单独计算db + 单独计算table. * (遇上virtual node,根据虚拟table节点,转化为实际table表,再根据db和实际table的映射,提取实际db) * ii. rule中存在交集字段 * 1. 提取交集字段,将db+tb的枚举值采取merge,优先计算出db * 2. 遍历db的枚举结果,将db+tb的枚举值采取replace,即以db的枚举值为准,计算出tb * </pre> * * @param comparativeMapChoicer * @param args sql的参数列表 * @param rule sql中虚拟表名对应的规则 * @return */ public MatcherResult match(ComparativeMapChoicer choicer, List<Object> args, VirtualTableRule<String, String> rule, boolean needSourceKey) { if (needSourceKey) { // 暂时不支持虚拟节点 return matchWithSourceKey(choicer, args, rule); } else { return matchNoSourceKey(choicer, args, rule); } } private MatcherResult matchNoSourceKey(ComparativeMapChoicer choicer, List<Object> args, VirtualTableRule<String, String> rule) { // 所有库表规则中用到的列和对应的比较树,作为一个规则链匹配时绑定参数的缓存。 // 一开始就把所有的取出来不够优化。可能有些是不需要取的。也就省了些绑定参数的操作 Map<String, Comparative> allRuleArgs = new HashMap<String, Comparative>(2); Map<String, Comparative> dbRuleArgs = new HashMap<String, Comparative>(2); // 匹配的规则所对应的列名和比较树 Map<String, Comparative> tbRuleArgs = new HashMap<String, Comparative>(2); // 匹配的规则所对应的列名和比较树 Object outerCtx = rule.getOuterContext(); // 竟然不需要defaultDbValue和defaultTbValue了 Rule<String> dbRule = findMatchedRule(allRuleArgs, rule.getDbShardRules(), dbRuleArgs, choicer, args, rule); Rule<String> tbRule = findMatchedRule(allRuleArgs, rule.getTbShardRules(), tbRuleArgs, choicer, args, rule); Map<String, Set<String>> topology; if (dbRule == null && tbRule == null) { // 若无库规则,静态拓扑里面只有一个库,则无论没表规则也好,全表扫也好,结果都和静态拓扑一样 // 若有库规则,那么是全库扫,则无论没表规则也好,全表扫也好,结果仍然和静态拓扑一样!! topology = rule.getActualTopology(); // 不猜不知道,世界真奇妙 } else if (dbRule == null) { Set<String> tbValues = tbRule.calculateNoTrace(tbRuleArgs, null, outerCtx); // 1.无库规则 2.有但是没匹配到 ;这时表规则一定不为空,且和库规则全无关联,自己算自己的 topology = new HashMap<String, Set<String>>(rule.getActualTopology().size()); for (String dbValue : rule.getActualTopology().keySet()) { topology.put(dbValue, tbValues); } } else if (tbRule == null) { // dbRule is VirtualNodeGroovyRule, just do the FullTableScan; if (dbRule instanceof VirtualNodeGroovyRule) { topology = rule.getActualTopology(); } else { // 1.无表规则 2.有但是没匹配到 ;这时库规则一定不为空,且和表规则全无关联,自己算自己的 Set<String> dbValues = dbRule.calculateNoTrace(dbRuleArgs, null, outerCtx); topology = new HashMap<String, Set<String>>(dbValues.size()); for (String dbValue : dbValues) { topology.put(dbValue, rule.getActualTopology().get(dbValue)); } } } else { // 看表规则和库规则的列有无交集 Set<String> commonSet = getCommonColumnSet(dbRule, tbRule); String[] commonColumn = commonSet == null ? null : commonSet.toArray(new String[commonSet.size()]); if (commonColumn == null || commonColumn.length == 0) { // 无交集 // modify by junyu,2011-9-23,增加虚拟节点 Set<String> tbValues = tbRule.calculateNoTrace(tbRuleArgs, null, outerCtx); Set<String> dbValues = null; if (dbRule instanceof VirtualNodeGroovyRule) { // 说明dbRule是虚拟映射 topology = new HashMap<String, Set<String>>(); for (String tab : tbValues) { String db = dbRule.calculateVnodeNoTrace(tab, null, outerCtx); if (!topology.containsKey(db)) { Set<String> tbSet = new HashSet<String>(); tbSet.add(tab); topology.put(db, tbSet); } else { topology.get(db).add(tab); } } } else { dbValues = dbRule.calculateNoTrace(dbRuleArgs, null, outerCtx); topology = new HashMap<String, Set<String>>(dbValues.size()); for (String dbValue : dbValues) { topology.put(dbValue, tbValues); } } } else { // 有交集 if (!isCrossInto) { topology = crossNoSourceKey1(dbRule, dbRuleArgs, tbRule, tbRuleArgs, commonColumn, outerCtx); } else { topology = crossNoSourceKey2(dbRule, dbRuleArgs, tbRule, tbRuleArgs, commonSet, outerCtx); } } } return new MatcherResult(buildTargetDbList(topology), dbRuleArgs, tbRuleArgs); } private MatcherResult matchWithSourceKey(ComparativeMapChoicer choicer, List<Object> args, VirtualTableRule<String, String> rule) { // 所有库表规则中用到的列和对应的比较树,作为一个规则链匹配时绑定参数的缓存。 // 一开始就把所有的取出来不够优化。可能有些是不需要取的。也就省了些绑定参数的操作 Map<String, Comparative> allRuleArgs = new HashMap<String, Comparative>(2); Map<String, Comparative> dbRuleArgs = new HashMap<String, Comparative>(2); // 匹配的规则所对应的列名和比较树 Map<String, Comparative> tbRuleArgs = new HashMap<String, Comparative>(2); // 匹配的规则所对应的列名和比较树 Object outerCtx = rule.getOuterContext(); // 竟然不需要defaultDbValue和defaultTbValue了 Rule<String> dbRule = findMatchedRule(allRuleArgs, rule.getDbShardRules(), dbRuleArgs, choicer, args, rule); Rule<String> tbRule = findMatchedRule(allRuleArgs, rule.getTbShardRules(), tbRuleArgs, choicer, args, rule); Map<String, Map<String, Field>> topology; if (dbRule == null && tbRule == null) { // 若无库规则,静态拓扑里面只有一个库,则无论没表规则也好,全表扫也好,结果都和静态拓扑一样 // 若有库规则,那么是全库扫,则无论没表规则也好,全表扫也好,结果仍然和静态拓扑一样!! Map<String, Set<String>> actualTopology = rule.getActualTopology(); topology = new HashMap<String, Map<String, Field>>(actualTopology.size()); for (Map.Entry<String, Set<String>> e : rule.getActualTopology().entrySet()) { topology.put(e.getKey(), toMapField(e.getValue())); } } else if (dbRule == null) { // 1.无库规则 2.有但是没匹配到 ;这时表规则一定不为空,且和库规则全无关联,自己算自己的 Map<String, Samples> tbValues = RuleUtils.cast(tbRule.calculate(tbRuleArgs, null, outerCtx)); topology = new HashMap<String, Map<String, Field>>(rule.getActualTopology().size()); for (String dbValue : rule.getActualTopology().keySet()) { topology.put(dbValue, toMapField(tbValues)); } } else if (tbRule == null) { // if dbRule is VirtualNodeGroovyRule,just do the fullTableScan if (dbRule instanceof VirtualNodeGroovyRule) { topology = new HashMap<String, Map<String, Field>>(rule.getActualTopology().size()); for (Map.Entry<String, Set<String>> e : rule.getActualTopology().entrySet()) { topology.put(e.getKey(), toMapField(e.getValue())); } } else { // 1.无表规则 2.有但是没匹配到 ;这时库规则一定不为空,且和表规则全无关联,自己算自己的 // bug fix by leiwen.zh: needIdInGroup不支持只分库的场景 Map<String, Samples> dbValues = RuleUtils.cast(dbRule.calculate(dbRuleArgs, null, outerCtx)); topology = new HashMap<String, Map<String, Field>>(dbValues.size()); for (String dbValue : dbValues.keySet()) { Map<String, Samples> tbValues = new HashMap<String, Samples>(); // 不分表,pattern即为实际表名 String tableName = rule.getTbNamePattern(); // 不分表,Sample与分库保持一致即可 Samples samples = dbValues.get(dbValue); tbValues.put(tableName, samples); topology.put(dbValue, toMapField(tbValues)); } } } else { // 看表规则和库规则的列有无交集 Set<String> commonSet = getCommonColumnSet(dbRule, tbRule); String[] commonColumn = commonSet == null ? null : commonSet.toArray(new String[commonSet.size()]); if (commonColumn == null || commonColumn.length == 0) { // 无交集 // modify by junyu,2011-10-24,原本没有加这个,需要测试下id in group 优化的部分 if (dbRule instanceof VirtualNodeGroovyRule) { Map<String, Samples> tbValues = RuleUtils.cast(tbRule.calculate(tbRuleArgs, null, outerCtx)); // 说明dbRule是虚拟映射 topology = new HashMap<String, Map<String, Field>>(); Map<String, Map<String, Samples>> templogy = new HashMap<String, Map<String, Samples>>(); for (Map.Entry<String, Samples> entry : tbValues.entrySet()) { String db = dbRule.calculateVnodeNoTrace(entry.getKey(), null, outerCtx); if (!topology.containsKey(db)) { Map<String, Samples> tbSet = new HashMap<String, Samples>(); tbSet.put(entry.getKey(), entry.getValue()); templogy.put(db, tbSet); } else { templogy.get(db).put(entry.getKey(), entry.getValue()); } } for (Map.Entry<String, Map<String, Samples>> entry : templogy.entrySet()) { topology.put(entry.getKey(), toMapField(entry.getValue())); } } else { Set<String> dbValues = dbRule.calculateNoTrace(dbRuleArgs, null, outerCtx); Map<String, Samples> tbValues = RuleUtils.cast(tbRule.calculate(tbRuleArgs, null, outerCtx)); topology = new HashMap<String, Map<String, Field>>(dbValues.size()); for (String dbValue : dbValues) { topology.put(dbValue, toMapField(tbValues)); } } } else { // 有交集 if (!isCrossInto) { topology = crossWithSourceKey1(dbRule, dbRuleArgs, tbRule, tbRuleArgs, commonColumn, outerCtx); } else { topology = crossWithSourceKey2(dbRule, dbRuleArgs, tbRule, tbRuleArgs, commonSet, outerCtx); } } } return new MatcherResult(buildTargetDbListWithSourceKey(topology), dbRuleArgs, tbRuleArgs); } private Map<String, Set<String>> crossNoSourceKey1(Rule<String> matchedDbRule, Map<String, Comparative> matchedDbRuleArgs, Rule<String> matchedTbRule, Map<String, Comparative> matchedTbRuleArgs, String[] commonColumn, Object outerCtx) { SamplesCtx dbRuleCtx = null; // 对于表规则中与库规则列名相同而自增类型不同的列,将其表枚举结果加入库规则的枚举集 Set<AdvancedParameter> mergeInCommon = diffTypeOrOptionalInCommon(matchedDbRule, matchedTbRule, commonColumn, matchedDbRuleArgs, matchedTbRuleArgs); if (mergeInCommon != null && !mergeInCommon.isEmpty()) { // 公共列包含有枚举类型不同的列,例如库是1_month,表示1_day Map<String, Set<Object>> tbTypes = RuleUtils.getSamplingField(matchedTbRuleArgs, mergeInCommon); dbRuleCtx = new SamplesCtx(new Samples(tbTypes), SamplesCtx.merge); } Map<String, Samples> dbValues = RuleUtils.cast(matchedDbRule.calculate(matchedDbRuleArgs, dbRuleCtx, outerCtx)); Map<String, Set<String>> topology = new HashMap<String, Set<String>>(dbValues.size()); for (Map.Entry<String, Samples> e : dbValues.entrySet()) { SamplesCtx tbRuleCtx = new SamplesCtx(e.getValue().subSamples(commonColumn), SamplesCtx.replace); Set<String> tbValues = matchedTbRule.calculateNoTrace(matchedTbRuleArgs, tbRuleCtx, outerCtx); topology.put(e.getKey(), tbValues); } return topology; } private Map<String, Map<String, Field>> crossWithSourceKey1(Rule<String> matchedDbRule, Map<String, Comparative> matchedDbRuleArgs, Rule<String> matchedTbRule, Map<String, Comparative> matchedTbRuleArgs, String[] commonColumn, Object outerCtx) { SamplesCtx dbRuleCtx = null; // 对于表规则中与库规则列名相同而自增类型不同的列,将其表枚举结果加入库规则的枚举集 Set<AdvancedParameter> mergeInCommon = diffTypeOrOptionalInCommon(matchedDbRule, matchedTbRule, commonColumn, matchedDbRuleArgs, matchedTbRuleArgs); if (mergeInCommon != null && !mergeInCommon.isEmpty()) { // 公共列包含有枚举类型不同的列,例如库是1_month,表示1_day Map<String, Set<Object>> tbTypes = RuleUtils.getSamplingField(matchedTbRuleArgs, mergeInCommon); dbRuleCtx = new SamplesCtx(new Samples(tbTypes), SamplesCtx.merge); } Map<String, Samples> dbValues = RuleUtils.cast(matchedDbRule.calculate(matchedDbRuleArgs, dbRuleCtx, outerCtx)); Map<String, Map<String, Field>> topology = new HashMap<String, Map<String, Field>>(dbValues.size()); for (Map.Entry<String, Samples> e : dbValues.entrySet()) { SamplesCtx tbRuleCtx = new SamplesCtx(e.getValue().subSamples(commonColumn), SamplesCtx.replace); Map<String, Samples> tbValues = RuleUtils.cast(matchedTbRule.calculate(matchedTbRuleArgs, tbRuleCtx, outerCtx)); topology.put(e.getKey(), toMapField(tbValues)); } return topology; } private Map<String, Set<String>> crossNoSourceKey2(Rule<String> matchedDbRule, Map<String, Comparative> matchedDbRuleArgs, Rule<String> matchedTbRule, Map<String, Comparative> matchedTbRuleArgs, Set<String> commonSet, Object outerCtx) { // 有交集 String[] commonColumn = commonSet == null ? null : commonSet.toArray(new String[commonSet.size()]); Set<AdvancedParameter> dbParams = RuleUtils.cast(matchedDbRule.getRuleColumnSet()); Set<AdvancedParameter> tbParams = RuleUtils.cast(matchedTbRule.getRuleColumnSet()); Map<String, Set<Object>> dbEnumerates = RuleUtils.getSamplingField(matchedDbRuleArgs, dbParams); Set<AdvancedParameter> mergeInCommon = diffTypeOrOptionalInCommon(matchedDbRule, matchedTbRule, commonColumn, matchedDbRuleArgs, matchedTbRuleArgs); if (mergeInCommon != null && !mergeInCommon.isEmpty()) { // 将自增类型不同的公共列的表枚举值加入库枚举值中 Map<String, Set<Object>> diifTypeTbEnumerates = RuleUtils.getSamplingField(matchedTbRuleArgs, mergeInCommon); for (Map.Entry<String, Set<Object>> e : diifTypeTbEnumerates.entrySet()) { dbEnumerates.get(e.getKey()).addAll(e.getValue()); } } Set<AdvancedParameter> tbOnly = new HashSet<AdvancedParameter>(); for (AdvancedParameter param : tbParams) { if (!commonSet.contains(param.key)) { tbOnly.add(param); } } Map<String, Set<String>> topology = new HashMap<String, Set<String>>(); if (tbOnly.isEmpty()) { // 分库列完全包含了分表列 for (Map<String, Object> dbSample : new Samples(dbEnumerates)) { // 遍历笛卡尔抽样 String dbIndex = matchedDbRule.eval(dbSample, outerCtx); String tbName = matchedTbRule.eval(dbSample, outerCtx); addToTopology(dbIndex, tbName, topology); } } else { Map<String, Set<Object>> tbEnumerates = RuleUtils.getSamplingField(matchedTbRuleArgs, tbOnly);// 只有表的枚举 Samples tbSamples = new Samples(tbEnumerates); for (Map<String, Object> dbSample : new Samples(dbEnumerates)) { // 遍历库笛卡尔抽样 String dbIndex = matchedDbRule.eval(dbSample, outerCtx); for (Map<String, Object> tbSample : tbSamples) { // 遍历表中单独列的笛卡尔抽样 // dbSample.putAll(tbSample); // String tbName = matchedTbRule.eval(dbSample, outerCtx); // modify by jianghang at 2013-11-18,应该是以dbSample为主构造枚举值才对 tbSample.putAll(dbSample); String tbName = matchedTbRule.eval(tbSample, outerCtx); addToTopology(dbIndex, tbName, topology); } } } return topology; } private Map<String, Map<String, Field>> crossWithSourceKey2(Rule<String> matchedDbRule, Map<String, Comparative> matchedDbRuleArgs, Rule<String> matchedTbRule, Map<String, Comparative> matchedTbRuleArgs, Set<String> commonSet, Object outerCtx) { // 有交集 String[] commonColumn = commonSet == null ? null : commonSet.toArray(new String[commonSet.size()]); Set<AdvancedParameter> dbParams = RuleUtils.cast(matchedDbRule.getRuleColumnSet()); Set<AdvancedParameter> tbParams = RuleUtils.cast(matchedTbRule.getRuleColumnSet()); Map<String, Set<Object>> dbEnumerates = RuleUtils.getSamplingField(matchedDbRuleArgs, dbParams); Set<AdvancedParameter> mergeInCommon = diffTypeOrOptionalInCommon(matchedDbRule, matchedTbRule, commonColumn, matchedDbRuleArgs, matchedTbRuleArgs); if (mergeInCommon != null && !mergeInCommon.isEmpty()) { // 将自增类型不同的公共列的表枚举值加入库枚举值中 Map<String, Set<Object>> diifTypeTbEnumerates = RuleUtils.getSamplingField(matchedTbRuleArgs, mergeInCommon); for (Map.Entry<String, Set<Object>> e : diifTypeTbEnumerates.entrySet()) { dbEnumerates.get(e.getKey()).addAll(e.getValue()); } } Set<AdvancedParameter> tbOnly = new HashSet<AdvancedParameter>(); for (AdvancedParameter param : tbParams) { if (!commonSet.contains(param.key)) { tbOnly.add(param); } } Map<String, Map<String, Field>> topology = new HashMap<String, Map<String, Field>>(); if (tbOnly.isEmpty()) { // 分库列完全包含了分表列 for (Map<String, Object> dbSample : new Samples(dbEnumerates)) { // 遍历笛卡尔抽样 String dbIndex = matchedDbRule.eval(dbSample, outerCtx); String tbName = matchedTbRule.eval(dbSample, outerCtx); addToTopologyWithSource(dbIndex, tbName, topology, dbSample, tbParams); } } else { Map<String, Set<Object>> tbEnumerates = RuleUtils.getSamplingField(matchedTbRuleArgs, tbOnly);// 只有表的枚举 Samples tbSamples = new Samples(tbEnumerates); for (Map<String, Object> dbSample : new Samples(dbEnumerates)) { // 遍历库笛卡尔抽样 String dbIndex = matchedDbRule.eval(dbSample, outerCtx); for (Map<String, Object> tbSample : tbSamples) { // 遍历表中单独列的笛卡尔抽样 // dbSample.putAll(tbSample); // String tbName = matchedTbRule.eval(dbSample, outerCtx); // modify by jianghang at 2013-11-18,应该是以dbSample为主构造枚举值才对 tbSample.putAll(dbSample); String tbName = matchedTbRule.eval(tbSample, outerCtx); addToTopologyWithSource(dbIndex, tbName, topology, dbSample, tbParams); } } } return topology; } private static void addToTopology(String dbIndex, String tbName, Map<String, Set<String>> topology) { Set<String> tbNames = topology.get(dbIndex); if (tbNames == null) { tbNames = new HashSet<String>(); topology.put(dbIndex, tbNames); } tbNames.add(tbName); } private static void addToTopologyWithSource(String dbIndex, String tbName, Map<String, Map<String, Field>> topology, Map<String, Object> tbSample, Set<AdvancedParameter> tbParams) { Map<String, Field> tbNames = topology.get(dbIndex); if (tbNames == null) { tbNames = new HashMap<String, Field>(); topology.put(dbIndex, tbNames); } Field f = tbNames.get(tbName); if (f == null) { f = new Field(tbParams.size()); tbNames.put(tbName, f); } for (AdvancedParameter ap : tbParams) { Set<Object> set = f.getSourceKeys().get(ap.key); if (set == null) { set = new HashSet<Object>(); } set.add(tbSample.get(ap.key)); } } private Map<String, Field> toMapField(Map<String/* rule计算结果 */, Samples/* 得到该结果的样本 */> values) { Map<String, Field> res = new HashMap<String, Field>(values.size()); for (Map.Entry<String, Samples> e : values.entrySet()) { Field f = new Field(e.getValue().size()); f.setSourceKeys(e.getValue().getColumnEnumerates()); res.put(e.getKey(), f); } return res; } private Map<String, Field> toMapField(Set<String> values) { Map<String, Field> res = new HashMap<String, Field>(values.size()); for (String valule : values) { res.put(valule, null); } return res; } private List<TargetDB> buildTargetDbList(Map<String, Set<String>> topology) { List<TargetDB> targetDbList = new ArrayList<TargetDB>(topology.size()); for (Map.Entry<String, Set<String>> e : topology.entrySet()) { TargetDB db = new TargetDB(); Map<String, Field> tableNames = new HashMap<String, Field>(e.getValue().size()); for (String tbName : e.getValue()) { tableNames.put(tbName, null); } db.setDbIndex(e.getKey()); db.setTableNames(tableNames); targetDbList.add(db); } return targetDbList; } private List<TargetDB> buildTargetDbListWithSourceKey(Map<String, Map<String, Field>> topology) { List<TargetDB> targetDbList = new ArrayList<TargetDB>(topology.size()); for (Map.Entry<String, Map<String, Field>> e : topology.entrySet()) { TargetDB db = new TargetDB(); db.setDbIndex(e.getKey()); db.setTableNames(e.getValue()); targetDbList.add(db); } return targetDbList; } private static <T> Rule<T> findMatchedRule(Map<String, Comparative> allRuleColumnArgs, List<Rule<T>> shardRules, Map<String, Comparative> matchArgs, ComparativeMapChoicer choicer, List<Object> args, VirtualTableRule<String, String> rule) { Rule<T> matchedRule = null; if (shardRules != null && shardRules.size() != 0) { matchedRule = findMatchedRule(allRuleColumnArgs, shardRules, matchArgs, choicer, args); if (matchedRule == null) { // 有分库或分表规则,但是没有匹配到,是否执行全部扫描 if (!rule.isAllowFullTableScan()) { List<Set<String>> shardColumns = new LinkedList<Set<String>>(); for (Rule<T> r : shardRules) { Set<String> columnSet = new LinkedHashSet<String>(); for (RuleColumn rc : r.getRuleColumnSet()) { columnSet.add(rc.key); } shardColumns.add(columnSet); } throw new IllegalArgumentException("sql contain no sharding column:" + shardColumns); } } } return matchedRule; } /** * @return 返回两个规则的公共列 */ private static Set<String> getCommonColumnSet(Rule<String> matchedDbRule, Rule<String> matchedTbRule) { Set<String> res = null; for (String key : matchedDbRule.getRuleColumns().keySet()) { if (matchedTbRule.getRuleColumns().containsKey(key)) { if (res == null) { res = new HashSet<String>(1); } res.add(key); } } return res; } /** * <pre> * 1. dbRule中和tbRule中存在相同的列,并且当前处于optional状态 * 2. tbRule中和dbRule列名相同而自增类型不用的AdvancedParameter对象 * </pre> * * @return */ private static Set<AdvancedParameter> diffTypeOrOptionalInCommon(Rule<String> dbRule, Rule<String> tbRule, String[] commonColumn, Map<String, Comparative> matchedDbRuleArgs, Map<String, Comparative> matchedTbRuleArgs) { Set<AdvancedParameter> mergeInCommon = null; for (String common : commonColumn) { AdvancedParameter dbap = (AdvancedParameter) dbRule.getRuleColumns().get(common); AdvancedParameter tbap = (AdvancedParameter) tbRule.getRuleColumns().get(common); boolean isOptional = matchedDbRuleArgs.containsKey(common) == false && matchedTbRuleArgs.containsKey(common) == false; if (dbap.atomicIncreateType != tbap.atomicIncreateType || isOptional) { if (mergeInCommon == null) { mergeInCommon = new HashSet<AdvancedParameter>(0); } mergeInCommon.add(tbap); } } return mergeInCommon; } /** * <pre> * 规则一:#a# #b?# * 规则二:#a# #c?# * 规则三:#b?# #d?# * * 参数为(a,c),则选规则二; * 参数为(a,d)则选规则一; * 参数为(b) 则选规则三 * </pre> * * @param <T> * @param allRuleColumnArgs * @param rules * @param matchArgs * @return */ private static <T> Rule<T> findMatchedRule(Map<String, Comparative> allRuleColumnArgs, List<Rule<T>> rules, Map<String, Comparative> matchArgs, ComparativeMapChoicer choicer, List<Object> args) { // 优先匹配必选列 for (Rule<T> r : rules) { matchArgs.clear(); for (RuleColumn ruleColumn : r.getRuleColumns().values()) { Comparative comparative = getComparative(ruleColumn.key, allRuleColumnArgs, choicer, args); if (comparative == null) { break; } matchArgs.put(ruleColumn.key, comparative); } if (matchArgs.size() == r.getRuleColumns().size()) { return r; // 完全匹配 } } // 匹配必选列 + 可选列 for (Rule<T> r : rules) { matchArgs.clear(); int mandatoryColumnCount = 0; for (RuleColumn ruleColumn : r.getRuleColumns().values()) { if (ruleColumn.optional) { continue; } mandatoryColumnCount++; Comparative comparative = getComparative(ruleColumn.key, allRuleColumnArgs, choicer, args); if (comparative == null) { break; } matchArgs.put(ruleColumn.key, comparative); } if (mandatoryColumnCount != 0 && matchArgs.size() == mandatoryColumnCount) { return r; // 必选列匹配 } } // 针对没有必选列的规则如:rule=..#a?#..#b?#.. 并且只有a或者b列在sql中有 for (Rule<T> r : rules) { matchArgs.clear(); for (RuleColumn ruleColumn : r.getRuleColumns().values()) { if (!ruleColumn.optional) { break; // 如果当前规则有必选项,直接跳过,因为走到这里必选列已经不匹配了 } Comparative comparative = getComparative(ruleColumn.key, allRuleColumnArgs, choicer, args); if (comparative != null) { matchArgs.put(ruleColumn.key, allRuleColumnArgs.get(ruleColumn.key)); } } if (matchArgs.size() != 0) { return r; // 第一个全是可选列的规则,并且args包含该规则的部分可选列 } } // add by jianghang at 2013-11-18 // 如果还没有匹配规则,则可能一种情况就是所有的Rule都不满足,最后再查找一次规则中所有列都为可选的进行返回,按规则顺序返回第一个 boolean isAllOptional = true; for (Rule<T> r : rules) { for (RuleColumn ruleColumn : r.getRuleColumns().values()) { if (!ruleColumn.optional) { isAllOptional = false; break;// 如果当前规则有必选项,直接跳过,因为走到这里必选列已经不匹配了 } } if (isAllOptional) { return r; } } return null; } private static Comparative getComparative(String colName, Map<String, Comparative> allRuleColumnArgs, ComparativeMapChoicer comparativeMapChoicer, List<Object> args) { Comparative comparative = allRuleColumnArgs.get(colName); // 先从缓存中获取 if (comparative == null) { comparative = comparativeMapChoicer.getColumnComparative(args, colName); if (comparative != null) { allRuleColumnArgs.put(colName, comparative); // 放入缓存 } } return comparative; } }