package com.taobao.tddl.optimizer.rule; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.collect.Lists; import com.taobao.tddl.common.exception.TddlException; import com.taobao.tddl.common.exception.TddlRuntimeException; import com.taobao.tddl.common.model.lifecycle.AbstractLifecycle; import com.taobao.tddl.optimizer.core.ASTNodeFactory; import com.taobao.tddl.optimizer.core.ast.QueryTreeNode; import com.taobao.tddl.optimizer.core.expression.IBooleanFilter; import com.taobao.tddl.optimizer.core.expression.IColumn; import com.taobao.tddl.optimizer.core.expression.IFilter; import com.taobao.tddl.optimizer.core.expression.IFilter.OPERATION; import com.taobao.tddl.optimizer.core.expression.IFunction; import com.taobao.tddl.optimizer.core.expression.ILogicalFilter; import com.taobao.tddl.optimizer.core.expression.ISelectable; import com.taobao.tddl.optimizer.utils.OptimizerUtils; import com.taobao.tddl.rule.TableRule; import com.taobao.tddl.rule.TddlRule; import com.taobao.tddl.rule.VirtualTableRoot; import com.taobao.tddl.rule.exceptions.RouteCompareDiffException; 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.ComparativeAND; import com.taobao.tddl.rule.model.sqljep.ComparativeBaseList; import com.taobao.tddl.rule.model.sqljep.ComparativeMapChoicer; import com.taobao.tddl.rule.model.sqljep.ComparativeOR; /** * 优化器中使用Tddl Rule的一些工具方法,需要依赖{@linkplain TddlRule}自己先做好初始化 * * @since 5.0.0 */ public class OptimizerRule extends AbstractLifecycle { private final static int DEFAULT_OPERATION_COMP = -1000; private final TddlRule tddlRule; public OptimizerRule(TddlRule tddlRule){ this.tddlRule = tddlRule; } @Override protected void doInit() throws TddlException { if (!tddlRule.isInited()) { tddlRule.init(); } } @Override protected void doDestory() throws TddlException { if (tddlRule.isInited()) { tddlRule.destory(); } } public List<TargetDB> shard(String logicTable, ComparativeMapChoicer choicer, boolean isWrite) { MatcherResult result; try { result = tddlRule.routeMverAndCompare(!isWrite, logicTable, choicer, Lists.newArrayList()); } catch (RouteCompareDiffException e) { throw new TddlRuntimeException(e); } List<TargetDB> targetDbs = result.getCalculationResult(); if (targetDbs == null || targetDbs.isEmpty()) { throw new IllegalArgumentException("can't find target db. table is " + logicTable + "."); } return targetDbs; } /** * 根据逻辑表和条件,计算一下目标库 */ public List<TargetDB> shard(String logicTable, final IFilter ifilter, boolean isWrite) { MatcherResult result; try { result = tddlRule.routeMverAndCompare(!isWrite, logicTable, new ComparativeMapChoicer() { @Override public Map<String, Comparative> getColumnsMap(List<Object> arguments, Set<String> partnationSet) { Map<String, Comparative> map = new HashMap<String, Comparative>(); for (String str : partnationSet) { map.put(str, getColumnComparative(arguments, str)); } return map; } @Override public Comparative getColumnComparative(List<Object> arguments, String colName) { return getComparative(ifilter, colName); } }, Lists.newArrayList()); } catch (RouteCompareDiffException e) { throw new TddlRuntimeException(e); } List<TargetDB> targetDbs = result.getCalculationResult(); if (targetDbs == null || targetDbs.isEmpty()) { throw new IllegalArgumentException("can't find target db. table is " + logicTable + ". filter is " + ifilter); } return targetDbs; } /** * 允许定义期望的expectedGroups,如果broadcast的逻辑表的物理拓扑结构包含了该节点,那说明可以做本地节点join,下推sql */ public List<TargetDB> shardBroadCast(String logicTable, final IFilter ifilter, boolean isWrite, List<String> expectedGroups) { if (expectedGroups == null) { return this.shard(logicTable, ifilter, isWrite); } if (!isBroadCast(logicTable)) { throw new TddlRuntimeException(logicTable + "不是broadCast的表"); } List<TargetDB> targets = this.shard(logicTable, (IFilter) null, isWrite); List<TargetDB> targetsMatched = new ArrayList<TargetDB>(); for (TargetDB target : targets) { if (expectedGroups.contains(target.getDbIndex())) { targetsMatched.add(target); } } return targetsMatched; } /** * 根据逻辑表返回一个随机的物理目标库TargetDB * * @param logicTable * @return */ public TargetDB shardAny(String logicTable) { TableRule tableRule = getTableRule(logicTable); if (tableRule == null) { // 设置为同名,同名不做转化 TargetDB target = new TargetDB(); target.setDbIndex(getDefaultDbIndex(logicTable, tddlRule.getCurrentRule())); target.addOneTable(logicTable); return target; } else { for (String group : tableRule.getActualTopology().keySet()) { Set<String> tableNames = tableRule.getActualTopology().get(group); if (tableNames == null || tableNames.isEmpty()) { continue; } TargetDB target = new TargetDB(); target.setDbIndex(group); target.addOneTable(tableNames.iterator().next()); return target; } } throw new IllegalArgumentException("can't find any target db. table is " + logicTable + ". "); } public String getDefaultGroup() { VirtualTableRoot root = tddlRule.getCurrentRule(); return root.getDefaultDbIndex(); } public String getJoinGroup(String logicTable) { TableRule table = getTableRule(logicTable); return table != null ? table.getJoinGroup() : null;// 没找到表规则,默认为单库 } public boolean isBroadCast(String logicTable) { TableRule table = getTableRule(logicTable); return table != null ? table.isBroadcast() : false;// 没找到表规则,默认为单库,所以不是广播表 } public List<String> getSharedColumns(String logicTable) { TableRule table = getTableRule(logicTable); return table != null ? table.getShardColumns() : new ArrayList<String>();// 没找到表规则,默认为单库 } private TableRule getTableRule(String logicTable) { VirtualTableRoot root = tddlRule.getCurrentRule(); TableRule table = root.getVirtualTable(logicTable); return table; } private String getDefaultDbIndex(String vtab, VirtualTableRoot vtrCurrent) { Map<String, String> dbIndexMap = vtrCurrent.getDbIndexMap(); if (dbIndexMap != null && dbIndexMap.get(vtab) != null) { return dbIndexMap.get(vtab); } return vtrCurrent.getDefaultDbIndex(); } /** * 将一个{@linkplain IFilter}表达式转化为Tddl Rule所需要的{@linkplain Comparative}对象 * * @param ifilter * @param colName * @return */ public static Comparative getComparative(IFilter ifilter, String colName) { // 前序遍历,找到所有符合要求的条件 if (ifilter == null) { return null; } if ("NOT".equalsIgnoreCase(ifilter.getFunctionName())) { return null; } if (ifilter instanceof ILogicalFilter) { if (ifilter.isNot()) { return null; } ComparativeBaseList comp = null; ILogicalFilter logicalFilter = (ILogicalFilter) ifilter; switch (ifilter.getOperation()) { case AND: comp = new ComparativeAND(); break; case OR: comp = new ComparativeOR(); break; default: throw new IllegalArgumentException(); } for (Object sub : logicalFilter.getSubFilter()) { if (!(sub instanceof IFilter)) { return null; } IFilter subFilter = (IFilter) sub; Comparative subComp = getComparative(subFilter, colName);// 递归 if (subComp != null) { comp.addComparative(subComp); } } if (comp == null || comp.getList() == null || comp.getList().isEmpty()) { return null; } else if (comp.getList().size() == 1) { return comp.getList().get(0);// 可能只有自己一个and } return comp; } else if (ifilter instanceof IBooleanFilter) { Comparative comp = null; IBooleanFilter booleanFilter = (IBooleanFilter) ifilter; // 判断非空 if (booleanFilter.getColumn() == null || (booleanFilter.getValue() == null && booleanFilter.getValues() == null)) { return null; } // 判断是否为 A > B , A > B + 1 if (booleanFilter.getColumn() instanceof ISelectable && booleanFilter.getValue() instanceof ISelectable) { return null; } // 判断是否为 A > ALL(subquery) if (booleanFilter.getColumn() instanceof QueryTreeNode || booleanFilter.getValue() instanceof QueryTreeNode) { return null; } // 必须要有一个是字段 if (!(booleanFilter.getColumn() instanceof IColumn || booleanFilter.getValue() instanceof IColumn)) { return null; } if (booleanFilter.isNot()) { return null; } if (booleanFilter.getOperation() == OPERATION.IN) {// in不能出现isReverse ComparativeBaseList orComp = new ComparativeOR(); for (Object value : booleanFilter.getValues()) { IBooleanFilter ef = ASTNodeFactory.getInstance().createBooleanFilter(); ef.setOperation(OPERATION.EQ); ef.setColumn(booleanFilter.getColumn()); ef.setValue(value); Comparative subComp = getComparative(ef, colName); if (subComp != null) { orComp.addComparative(subComp); } } if (orComp.getList().isEmpty()) {// 所有都被过滤 return null; } return orComp; } else { int operationComp = DEFAULT_OPERATION_COMP; switch (booleanFilter.getOperation()) { case GT: operationComp = Comparative.GreaterThan; break; case EQ: operationComp = Comparative.Equivalent; break; case GT_EQ: operationComp = Comparative.GreaterThanOrEqual; break; case LT: operationComp = Comparative.LessThan; break; case LT_EQ: operationComp = Comparative.LessThanOrEqual; break; default: return null; } IColumn column = null; Object value = null; if (booleanFilter.getColumn() instanceof IColumn) { column = OptimizerUtils.getColumn(booleanFilter.getColumn()); value = getComparableWhenTypeIsNowReturnDate(booleanFilter.getValue()); } else {// 出现 1 = id 的写法 column = OptimizerUtils.getColumn(booleanFilter.getValue()); value = getComparableWhenTypeIsNowReturnDate(booleanFilter.getColumn()); operationComp = Comparative.exchangeComparison(operationComp); // 反转一下 } if (colName.equalsIgnoreCase(column.getColumnName()) && operationComp != DEFAULT_OPERATION_COMP) { if (!(value instanceof Comparable)) { throw new TddlRuntimeException("type: " + value.getClass().getSimpleName() + " is not comparable, cannot be used in partition column"); } comp = new Comparative(operationComp, (Comparable) value); } return comp; } } else { // 为null,全表扫描 return null; } } private static Object getComparableWhenTypeIsNowReturnDate(Object val) { if (val instanceof IFunction) { IFunction func = (IFunction) val; if ("NOW".equalsIgnoreCase(func.getFunctionName())) { return new Date(); } } return val; } }