package com.taobao.tddl.rule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import com.taobao.tddl.common.model.DBType;
import com.taobao.tddl.rule.Rule.RuleColumn;
import com.taobao.tddl.rule.impl.EnumerativeRule;
import com.taobao.tddl.rule.model.AdvancedParameter;
import com.taobao.tddl.rule.utils.RuleUtils;
import com.taobao.tddl.rule.utils.sample.Samples;
import com.taobao.tddl.rule.virtualnode.DBTableMap;
import com.taobao.tddl.rule.virtualnode.TableSlotMap;
/**
* 类名取名兼容老的rule代码<br/>
* 描述一个逻辑表怎样分库分表,允许指定逻辑表名和db/table的{@linkplain Rule}规则
*
* <pre>
* 一个配置事例:
* -----------
* <!-- 按照user_id取模划分64张表,表明具体为'user_0000'-'user_0063',
* 'user_0000'-'user_0031' 在'TDDL_0000_GROUP'中,
* 'user_0032'-'user_0063' 在'TDDL_0001_GROUP'中 -->
* <bean id="user_bean" class="com.taobao.tddl.rule.VirtualTable">
* <!-- groupKey格式框架,{}中的数将会被dbRuleArray的值替代,并保持位数 -->
* <property name="dbNamePattern" value="TDDL_{0000}_GROUP"/>
* <!-- 具体的分库规则 -->
* <property name="dbRuleArray">
* <!-- 按照user_id取模划分64张表,结果除以32后分到两个库中 -->
* <value>(#user_id,1,64#.longValue() % 64).intdiv(32)</value>
* </property>
* <!-- 具体表名格式框架,{}中的数将会被tbRuleArray的值替代,并保持位数 -->
* <property name="tbNamePattern" value="user_{0000}"></property>
* <!-- 具体的分表规则 -->
* <property name="tbRuleArray">
* <!-- 按照user_id取模划分64张表 -->
* <value>#user_id,1,64#.longValue() % 64</value>
* </property>
* <!-- 全表扫描开关,默认关闭,是否允许应用端在没有给定分表键值的情况下查询全表 -->
* <property name="allowFullTableScan" value="true"/>
* </bean>
* </pre>
*
* @author linxuan
* @author jianghang 2013-11-4 下午5:33:51
* @since 5.0.0
*/
public class TableRule extends VirtualTableSupport implements VirtualTableRule {
/** =================================================== **/
/** == 原始的配置字符串 == **/
/** =================================================== **/
protected String dbNamePattern; // item_{0000}_dbkey
protected String tbNamePattern; // item_{0000}
protected String[] dbRules; // rule配置字符串
protected String[] tbRules; // rule配置字符串
protected List<String> extraPackages; // 自定义用户包
protected boolean allowReverseOutput; // 是否允许反向输出
protected boolean allowFullTableScan = false; // 是否允许全表扫描
protected boolean disableFullTableScan = true; // 是否关闭全表扫描
/**
* 虚拟节点映射
*/
protected TableSlotMap tableSlotMap;
protected DBTableMap dbTableMap;
protected String tableSlotKeyFormat;
/** ============ 运行时变量 ================= **/
protected List<Rule<String>> dbShardRules;
protected List<Rule<String>> tbShardRules;
protected List<String> shardColumns; // 分库字段
protected Object outerContext;
/**
* 是否是个广播表,optimizer模块需要用他来标记某个表是否需要进行复制
*/
protected boolean broadcast = false;
/**
* 相同的join group 应该具有相同的切分规则
*/
protected String joinGroup = null;
public void doInit() {
// 初始化虚拟节点
if (tableSlotMap != null) {
tableSlotMap.setTableSlotKeyFormat(tableSlotKeyFormat);
tableSlotMap.setLogicTable(this.virtualTbName);
tableSlotMap.init();
}
if (dbTableMap != null) {
dbTableMap.setTableSlotKeyFormat(tableSlotKeyFormat);
dbTableMap.setLogicTable(this.virtualTbName);
dbTableMap.init();
}
// 处理一下占位符
replaceWithParam(this.dbRules, dbRuleParames != null ? dbRuleParames : ruleParames);
replaceWithParam(this.tbRules, tbRuleParames != null ? tbRuleParames : ruleParames);
// 构造一下Rule对象
String packagesStr = buildExtraPackagesStr(extraPackages);
if (dbShardRules == null) { // 如果未设置Rule对象,基于string生成rule对象
setDbShardRules(convertToRuleArray(dbRules, dbNamePattern, packagesStr, dbTableMap, tableSlotMap, false));
}
if (tbShardRules == null) {
setTbShardRules(convertToRuleArray(tbRules, tbNamePattern, packagesStr, dbTableMap, tableSlotMap, true));
}
if (tbShardRules == null || tbShardRules.size() == 0) {
if (this.tbNamePattern == null) {
// 表规则没有,tbKeyPattern为空
this.tbNamePattern = this.virtualTbName;
}
}
// 构造一下分区字段
buildShardColumns();
// 基于rule计算一下拓扑结构
initActualTopology();
}
public void initActualTopology() {
if (actualTopology != null) {
// 将逗号分隔的表名转换为Set
for (Map.Entry<String/* 库 */, Set<String/* 表 */>> e : this.actualTopology.entrySet()) {
if (e.getValue().size() == 1) { // 如果只有一个元素,则怀疑为逗号分隔的
Set<String> tables = new LinkedHashSet<String>();
tables.addAll(Arrays.asList(e.getValue().iterator().next().split(tableNameSepInSpring)));
e.setValue(tables);
}
}
showTopology(false);
return; // 用户显式设置的优先
}
actualTopology = new TreeMap<String, Set<String>>();
if (RuleUtils.isEmpty(dbShardRules) && RuleUtils.isEmpty(tbShardRules)) { // 啥规则都没有
Set<String> tbs = new TreeSet<String>();
tbs.add(this.tbNamePattern);
actualTopology.put(this.dbNamePattern, tbs);
} else if (RuleUtils.isEmpty(dbShardRules)) { // 没有库规则
Set<String> tbs = new TreeSet<String>();
for (Rule<String> tbRule : tbShardRules) {
tbs.addAll(vbvRule(tbRule, getEnumerates(tbRule)));
}
actualTopology.put(this.dbNamePattern, tbs);
} else if (RuleUtils.isEmpty(tbShardRules)) {// 没有表规则
Set<String> tbs = new TreeSet<String>();
tbs.add(this.tbNamePattern);
for (Rule<String> dbRule : dbShardRules) {
for (String dbIndex : vbvRule(dbRule, getEnumerates(dbRule))) {
actualTopology.put(dbIndex, tbs);
}
}
} else { // 库表规则都有
for (Rule<String> dbRule : dbShardRules) {
for (Rule<String> tbRule : tbShardRules) {
if (this.tableSlotMap != null && this.dbTableMap != null) { // 存在vnode
valueByValue(this.actualTopology, dbRule, tbRule, true);
} else {
valueByValue(this.actualTopology, dbRule, tbRule, false);
}
}
}
}
// 打印一下
showTopology(true);
}
// ==================== build topology =====================
// 计算一下规则
private Set<String> vbvRule(Rule<String> rule, Samples samples) {
Set<String> tbs = new TreeSet<String>();
for (Map<String, Object> sample : samples) {
tbs.add(rule.eval(sample, null));
}
return tbs;
}
// 计算一下规则
private Set<String> vbvRule(Rule<String> rule, Map<String, Set<Object>> enumerates) {
Set<String> tbs = new TreeSet<String>();
for (Map<String, Object> sample : new Samples(enumerates)) {
tbs.add(rule.eval(sample, null));
}
return tbs;
}
// 计算一下规则,同时记录对应计算参数
private Map<String, Samples> vbvTrace(Rule<String> rule, Map<String, Set<Object>> enumerates) {
Map<String, Samples> db2Samples = new TreeMap<String, Samples>();
Samples dbSamples = new Samples(enumerates);
for (Map<String, Object> sample : dbSamples) {
String v = rule.eval(sample, null);
Samples s = db2Samples.get(v);
if (s == null) {
s = new Samples(sample.keySet());
db2Samples.put(v, s);
}
s.addSample(sample);
}
return db2Samples;
}
private void valueByValue(Map<String, Set<String>> topology, Rule<String> dbRule, Rule<String> tbRule,
boolean isVnode) {
Map<String/* 列名 */, Set<Object>> dbEnumerates = getEnumerates(dbRule);
Map<String/* 列名 */, Set<Object>> tbEnumerates = getEnumerates(tbRule);
// 以table rule列为基准
Set<AdvancedParameter> params = RuleUtils.cast(tbRule.getRuleColumnSet());
for (AdvancedParameter tbap : params) {
if (dbEnumerates.containsKey(tbap.key)) {
// 库表规则的公共列名,表枚举值要涵盖所有库枚举值跨越的范围= =!
Set<Object> tbValuesBasedONdbValue = new HashSet<Object>();
for (Object dbValue : dbEnumerates.get(tbap.key)) {
tbValuesBasedONdbValue.addAll(tbap.enumerateRange(dbValue));
}
dbEnumerates.get(tbap.key).addAll(tbValuesBasedONdbValue);
} else {
dbEnumerates.put(tbap.key, tbEnumerates.get(tbap.key));
}
}
// 有虚拟节点的话按照虚拟节点计算
if (isVnode) {
Samples tabSamples = new Samples(tbEnumerates);
Set<String> tbs = new TreeSet<String>();
for (Map<String, Object> sample : tabSamples) {
String value = tbRule.eval(sample, null);
tbs.add(value);
}
for (String table : tbs) {
Map<String, Object> sample = new HashMap<String, Object>(1);
sample.put(EnumerativeRule.REAL_TABLE_NAME_KEY, table);
String db = dbRule.eval(sample, null);
if (topology.get(db) == null) {
Set<String> tabs = new HashSet<String>();
tabs.add(table);
topology.put(db, tabs);
} else {
topology.get(db).add(table);
}
}
return;
} else {
// 没有虚拟节点按正常走
Map<String, Samples> dbs = vbvTrace(dbRule, dbEnumerates);// 库计算结果,与得到结果的输入值集合
for (Map.Entry<String/* 库值 */, Samples> e : dbs.entrySet()) {
Set<String> tbs = topology.get(e.getKey());
if (tbs == null) {
tbs = vbvRule(tbRule, e.getValue());
topology.put(e.getKey(), tbs);
} else {
tbs.addAll(vbvRule(tbRule, e.getValue()));
}
}
}
}
/**
* 根据#id,1,32|512_64#中第三段定义的遍历值范围,枚举规则中每个列的遍历值
*/
private Map<String/* 列名 */, Set<Object>/* 列值 */> getEnumerates(Rule rule) {
Set<AdvancedParameter> params = RuleUtils.cast(rule.getRuleColumnSet());
Map<String/* 列名 */, Set<Object>/* 列值 */> enumerates = new HashMap<String, Set<Object>>(params.size());
for (AdvancedParameter ap : params) {
enumerates.put(ap.key, ap.enumerateRange());
}
return enumerates;
}
private String buildExtraPackagesStr(List<String> extraPackages) {
StringBuilder ep = new StringBuilder("");
if (extraPackages != null) {
int packNum = extraPackages.size();
for (int i = 0; i < packNum; i++) {
ep.append("import ");
ep.append(extraPackages.get(i));
ep.append(";");
}
}
return ep.toString();
}
private void buildShardColumns() {
Set<String> shardColumns = new HashSet<String>();
if (null != dbShardRules) {
for (Rule<String> rule : dbShardRules) {
Map<String, RuleColumn> columRule = rule.getRuleColumns();
if (null != columRule && !columRule.isEmpty()) {
shardColumns.addAll(columRule.keySet());
}
}
}
if (null != tbShardRules) {
for (Rule<String> rule : tbShardRules) {
Map<String, RuleColumn> columRule = rule.getRuleColumns();
if (null != columRule && !columRule.isEmpty()) {
shardColumns.addAll(columRule.keySet());
}
}
}
this.shardColumns = new ArrayList<String>(shardColumns);// 没有表配置,代表没有走分区
}
// ===================== setter / getter ====================
public void setDbRuleArray(List<String> dbRules) {
// 若类型改为String[],spring会自动以逗号分隔,变态!
dbRules = trimRuleString(dbRules);
this.dbRules = dbRules.toArray(new String[dbRules.size()]);
}
public void setTbRuleArray(List<String> tbRules) {
// 若类型改为String[],spring会自动以逗号分隔,变态!
tbRules = trimRuleString(tbRules);
this.tbRules = tbRules.toArray(new String[tbRules.size()]);
}
public void setDbRules(String dbRules) {
if (this.dbRules == null) {
// 优先级比dbRuleArray低
// this.dbRules = dbRules.split("\\|");
this.dbRules = new String[] { dbRules.trim() }; // 废掉|分隔符,没人用且容易造成混乱
}
}
public void setTbRules(String tbRules) {
if (this.tbRules == null) {
// 优先级比tbRuleArray低
// this.tbRules = tbRules.split("\\|");
this.tbRules = new String[] { tbRules.trim() }; // 废掉|分隔符,没人用且容易造成混乱
}
}
public void setRuleParames(String ruleParames) {
if (ruleParames.indexOf('|') != -1) {
// 优先用|线分隔,因为有些规则表达式中会有逗号
this.ruleParames = ruleParames.split("\\|");
} else {
this.ruleParames = ruleParames.split(",");
}
}
public void setDbNamePattern(String dbKeyPattern) {
this.dbNamePattern = dbKeyPattern;
}
public void setTbNamePattern(String tbKeyPattern) {
this.tbNamePattern = tbKeyPattern;
}
public void setExtraPackages(List<String> extraPackages) {
this.extraPackages = extraPackages;
}
public void setOuterContext(Object outerContext) {
this.outerContext = outerContext;
}
public void setAllowReverseOutput(boolean allowReverseOutput) {
this.allowReverseOutput = allowReverseOutput;
}
public boolean isDisableFullTableScan() {
return disableFullTableScan;
}
public void setDisableFullTableScan(boolean disableFullTableScan) {
this.disableFullTableScan = disableFullTableScan;
}
public void setAllowFullTableScan(boolean allowFullTableScan) {
this.allowFullTableScan = allowFullTableScan;
}
public void setDbType(DBType dbType) {
this.dbType = dbType;
}
public void setDbShardRules(List<Rule<String>> dbShardRules) {
this.dbShardRules = dbShardRules;
}
public void setTbShardRules(List<Rule<String>> tbShardRules) {
this.tbShardRules = tbShardRules;
}
public DBType getDbType() {
return dbType;
}
public List getDbShardRules() {
return dbShardRules;
}
public List getTbShardRules() {
return tbShardRules;
}
public Object getOuterContext() {
return outerContext;
}
public TableSlotMap getTableSlotMap() {
return tableSlotMap;
}
public DBTableMap getDbTableMap() {
return dbTableMap;
}
public boolean isAllowReverseOutput() {
return allowReverseOutput;
}
public boolean isAllowFullTableScan() {
return allowFullTableScan;
}
public String getTbNamePattern() {
return tbNamePattern;
}
public String getDbNamePattern() {
return dbNamePattern;
}
public String[] getDbRuleStrs() {
return dbRules;
}
public String[] getTbRulesStrs() {
return tbRules;
}
public void setDbTableMap(DBTableMap dbTableMap) {
this.dbTableMap = dbTableMap;
}
public void setTableSlotMap(TableSlotMap tableSlotMap) {
this.tableSlotMap = tableSlotMap;
}
public void setTableSlotKeyFormat(String tableSlotKeyFormat) {
this.tableSlotKeyFormat = tableSlotKeyFormat;
}
public boolean isBroadcast() {
return broadcast;
}
public void setBroadcast(boolean broadcast) {
this.broadcast = broadcast;
}
public String getJoinGroup() {
return joinGroup;
}
public void setJoinGroup(String joinGroup) {
this.joinGroup = joinGroup;
}
public List<String> getShardColumns() {
return shardColumns;
}
}