package com.taobao.tddl.optimizer.costbased.chooser;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import com.taobao.tddl.common.model.ExtraCmd;
import com.taobao.tddl.common.utils.GeneralUtil;
import com.taobao.tddl.optimizer.OptimizerContext;
import com.taobao.tddl.optimizer.config.table.IndexMeta;
import com.taobao.tddl.optimizer.config.table.TableMeta;
import com.taobao.tddl.optimizer.core.expression.IFilter;
import com.taobao.tddl.optimizer.core.expression.ISelectable;
import com.taobao.tddl.optimizer.costbased.esitimater.CostEsitimaterFactory;
import com.taobao.tddl.optimizer.costbased.esitimater.stat.KVIndexStat;
import com.taobao.tddl.optimizer.utils.FilterUtils;
import com.taobao.tddl.optimizer.utils.OptimizerUtils;
/**
* 索引选择
*
* <pre>
* 索引选择策略:
* 1. 根据选择条件查询,计算出开销最小
* 2. 根据选择的列,找出全覆盖的索引 (顺序和查询顺序一致,前缀查询)
* </pre>
*
* @author Dreamond
*/
public class IndexChooser {
private static final int initialScore = 10000;
public static IndexMeta findBestIndex(TableMeta tableMeta, List<ISelectable> columns, List<IFilter> filters,
String tablename, Map<String, Object> extraCmd) {
if (!chooseIndex(extraCmd)) {
return null;
}
List<IndexMeta> indexs = tableMeta.getIndexs();
if (indexs.isEmpty()) {
return null;
}
Map<Object, List<IFilter>> columnFilters = FilterUtils.toColumnFiltersMap(filters);
int scores[] = new int[indexs.size()];
for (int i = 0; i < scores.length; i++) {
scores[i] = initialScore;
}
for (int i = 0; i < indexs.size(); i++) {
// 目前不使用弱一致索引
if (!indexs.get(i).isStronglyConsistent()) {
scores[i] = Integer.MAX_VALUE;
continue;
}
KVIndexStat kvIndexStat = OptimizerContext.getContext()
.getStatManager()
.getKVIndex(indexs.get(i).getName());
List<ISelectable> indexColumns = OptimizerUtils.columnMetaListToIColumnList(indexs.get(i).getKeyColumns(),
tablename);
for (int j = 0; j < indexColumns.size(); j++) {
boolean isContain = false;
if (columnFilters.isEmpty()) {// 此时以columns为准
isContain = columns.contains(indexColumns.get(j));
} else {
isContain = columnFilters.containsKey(indexColumns.get(j));
}
if (isContain) {
scores[i] = (int) CostEsitimaterFactory.estimateRowCount(scores[i],
columnFilters.get((indexColumns.get(j))),
indexs.get(i),
kvIndexStat);
scores[i] -= 1; // 命中一个主键字段
} else {
break;
}
}
}
for (int i = 0; i < scores.length; i++) {
scores[i] = initialScore - scores[i];
}
int maxIndex = 0;
int maxScore = scores[maxIndex];
for (int i = 1; i < scores.length; i++) {
if (scores[i] > maxScore) {
maxIndex = i;
maxScore = scores[i];
}
}
if (maxScore == 0) {
return null;
}
return indexs.get(maxIndex);
}
/**
* 根据查询字段,查找一个索引包含所有选择列,并且包含的无关列最少则选择该索引
*/
public static IndexMeta findBestIndexByAllColumnsSelected(TableMeta tableMeta, List<ISelectable> queryColumns,
Map<String, Object> extraCmd) {
if (!chooseIndex(extraCmd)) {
return null;
}
IndexMeta indexChoosed = null;
int theLeastColumnsNumber = Integer.MAX_VALUE;
List<IndexMeta> indexs = tableMeta.getIndexs();
for (int i = 0; i < indexs.size(); i++) {
List<ISelectable> indexColumns = OptimizerUtils.columnMetaListToIColumnList(indexs.get(i).getKeyColumns());
if (indexColumns.containsAll(queryColumns)) {
if (theLeastColumnsNumber > indexs.get(i).getKeyColumns().size()) {
theLeastColumnsNumber = indexs.get(i).getKeyColumns().size();
indexChoosed = indexs.get(i);
}
}
}
return indexChoosed;
}
private static boolean chooseIndex(Map<String, Object> extraCmd) {
String ifChooseIndex = ObjectUtils.toString(GeneralUtil.getExtraCmdString(extraCmd,
ExtraCmd.CHOOSE_INDEX));
// 默认返回true
if (StringUtils.isEmpty(ifChooseIndex)) {
return true;
} else {
return BooleanUtils.toBoolean(ifChooseIndex);
}
}
}