Phoenix创建索引源码过程
date: 2020-09-27 13:50:00
updated: 2020-09-28 16:30:00
Phoenix创建索引源码过程
org.apache.phoenix.index.IndexMaintainer
public byte[] buildRowKey(ValueGetter valueGetter, ImmutableBytesWritable rowKeyPtr, byte[] regionStartKey, byte[] regionEndKey, long ts) {
ImmutableBytesWritable ptr = new ImmutableBytesWritable();
//判断是否是构建本地索引,考虑两个条件:1.本地索引是否开启 2.startRK 是否传进来了
boolean prependRegionStartKey = isLocalIndex && regionStartKey != null;
boolean isIndexSalted = !isLocalIndex && nIndexSaltBuckets > 0;
//如果开启本地索引,则在数据前面添加前缀,判断startRK是否是region起始startRK,如果是则使用该region的EndRK
int prefixKeyLength =
prependRegionStartKey ? (regionStartKey.length != 0 ? regionStartKey.length
: regionEndKey.length) : 0;
TrustedByteArrayOutputStream stream = new TrustedByteArrayOutputStream(estimatedIndexRowKeyBytes + (prependRegionStartKey ? prefixKeyLength : 0));
// 构建数据流对象,对数据进行put
DataOutput output = new DataOutputStream(stream);
如果是本地索引,则在rowkey前加入startrowkey索引
// For local indexes, we must prepend the row key with the start region key
if (prependRegionStartKey) {
if (regionStartKey.length == 0) {
// 如果startRK为null,则其实使用的endRK
output.write(new byte[prefixKeyLength]);
} else {
output.write(regionStartKey);
}
}
判断是否有加盐,如果有,则增加一个标志位,后面再更改这个标志位
if (isIndexSalted) {
output.write(0); // will be set at end to index salt byte
}
如果在索引视图id不为null,会在索引rowkey中加入视图id
if (viewIndexId != null) {
output.write(viewIndexId);
}
dataRowKeySchema是数据表的信息,忽略在视图变量的中常量值,并标记出原表pk的rowkey的offset 和 length,方便后面定位数据表rowkey插入。
for (int i = dataPosOffset; i < dataRowKeySchema.getFieldCount(); i++) {
Boolean hasValue=dataRowKeySchema.next(ptr, i, maxRowKeyOffset);
// Ignore view constants from the data table, as these
// don't need to appear in the index (as they're the
// same for all rows in this index)
if (!viewConstantColumnBitSet.get(i)) {
int pos = rowKeyMetaData.getIndexPkPosition(i-dataPosOffset);
if (Boolean.TRUE.equals(hasValue)) {
dataRowKeyLocator[0][pos] = ptr.getOffset();
dataRowKeyLocator[1][pos] = ptr.getLength();
} else {
dataRowKeyLocator[0][pos] = 0;
dataRowKeyLocator[1][pos] = 0;
}
}
}
考虑索引的数据的顺序
// 获取表达式索引,表达式索引默认值都为1,未开启的时候isNullAble为true
Iterator<Expression> expressionIterator = indexedExpressions.iterator();
// nIndexedColumns 的构成是索引列+主键 如果是组合索引,则循环多个索引列
for (int i = 0; i < nIndexedColumns; i++) {
PDataType dataColumnType;
boolean isNullable;
SortOrder dataSortOrder;
// dataPkPosition为-1则表示为表达式索引,否则为属性索引
if (dataPkPosition[i] == EXPRESSION_NOT_PRESENT) {
Expression expression = expressionIterator.next();
dataColumnType = expression.getDataType();
dataSortOrder = expression.getSortOrder();
isNullable = expression.isNullable();
expression.evaluate(new ValueGetterTuple(valueGetter, ts), ptr);
}
// 主键pk 走这个分支
else {
Field field = dataRowKeySchema.getField(dataPkPosition[i]);
dataColumnType = field.getDataType();
ptr.set(rowKeyPtr.get(), dataRowKeyLocator[0][i], dataRowKeyLocator[1][i]);
dataSortOrder = field.getSortOrder();
isNullable = field.isNullable();
}
// 考虑列值的顺序,考虑字节的比较,考虑索引列的顺序
// 判断查询是否desc,默认为asc。
boolean isDataColumnInverted = dataSortOrder != SortOrder.ASC;
// 获取索引列的的数据类型,详情看后面getIndexColumnDataType函数
PDataType indexColumnType = IndexUtil.getIndexColumnDataType(isNullable, dataColumnType);
//根据数据列返回不同的datatype,判断该列是否可比较。不可比较的列有decimal,varchar,boolean,Binary
boolean isBytesComparable = dataColumnType.isBytesComparableWith(indexColumnType);
// 获取列是否是逆序的
boolean isIndexColumnDesc = descIndexColumnBitSet.get(i);
if (isBytesComparable && isDataColumnInverted == isIndexColumnDesc) {
output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
} else {
if (!isBytesComparable) {
// 让不可比较的类型具有可比性
indexColumnType.coerceBytes(ptr, dataColumnType, dataSortOrder, SortOrder.getDefault());
}
// 按位取异或值,二进制数比较肯定是字典序,从最高位开始比较,直到遇到第一个不一样的位,这个位上哪个数等于1哪个数就较大。
if (isDataColumnInverted != isIndexColumnDesc) {
writeInverted(ptr.get(), ptr.getOffset(), ptr.getLength(), output);
} else {
output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
}
}
// 判断数据是不是一个固定长度的字段,如果不是根据数据的正序逆序添加一个标志位
if (!indexColumnType.isFixedWidth()) {
output.writeByte(SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, ptr.getLength() == 0, isIndexColumnDesc ? SortOrder.DESC : SortOrder.ASC));
}
}
填充开始的加盐部分的字节位,规则是根据数据做hash,然后再对nIndexSaltBuckets取余
if (isIndexSalted) {
// Set salt byte
byte saltByte = SaltingUtil.getSaltingByte(indexRowKey, SaltingUtil.NUM_SALTING_BYTES, length-SaltingUtil.NUM_SALTING_BYTES, nIndexSaltBuckets);
indexRowKey[0] = saltByte;
}
返回所有的生成的rowkey
return indexRowKey.length == length ? indexRowKey : Arrays.copyOf(indexRowKey, length);
根据数据列返回不同的datatype,判断该列是否可比较。不可比较的列有decimal,varchar,boolean,Binary等
// Since we cannot have nullable fixed length in a row key
// we need to translate to variable length. The verification that we have a valid index
// row key was already done, so here we just need to convert from one built-in type to
// another.
public static PDataType getIndexColumnDataType(boolean isNullable, PDataType dataType) {
if (dataType == null || !isNullable || !dataType.isFixedWidth()) {
return dataType;
}
// for fixed length numeric types and boolean
if (dataType.isCastableTo(PDecimal.INSTANCE)) {
return PDecimal.INSTANCE;
}
// for CHAR
if (dataType.isCoercibleTo(PVarchar.INSTANCE)) {
return PVarchar.INSTANCE;
}
if (PBinary.INSTANCE.equals(dataType)) {
return PVarbinary.INSTANCE;
}
throw new IllegalArgumentException("Unsupported non nullable type " + dataType);
}
让数据有可比性
protected static int toBytes(BigDecimal v, byte[] result, final int offset, int length) {
// From scale to exponent byte (if BigDecimal is positive): (-(scale+(scale % 2 == 0 : 0 : 1)) / 2 + 65) | 0x80
// If scale % 2 is 1 (i.e. it's odd), then multiple last base-100 digit by 10
// For example: new BigDecimal(BigInteger.valueOf(1), -4);
// (byte)((-(-4+0) / 2 + 65) | 0x80) = -61
// From scale to exponent byte (if BigDecimal is negative): ~(-(scale+1)/2 + 65 + 128) & 0x7F
// For example: new BigDecimal(BigInteger.valueOf(1), 2);
// ~(-2/2 + 65 + 128) & 0x7F = 63
Phoenix创建索引源码过程的更多相关文章
- SparkConf加载与SparkContext创建(源码阅读四)
sparkContext创建还没完呢,紧接着前两天,我们继续探索..作死... 紧接着前几天我们继续SparkContext的创建: 接下来从这里我们可以看到,spark开始加载hadoop的配置信息 ...
- SparkConf加载与SparkContext创建(源码阅读一)
即日起开始spark源码阅读之旅,这个过程是相当痛苦的,也许有大量的看不懂,但是每天一个方法,一点点看,相信总归会有极大地提高的.那么下面开始: 创建sparkConf对象,那么究竟它干了什么了类,从 ...
- nova创建虚拟机源码分析系列之七 传入参数转换成内部id
上一篇博文将nova创建虚机的流程推进到了/compute/api.py中的create()函数,接下来就继续分析. 在分析之前简单介绍nova组件源码的架构.以conductor组件为例: 每个组件 ...
- nova创建虚拟机源码分析系列之五 nova源码分发实现
前面讲了很多nova restful的功能,无非是为本篇博文分析做铺垫.本节说明nova创建虚拟机的请求发送到openstack之后,nova是如何处理该条URL的请求,分析到处理的类. nova对于 ...
- nova创建虚拟机源码分析系列之三 PasteDeploy
上一篇博文介绍WSGI在nova创建虚拟机过程的作用是解析URL,是以一个最简单的例子去给读者有一个印象.在openstack中URL复杂程度也大大超过上一个例子.所以openstack使用了Past ...
- nova创建虚拟机源码分析系列之一 restful api
开始学习openstack源码,源码文件多,分支不少.按照学习的方法走通一条线是最好的,而网上推荐的最多的就是nova创建虚机的过程.从这一条线入手,能够贯穿openstack核心服务.写博文仅做学习 ...
- IDEA创建Tomcat8源码工程流程
上一篇文章的产出,其实离不开网上各位大神们的辅助,正是通过他们的讲解,我才对Tomcat的结构有了更进一步的认识. 但在描述前后端交互的过程中,还有很多细节并没有描述到位,所以就有了研究Tomcat源 ...
- [原]编译Android源码过程中遇到的问题
编译Android源码的过程参考Android官网介绍: 1.下载Android源码的步骤:https://source.android.com/source/downloading.html 2.编 ...
- ubuntu13.04下载android4.0.1源码过程
最初我参考的是老罗的博客http://blog.csdn.net/luoshengyang/article/details/6559955 进行下载安装的,但弄着弄着就发现不太对劲了.这里记录下详细过 ...
随机推荐
- BeautifulSoup与Xpath
BeautifulSoup 一 简介 简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据.官方解释如下: ''' Beautiful Soup提供一些简单的.p ...
- 手把手教你配置git和git仓库
今天是git专题的第二篇,我们来介绍一下git的基本配置,以及建立一个git仓库的基本方法. 首先申明一点,本文不会介绍git的安装.一方面是大部分个人PC的系统当中都是已经装好了git的,另外一方面 ...
- 原子类CAS的底层实现
原子类使用 public class CASDemo { public static void main(String[] args) { AtomicInteger atomicInteger = ...
- 利用Z.Expressions.Eval表达式求值
Z.Expression.Eval是一个开源的(OpenSource),可扩展的(Extensible),超轻量级(Super lightweight)的公式化语言解析执行工具包. 使用方法:1.从n ...
- 消息队列MQ面试专题(rabbitmq)
正文: 1.什么是 rabbitmq 采用 AMQP 高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦 2.为什么要使用 rabbitmq 在分布 ...
- springboot整合Mangodb实现crud,高级查询,分页,排序,简单聚合
//linux安装mangodb教程:https://www.cnblogs.com/yangxiaohui227/p/11347832.html 1.引入maven 依赖 <dependenc ...
- Go-变量-var
什么是变量? 一种抽象,计算机用来保存现实数据的容器,通过这个变量抽象可以写入现实数据到计算机中,并且可以读取变量取到保存到计算机中的现实数字化数据 Go-变量定义 关键字 var 关键符号 := i ...
- Python-找字典中公共key-zip reduce lambda
场景实例: 西班牙足球联赛,每轮球员进球统计: 第一轮:{'1':1,'2':4,'5':2,'7':3} 第一轮:{'2':1,'5':4,'6':2,'3':3} 第一轮:{'1':1,'4':4 ...
- 决策树防止过拟合(预剪枝(Pre-Pruning))
预剪枝(Pre-Pruning):预剪枝就是在构造决策树的过程中,先对每个结点在划分前进行估计,若果当前结点的划分不能带来决策树模型泛华性能的提升,则不对当前结点进行划分并且将当前结点标记为叶结点.
- 《C++primerplus》第6章练习题
本来前面五题都做完了,写博客时没保存好草稿= =,写了个整合版的程序,实现前五题的关键部分. 1.定义一个叫jojo的结构,存储姓名.替身和力量值,使用动态结构数组初始化二乔.承太郎和乔鲁诺乔巴纳等人 ...