phoenix jdbc driver查询源码分析
来源:互联网 发布:网页美工的工作内容 编辑:程序博客网 时间:2024/05/22 07:58
phoenix driver查询源码分析
首先 phoenix 支持jbbc协议,则就要按装jdbc的协议接口进行处理。
首先要实现driver PhoenixDriver,这个driver负责连接的创建,和连接到目标
服务器端的链接创建。
在该类中有一个创建链接的方法
protected final Connection createConnection(String url, Properties info) throws SQLException { Properties augmentedInfo = PropertiesUtil.deepCopy(info); augmentedInfo.putAll(getDefaultProps().asMap()); ConnectionQueryServices connectionServices = getConnectionQueryServices(url, augmentedInfo); PhoenixConnection connection = connectionServices.connect(url, augmentedInfo); return connection;}
然后拿到链接对象进行创建链接
@Overrideprotected ConnectionQueryServices getConnectionQueryServices(String url, Properties info) throws SQLException { try { lockInterruptibly(LockMode.READ); checkClosed(); ConnectionInfo connInfo = ConnectionInfo.create(url); QueryServices services = getQueryServices(); ConnectionInfo normalizedConnInfo = connInfo.normalize(services.getProps()); ConnectionQueryServices connectionQueryServices = connectionQueryServicesMap.get(normalizedConnInfo); if (connectionQueryServices == null) { if (normalizedConnInfo.isConnectionless()) { connectionQueryServices = new ConnectionlessQueryServicesImpl(services, normalizedConnInfo, info); } else { connectionQueryServices = new ConnectionQueryServicesImpl(services, normalizedConnInfo, info); } ConnectionQueryServices prevValue = connectionQueryServicesMap.putIfAbsent(normalizedConnInfo, connectionQueryServices); if (prevValue != null) { connectionQueryServices = prevValue; } }
在 ConnectionInfo connInfo = ConnectionInfo.create(url);这个方法当中,解释url中的zk地址,
然后创建链接查询对象
connectionQueryServices = new ConnectionQueryServicesImpl(services, normalizedConnInfo, info);
在 ConnectionQueryServicesImpl 开始进行 init(final String url, final Properties props) 初始化
首先打开链接
private void openConnection() throws SQLException { try { // check if we need to authenticate with kerberos String clientKeytab = this.getProps().get(HBASE_CLIENT_KEYTAB); String clientPrincipal = this.getProps().get(HBASE_CLIENT_PRINCIPAL); if (clientKeytab != null && clientPrincipal != null) { logger.info("Trying to connect to a secure cluster with keytab:" + clientKeytab); UserGroupInformation.setConfiguration(config); User.login(config, HBASE_CLIENT_KEYTAB, HBASE_CLIENT_PRINCIPAL, null); logger.info("Successfull login to secure cluster!!"); } boolean transactionsEnabled = props.getBoolean( QueryServices.TRANSACTIONS_ENABLED, QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED); // only initialize the tx service client if needed if (transactionsEnabled) { initTxServiceClient(); } this.connection = HBaseFactoryProvider.getHConnectionFactory().createConnection(this.config); } catch (IOException e) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ESTABLISH_CONNECTION) .setRootCause(e).build().buildException(); } if (this.connection.isClosed()) { // TODO: why the heck doesn't this throw above? throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ESTABLISH_CONNECTION).build().buildException(); }}
可以看到,打开的connection 是 org.apache.hadoop.hbase.client.HConnection; 是创建到hbase的链接了
接着包装了一下链接对象
metaConnection = new PhoenixConnection( ConnectionQueryServicesImpl.this, globalUrl, scnProps, newEmptyMetaData());
可以看下该对象的初始化,phoenix里面的很多特性都是在这里可以看到缩影
public PhoenixConnection(ConnectionQueryServices services, String url, Properties info, PMetaData metaData, MutationState mutationState, boolean isDescVarLengthRowKeyUpgrade) throws SQLException { this.url = url; this.isDescVarLengthRowKeyUpgrade = isDescVarLengthRowKeyUpgrade; // Copy so client cannot change this.info = info == null ? new Properties() : PropertiesUtil.deepCopy(info); final PName tenantId = JDBCUtil.getTenantId(url, info); if (this.info.isEmpty() && tenantId == null) { this.services = services; } else { // Create child services keyed by tenantId to track resource usage for // a tenantId for all connections on this JVM. if (tenantId != null) { services = services.getChildQueryServices(tenantId.getBytesPtr()); } ReadOnlyProps currentProps = services.getProps(); final ReadOnlyProps augmentedProps = currentProps.addAll(filterKnownNonProperties(this.info)); this.services = augmentedProps == currentProps ? services : new DelegateConnectionQueryServices(services) { @Override public ReadOnlyProps getProps() { return augmentedProps; } }; } Long scnParam = JDBCUtil.getCurrentSCN(url, this.info); checkScn(scnParam); this.scn = scnParam; this.isAutoFlush = this.services.getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED) && this.services.getProps().getBoolean(QueryServices.AUTO_FLUSH_ATTRIB, QueryServicesOptions.DEFAULT_AUTO_FLUSH) ; this.isAutoCommit = JDBCUtil.getAutoCommit( url, this.info, this.services.getProps().getBoolean( QueryServices.AUTO_COMMIT_ATTRIB, QueryServicesOptions.DEFAULT_AUTO_COMMIT)); this.consistency = JDBCUtil.getConsistencyLevel(url, this.info, this.services.getProps() .get(QueryServices.CONSISTENCY_ATTRIB, QueryServicesOptions.DEFAULT_CONSISTENCY_LEVEL)); this.tenantId = tenantId; this.mutateBatchSize = JDBCUtil.getMutateBatchSize(url, this.info, this.services.getProps()); datePattern = this.services.getProps().get(QueryServices.DATE_FORMAT_ATTRIB, DateUtil.DEFAULT_DATE_FORMAT); timePattern = this.services.getProps().get(QueryServices.TIME_FORMAT_ATTRIB, DateUtil.DEFAULT_TIME_FORMAT); timestampPattern = this.services.getProps().get(QueryServices.TIMESTAMP_FORMAT_ATTRIB, DateUtil.DEFAULT_TIMESTAMP_FORMAT); String numberPattern = this.services.getProps().get(QueryServices.NUMBER_FORMAT_ATTRIB, NumberUtil.DEFAULT_NUMBER_FORMAT); int maxSize = this.services.getProps().getInt(QueryServices.MAX_MUTATION_SIZE_ATTRIB,QueryServicesOptions.DEFAULT_MAX_MUTATION_SIZE); Format dateFormat = DateUtil.getDateFormatter(datePattern); Format timeFormat = DateUtil.getDateFormatter(timePattern); Format timestampFormat = DateUtil.getDateFormatter(timestampPattern); formatters.put(PDate.INSTANCE, dateFormat); formatters.put(PTime.INSTANCE, timeFormat); formatters.put(PTimestamp.INSTANCE, timestampFormat); formatters.put(PUnsignedDate.INSTANCE, dateFormat); formatters.put(PUnsignedTime.INSTANCE, timeFormat); formatters.put(PUnsignedTimestamp.INSTANCE, timestampFormat); formatters.put(PDecimal.INSTANCE, FunctionArgumentType.NUMERIC.getFormatter(numberPattern)); // We do not limit the metaData on a connection less than the global one, // as there's not much that will be cached here. Pruner pruner = new Pruner() { @Override public boolean prune(PTable table) { long maxTimestamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn; return (table.getType() != PTableType.SYSTEM && ( table.getTimeStamp() >= maxTimestamp || ! Objects.equal(tenantId, table.getTenantId())) ); } @Override public boolean prune(PFunction function) { long maxTimestamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn; return ( function.getTimeStamp() >= maxTimestamp || ! Objects.equal(tenantId, function.getTenantId())); } }; this.isRequestLevelMetricsEnabled = JDBCUtil.isCollectingRequestLevelMetricsEnabled(url, info, this.services.getProps()); this.mutationState = mutationState == null ? newMutationState(maxSize) : new MutationState(mutationState); this.metaData = metaData.pruneTables(pruner); this.metaData = metaData.pruneFunctions(pruner); this.services.addConnection(this); // setup tracing, if its enabled this.sampler = Tracing.getConfiguredSampler(this); this.customTracingAnnotations = getImmutableCustomTracingAnnotations(); this.scannerQueue = new LinkedBlockingQueue<>(); this.tableResultIteratorFactory = new DefaultTableResultIteratorFactory(); GLOBAL_OPEN_PHOENIX_CONNECTIONS.increment();}
在上面的代码中可以到 tenantId (承租)这涉及到使用人员的资源分配的问题.
scnParam 属性,涉及到当时hbase的时间戳的问题
.consistency 属性,涉及到数据的一致性问题
.所以其它phoenix对hbase的查询就是在hbase的connection上面包装了一层的代理,增量自己的特性。
当链接创建成功后,就要开始初始化phoenix的元数据表。在类 ConnectionQueryServicesImpl.init 的方法中
进行相关的元数据表的初始化,判断相关的元数据表是否已经创建,如果没有创建就进行创建
metaConnection.createStatement().executeUpdate(QueryConstants.CREATE_TABLE_METADATA);CREATE_TABLE_METADATA = CREATE TABLE " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE +
上面就是对system.catalog的目录表的创建。
首先创建 statement,就是下面的对象
PhoenixStatement statement = new PhoenixStatement(this); @Overridepublic int executeUpdate(String sql) throws SQLException { CompilableStatement stmt = parseStatement(sql); if (!stmt.getOperation().isMutation) { throw new ExecuteUpdateNotApplicableException(sql); } if (!batch.isEmpty()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.EXECUTE_UPDATE_WITH_NON_EMPTY_BATCH) .build().buildException(); } int updateCount = executeMutation(stmt); flushIfNecessary(); return updateCount;}
然后进行sql的编译
parser = new PhoenixStatementParser(sql, new ExecutableNodeFactory());
创建了上面的对象对sql进行了解释,接着到 executeMutation方法中进行执行
new CallRunner.CallableThrowable<Integer, SQLException>() { @Override public Integer call() throws SQLException { try { MutationState state = connection.getMutationState(); //这里进行编译成执行计划 MutationPlan plan = stmt.compilePlan(PhoenixStatement.this, Sequence.ValueOp.VALIDATE_SEQUENCE); if (plan.getTargetRef() != null && plan.getTargetRef().getTable() != null && plan.getTargetRef().getTable().isTransactional()) { state.startTransaction(); } Iterator<TableRef> tableRefs = plan.getSourceRefs().iterator(); state.sendUncommitted(tableRefs); state.checkpointIfNeccessary(plan); MutationState lastState = plan.execute(); state.join(lastState); if (connection.getAutoCommit()) { connection.commit();; } setLastResultSet(null); setLastQueryPlan(null); // Unfortunately, JDBC uses an int for update count, so we // just max out at Integer.MAX_VALUE int lastUpdateCount = (int) Math.min(Integer.MAX_VALUE, lastState.getUpdateCount()); setLastUpdateCount(lastUpdateCount); setLastUpdateOperation(stmt.getOperation()); connection.incrementStatementExecutionCounter(); return lastUpdateCount;
可以分析的到之前的stmt是 ExecutableCreateTableStatement 对象的
public MutationPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException { CreateTableCompiler compiler = new CreateTableCompiler(stmt, this.getOperation()); return compiler.compile(this); }
然后创建了执行计划
public MutationPlan compile(final CreateTableStatement create) throws SQLException { final PhoenixConnection connection = statement.getConnection(); ColumnResolver resolver = FromCompiler.getResolverForCreation(create, connection); PTableType type = create.getTableType(); PhoenixConnection connectionToBe = connection; PTable parentToBe = null; ViewType viewTypeToBe = null; Scan scan = new Scan(); final StatementContext context = new StatementContext(statement, resolver, scan, new SequenceManager(statement)); // TODO: support any statement for a VIEW instead of just a WHERE clause ParseNode whereNode = create.getWhereClause(); String viewStatementToBe = null; byte[][] viewColumnConstantsToBe = null; BitSet isViewColumnReferencedToBe = null; if (type == PTableType.VIEW) { TableRef tableRef = resolver.getTables().get(0); int nColumns = tableRef.getTable().getColumns().size(); isViewColumnReferencedToBe = new BitSet(nColumns); // Used to track column references in a view ExpressionCompiler expressionCompiler = new ColumnTrackingExpressionCompiler(context, isViewColumnReferencedToBe); parentToBe = tableRef.getTable(); viewTypeToBe = parentToBe.getViewType() == ViewType.MAPPED ? ViewType.MAPPED : ViewType.UPDATABLE; if (whereNode == null) { viewStatementToBe = parentToBe.getViewStatement(); } else { whereNode = StatementNormalizer.normalize(whereNode, resolver); if (whereNode.isStateless()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WHERE_IS_CONSTANT) .build().buildException(); } // If our parent has a VIEW statement, combine it with this one if (parentToBe.getViewStatement() != null) { SelectStatement select = new SQLParser(parentToBe.getViewStatement()).parseQuery().combine(whereNode); whereNode = select.getWhere(); } Expression where = whereNode.accept(expressionCompiler); if (where != null && !LiteralExpression.isTrue(where)) { TableName baseTableName = create.getBaseTableName(); StringBuilder buf = new StringBuilder(); whereNode.toSQL(resolver, buf); viewStatementToBe = QueryUtil.getViewStatement(baseTableName.getSchemaName(), baseTableName.getTableName(), buf.toString()); } if (viewTypeToBe != ViewType.MAPPED) { Long scn = connection.getSCN(); connectionToBe = (scn != null || tableRef.getTable().isTransactional()) ? connection : // If we haved no SCN on our connection and the base table is not transactional, freeze the SCN at when // the base table was resolved to prevent any race condition on // the error checking we do for the base table. The only potential // issue is if the base table lives on a different region server // than the new table will, then we're relying here on the system // clocks being in sync. new PhoenixConnection( // When the new table is created, we still want to cache it // on our connection. new DelegateConnectionQueryServices(connection.getQueryServices()) { @Override public PMetaData addTable(PTable table, long resolvedTime) throws SQLException { return connection.addTable(table, resolvedTime); } }, connection, tableRef.getTimeStamp()); viewColumnConstantsToBe = new byte[nColumns][]; ViewWhereExpressionVisitor visitor = new ViewWhereExpressionVisitor(parentToBe, viewColumnConstantsToBe); where.accept(visitor); // If view is not updatable, viewColumnConstants should be empty. We will still // inherit our parent viewConstants, but we have no additional ones. viewTypeToBe = visitor.isUpdatable() ? ViewType.UPDATABLE : ViewType.READ_ONLY; if (viewTypeToBe != ViewType.UPDATABLE) { viewColumnConstantsToBe = null; } } } } final ViewType viewType = viewTypeToBe; final String viewStatement = viewStatementToBe; final byte[][] viewColumnConstants = viewColumnConstantsToBe; final BitSet isViewColumnReferenced = isViewColumnReferencedToBe; List<ParseNode> splitNodes = create.getSplitNodes(); final byte[][] splits = new byte[splitNodes.size()][]; ImmutableBytesWritable ptr = context.getTempPtr(); ExpressionCompiler expressionCompiler = new ExpressionCompiler(context); for (int i = 0; i < splits.length; i++) { ParseNode node = splitNodes.get(i); if (node instanceof BindParseNode) { context.getBindManager().addParamMetaData((BindParseNode) node, VARBINARY_DATUM); } if (node.isStateless()) { Expression expression = node.accept(expressionCompiler); if (expression.evaluate(null, ptr)) {; splits[i] = ByteUtil.copyKeyBytesIfNecessary(ptr); continue; } } throw new SQLExceptionInfo.Builder(SQLExceptionCode.SPLIT_POINT_NOT_CONSTANT) .setMessage("Node: " + node).build().buildException(); } final MetaDataClient client = new MetaDataClient(connectionToBe); final PTable parent = parentToBe; return new BaseMutationPlan(context, operation) { @Override public MutationState execute() throws SQLException { try { //这里创建表了 return client.createTable(create, splits, parent, viewStatement, viewType, viewColumnConstants, isViewColumnReferenced); } finally { if (client.getConnection() != connection) { client.getConnection().close(); } } } @Override public ExplainPlan getExplainPlan() throws SQLException { return new ExplainPlan(Collections.singletonList("CREATE TABLE")); } };}
可以看到上面创建的对象
final MetaDataClient client = new MetaDataClient(connectionToBe);
链接到hbase了,然后进行了MetaDataClient.createTable
public MutationState createTable(CreateTableStatement statement, byte[][] splits, PTable parent, String viewStatement, ViewType viewType, byte[][] viewColumnConstants, BitSet isViewColumnReferenced) throws SQLException { PTable table = createTableInternal(statement, splits, parent, viewStatement, viewType, viewColumnConstants, isViewColumnReferenced, null, null, null); if (table == null || table.getType() == PTableType.VIEW || table.isTransactional()) { return new MutationState(0,connection); } // Hack to get around the case when an SCN is specified on the connection. // In this case, we won't see the table we just created yet, so we hack // around it by forcing the compiler to not resolve anything. PostDDLCompiler compiler = new PostDDLCompiler(connection); //connection.setAutoCommit(true); // Execute any necessary data updates Long scn = connection.getSCN(); long ts = (scn == null ? table.getTimeStamp() : scn); // Getting the schema through the current connection doesn't work when the connection has an scn specified // Since the table won't be added to the current connection. TableRef tableRef = new TableRef(null, table, ts, false); byte[] emptyCF = SchemaUtil.getEmptyColumnFamily(table); MutationPlan plan = compiler.compile(Collections.singletonList(tableRef), emptyCF, null, null, tableRef.getTimeStamp()); return connection.getQueryServices().updateData(plan);}
在MetaDataClient.createTableInternal方法体中,要根据创建的不同类型的表,是hbase表还是索引对象还是序列对象,插入相关的元数据表到不同的对象中
PreparedStatement tableUpsert = connection.prepareStatement(CREATE_TABLE); tableUpsert.setString(1, tenantIdStr); tableUpsert.setString(2, schemaName); tableUpsert.setString(3, tableName); tableUpsert.setString(4, tableType.getSerializedValue()); tableUpsert.setLong(5, PTable.INITIAL_SEQ_NUM); tableUpsert.setInt(6, position); if (saltBucketNum != null) { tableUpsert.setInt(7, saltBucketNum); } else { tableUpsert.setNull(7, Types.INTEGER); } tableUpsert.setString(8, pkName); tableUpsert.setString(9, dataTableName); tableUpsert.setString(10, indexState == null ? null : indexState.getSerializedValue()); tableUpsert.setBoolean(11, isImmutableRows); tableUpsert.setString(12, defaultFamilyName); tableUpsert.setString(13, viewStatement); tableUpsert.setBoolean(14, disableWAL); tableUpsert.setBoolean(15, multiTenant); if (viewType == null) { tableUpsert.setNull(16, Types.TINYINT); } else { tableUpsert.setByte(16, viewType.getSerializedValue()); } if (indexId == null) { tableUpsert.setNull(17, Types.SMALLINT); } else { tableUpsert.setShort(17, indexId); } if (indexType == null) { tableUpsert.setNull(18, Types.TINYINT); } else { tableUpsert.setByte(18, indexType.getSerializedValue()); } tableUpsert.setBoolean(19, storeNulls); if (parent != null && tableType == PTableType.VIEW) { tableUpsert.setInt(20, parent.getColumns().size()); } else { tableUpsert.setInt(20, BASE_TABLE_BASE_COLUMN_COUNT); } tableUpsert.setBoolean(21, transactional); tableUpsert.setLong(22, updateCacheFrequency); tableUpsert.execute();CREATE_TABLE = "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE
如该方法,就是插入到目录表中,进行元数据的保存.
可以到 select * from SYSTEM.CATALOG 查到各个表的元数据了。
当在元数据表中插入完成后,就调用如下的方法,真正到hbase数据库中进行创建hbase表
MetaDataMutationResult result = connection.getQueryServices().createTable( tableMetaData, viewType == ViewType.MAPPED || indexId != null ? physicalNames.get(0).getBytes() : null, tableType, tableProps, familyPropList, splits);
开始创建物理表
if ((tableType == PTableType.VIEW && physicalTableName != null) || (tableType != PTableType.VIEW && physicalTableName == null)) { // For views this will ensure that metadata already exists // For tables and indexes, this will create the metadata if it doesn't already exist ensureTableCreated(tableName, tableType, tableProps, families, splits, true); }
在ConnectionQueryServicesImpl.ensureTableCreated方法中
HTableDescriptor newDesc = generateTableDescriptor(tableName, existingDesc, tableType , props, families, splits);
添加协处理器
private void addCoprocessors(byte[] tableName, HTableDescriptor descriptor, PTableType tableType, Map<String,Object> tableProps) throws SQLException { // The phoenix jar must be available on HBase classpath int priority = props.getInt(QueryServices.COPROCESSOR_PRIORITY_ATTRIB, QueryServicesOptions.DEFAULT_COPROCESSOR_PRIORITY); try { if (!descriptor.hasCoprocessor(ScanRegionObserver.class.getName())) { descriptor.addCoprocessor(ScanRegionObserver.class.getName(), null, priority, null); } if (!descriptor.hasCoprocessor(UngroupedAggregateRegionObserver.class.getName())) { descriptor.addCoprocessor(UngroupedAggregateRegionObserver.class.getName(), null, priority, null); } if (!descriptor.hasCoprocessor(GroupedAggregateRegionObserver.class.getName())) { descriptor.addCoprocessor(GroupedAggregateRegionObserver.class.getName(), null, priority, null); } if (!descriptor.hasCoprocessor(ServerCachingEndpointImpl.class.getName())) { descriptor.addCoprocessor(ServerCachingEndpointImpl.class.getName(), null, priority, null); } boolean isTransactional = Boolean.TRUE.equals(tableProps.get(TableProperty.TRANSACTIONAL.name())) || Boolean.TRUE.equals(tableProps.get(TxConstants.READ_NON_TX_DATA)); // For ALTER TABLE // TODO: better encapsulation for this // Since indexes can't have indexes, don't install our indexing coprocessor for indexes. // Also don't install on the SYSTEM.CATALOG and SYSTEM.STATS table because we use // all-or-none mutate class which break when this coprocessor is installed (PHOENIX-1318). if ((tableType != PTableType.INDEX && tableType != PTableType.VIEW) && !SchemaUtil.isMetaTable(tableName) && !SchemaUtil.isStatsTable(tableName)) { if (isTransactional) { if (!descriptor.hasCoprocessor(PhoenixTransactionalIndexer.class.getName())) { descriptor.addCoprocessor(PhoenixTransactionalIndexer.class.getName(), null, priority, null); } // For alter table, remove non transactional index coprocessor if (descriptor.hasCoprocessor(Indexer.class.getName())) { descriptor.removeCoprocessor(Indexer.class.getName()); } } else { if (!descriptor.hasCoprocessor(Indexer.class.getName())) { // If exception on alter table to transition back to non transactional if (descriptor.hasCoprocessor(PhoenixTransactionalIndexer.class.getName())) { descriptor.removeCoprocessor(PhoenixTransactionalIndexer.class.getName()); } Map<String, String> opts = Maps.newHashMapWithExpectedSize(1); opts.put(NonTxIndexBuilder.CODEC_CLASS_NAME_KEY, PhoenixIndexCodec.class.getName()); Indexer.enableIndexing(descriptor, PhoenixIndexBuilder.class, opts, priority); } } } if (SchemaUtil.isStatsTable(tableName) && !descriptor.hasCoprocessor(MultiRowMutationEndpoint.class.getName())) { descriptor.addCoprocessor(MultiRowMutationEndpoint.class.getName(), null, priority, null); } if (descriptor.getValue(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_BYTES) != null && Boolean.TRUE.equals(PBoolean.INSTANCE.toObject(descriptor .getValue(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_BYTES)))) { if (!descriptor.hasCoprocessor(IndexHalfStoreFileReaderGenerator.class.getName())) { descriptor.addCoprocessor(IndexHalfStoreFileReaderGenerator.class.getName(), null, priority, null); } } else { if (!descriptor.hasCoprocessor(LocalIndexSplitter.class.getName()) && !SchemaUtil.isMetaTable(tableName) && !SchemaUtil.isSequenceTable(tableName)) { descriptor.addCoprocessor(LocalIndexSplitter.class.getName(), null, priority, null); } } // Setup split policy on Phoenix metadata table to ensure that the key values of a Phoenix table // stay on the same region. if (SchemaUtil.isMetaTable(tableName) || SchemaUtil.isFunctionTable(tableName)) { if (!descriptor.hasCoprocessor(MetaDataEndpointImpl.class.getName())) { descriptor.addCoprocessor(MetaDataEndpointImpl.class.getName(), null, priority, null); } if(SchemaUtil.isMetaTable(tableName) ) { if (!descriptor.hasCoprocessor(MetaDataRegionObserver.class.getName())) { descriptor.addCoprocessor(MetaDataRegionObserver.class.getName(), null, priority + 1, null); } } } else if (SchemaUtil.isSequenceTable(tableName)) { if (!descriptor.hasCoprocessor(SequenceRegionObserver.class.getName())) { descriptor.addCoprocessor(SequenceRegionObserver.class.getName(), null, priority, null); } } if (isTransactional) { if (!descriptor.hasCoprocessor(PhoenixTransactionalProcessor.class.getName())) { descriptor.addCoprocessor(PhoenixTransactionalProcessor.class.getName(), null, priority - 10, null); } } else { // If exception on alter table to transition back to non transactional if (descriptor.hasCoprocessor(PhoenixTransactionalProcessor.class.getName())) { descriptor.removeCoprocessor(PhoenixTransactionalProcessor.class.getName()); } } } catch (IOException e) { throw ServerUtil.parseServerException(e); }}
从上面看,添加了一系列的协处理器了,phoenix的核心就是使用协处理器的功能进行数据的处理,可以看到上面根据是否是
本地索引表,是否具有事务,进行不同的协处理器的添加
然后就进行hbase表的创建
try { existingDesc = admin.getTableDescriptor(tableName); } catch (org.apache.hadoop.hbase.TableNotFoundException e) { tableExist = false; if (tableType == PTableType.VIEW) { String fullTableName = Bytes.toString(tableName); throw new ReadOnlyTableException( "An HBase table for a VIEW must already exist", SchemaUtil.getSchemaNameFromFullName(fullTableName), SchemaUtil.getTableNameFromFullName(fullTableName)); } } HTableDescriptor newDesc = generateTableDescriptor(tableName, existingDesc, tableType , props, families, splits); //重要的表是否存储判断 if (!tableExist) { if (newDesc.getValue(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_BYTES) != null && Boolean.TRUE.equals( PBoolean.INSTANCE.toObject(newDesc.getValue(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_BYTES)))) { newDesc.setValue(HTableDescriptor.SPLIT_POLICY, IndexRegionSplitPolicy.class.getName()); } // Remove the splitPolicy attribute to prevent HBASE-12570 if (isMetaTable) { newDesc.remove(HTableDescriptor.SPLIT_POLICY); } try { if (splits == null) { admin.createTable(newDesc); } else { admin.createTable(newDesc, splits); } } catch (TableExistsException e) { // We can ignore this, as it just means that another client beat us // to creating the HBase metadata. return null; } if (isMetaTable) { checkClientServerCompatibility(); /* * Now we modify the table to add the split policy, since we know that the client and * server and compatible. This works around HBASE-12570 which causes the cluster to be * brought down. */ newDesc.setValue(HTableDescriptor.SPLIT_POLICY, MetaDataSplitPolicy.class.getName()); if (allowOnlineTableSchemaUpdate()) { // No need to wait/poll for this update admin.modifyTable(tableName, newDesc); } else { admin.disableTable(tableName); admin.modifyTable(tableName, newDesc); admin.enableTable(tableName); } } return null; } else { if (isMetaTable) { checkClientServerCompatibility(); } if (!modifyExistingMetaData) { return existingDesc; // Caller already knows that no metadata was changed } boolean willBeTx = Boolean.TRUE.equals(props.get(TableProperty.TRANSACTIONAL.name())); // If mapping an existing table as transactional, set property so that existing // data is correctly read. if (willBeTx) { newDesc.setValue(TxConstants.READ_NON_TX_DATA, Boolean.TRUE.toString()); } else { // If we think we're creating a non transactional table when it's already // transactional, don't allow. if (existingDesc.hasCoprocessor(PhoenixTransactionalProcessor.class.getName())) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.TX_MAY_NOT_SWITCH_TO_NON_TX) .setSchemaName(SchemaUtil.getSchemaNameFromFullName(tableName)) .setTableName(SchemaUtil.getTableNameFromFullName(tableName)).build().buildException(); } newDesc.remove(TxConstants.READ_NON_TX_DATA); } if (existingDesc.equals(newDesc)) { return null; // Indicate that no metadata was changed } modifyTable(tableName, newDesc, true); return newDesc; }
可以看到,上面先是判断该hbase表是否存在,如果不存在,就创建,否则就进行修改hbase表的属性,这个特性对于
phoenix来说是比较重要的,就是假如hbase该表已经存在,则可以只建立phoenix的类似于视图的表,对原来的表数据不
影响,否则才建立物理表。
当上面真正的表创建完成后,接下来要根据它是否是视图还是物理表,进行索引表的创建
ImmutableBytesWritable ptr = new ImmutableBytesWritable(); if (tableType == PTableType.INDEX) { // Index on view // Physical index table created up front for multi tenant // TODO: if viewIndexId is Short.MIN_VALUE, then we don't need to attempt to create it if (physicalTableName != null) { if (localIndexTable) { ensureLocalIndexTableCreated(tableName, tableProps, families, splits, MetaDataUtil.getClientTimeStamp(m)); } else if (!MetaDataUtil.isMultiTenant(m, kvBuilder, ptr)) { ensureViewIndexTableCreated(tenantIdBytes.length == 0 ? null : PNameFactory.newName(tenantIdBytes), physicalTableName, MetaDataUtil.getClientTimeStamp(m)); } } } else if (tableType == PTableType.TABLE && MetaDataUtil.isMultiTenant(m, kvBuilder, ptr)) { // Create view index table up front for multi tenant tables ptr.set(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES); MetaDataUtil.getMutationValue(m, PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME_BYTES, kvBuilder, ptr); List<Pair<byte[],Map<String,Object>>> familiesPlusDefault = null; for (Pair<byte[],Map<String,Object>> family : families) { byte[] cf = family.getFirst(); if (Bytes.compareTo(cf, 0, cf.length, ptr.get(), ptr.getOffset(),ptr.getLength()) == 0) { familiesPlusDefault = families; break; } } // Don't override if default family already present if (familiesPlusDefault == null) { byte[] defaultCF = ByteUtil.copyKeyBytesIfNecessary(ptr); // Only use splits if table is salted, otherwise it may not be applicable // Always add default column family, as we don't know in advance if we'll need it familiesPlusDefault = Lists.newArrayList(families); familiesPlusDefault.add(new Pair<byte[],Map<String,Object>>(defaultCF,Collections.<String,Object>emptyMap())); } ensureViewIndexTableCreated(tableName, tableProps, familiesPlusDefault, MetaDataUtil.isSalted(m, kvBuilder, ptr) ? splits : null, MetaDataUtil.getClientTimeStamp(m)); }
可以看到上面如果是视图,并且是本地索引表(这个是phoenix的特征,索引表的region是和数据的region是在同一个regionserver实例中的的)
则调用ensureLocalIndexTableCreated 方法进行本地索引表的创建,到最后低层还是调用到
ConnectionQueryServicesImpl.ensureTableCreated方法中进行表的直接创建过程,所以这个是一个核心方法
里面会根据不同的表类型,不同的属性,添加不同的协处理器
当直接要创建的表和涉及到索引表创建完成后,接下来会调用到system.catalog的表的远程rpc协处理器的调用
进行远程的判断,相关的表是否已经创建完成,是否存放在元数据表当中
byte[] tableKey = SchemaUtil.getTableKey(tenantIdBytes, schemaBytes, tableBytes); MetaDataMutationResult result = metaDataCoprocessorExec(tableKey, new Batch.Call<MetaDataService, MetaDataResponse>() { @Override public MetaDataResponse call(MetaDataService instance) throws IOException { ServerRpcController controller = new ServerRpcController(); BlockingRpcCallback<MetaDataResponse> rpcCallback = new BlockingRpcCallback<MetaDataResponse>(); CreateTableRequest.Builder builder = CreateTableRequest.newBuilder(); for (Mutation m : tableMetaData) { MutationProto mp = ProtobufUtil.toProto(m); builder.addTableMetadataMutations(mp.toByteString()); } builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER)); CreateTableRequest build = builder.build(); instance.createTable(controller, build, rpcCallback); if(controller.getFailedOn() != null) { throw controller.getFailedOn(); } return rpcCallback.get(); } });
metaDataCoprocessorExec(tableKey, callable, PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME_BYTES);
所以调用流程是比较严谨的,防止相关的元数据表没有完全正确的创建。
调用到远程的是 MetaDataEndpointImpl.createTable
@Overridepublic void createTable(RpcController controller, CreateTableRequest request, RpcCallback<MetaDataResponse> done) { MetaDataResponse.Builder builder = MetaDataResponse.newBuilder(); byte[][] rowKeyMetaData = new byte[3][]; byte[] schemaName = null; byte[] tableName = null; try { List<Mutation> tableMetadata = ProtobufUtil.getMutations(request); MetaDataUtil.getTenantIdAndSchemaAndTableName(tableMetadata, rowKeyMetaData); byte[] tenantIdBytes = rowKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX]; schemaName = rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX]; tableName = rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX]; byte[] parentSchemaName = null; byte[] parentTableName = null; PTableType tableType = MetaDataUtil.getTableType(tableMetadata, GenericKeyValueBuilder.INSTANCE, new ImmutableBytesWritable()); byte[] parentTableKey = null; Mutation viewPhysicalTableRow = null; if (tableType == PTableType.VIEW) { byte[][] parentSchemaTableNames = new byte[2][]; /* * For a view, we lock the base physical table row. For a mapped view, there is * no link present to the physical table. So the viewPhysicalTableRow is null * in that case. */ viewPhysicalTableRow = getPhysicalTableForView(tableMetadata, parentSchemaTableNames); parentSchemaName = parentSchemaTableNames[0]; parentTableName = parentSchemaTableNames[1]; if (parentTableName != null) { parentTableKey = SchemaUtil.getTableKey(ByteUtil.EMPTY_BYTE_ARRAY, parentSchemaName, parentTableName); } } else if (tableType == PTableType.INDEX) { parentSchemaName = schemaName; /* * For an index we lock the parent table's row which could be a physical table or a view. * If the parent table is a physical table, then the tenantIdBytes is empty because * we allow creating an index with a tenant connection only if the parent table is a view. */ parentTableName = MetaDataUtil.getParentTableName(tableMetadata); parentTableKey = SchemaUtil.getTableKey(tenantIdBytes, parentSchemaName, parentTableName); } Region region = env.getRegion(); List<RowLock> locks = Lists.newArrayList(); // Place a lock using key for the table to be created byte[] tableKey = SchemaUtil.getTableKey(tenantIdBytes, schemaName, tableName); try { acquireLock(region, tableKey, locks); // If the table key resides outside the region, return without doing anything MetaDataMutationResult result = checkTableKeyInRegion(tableKey, region); if (result != null) { done.run(MetaDataMutationResult.toProto(result)); return; } long clientTimeStamp = MetaDataUtil.getClientTimeStamp(tableMetadata); ImmutableBytesPtr parentCacheKey = null; if (parentTableName != null) { // Check if the parent table resides in the same region. If not, don't worry about locking the parent table row // or loading the parent table. For a view, the parent table that needs to be locked is the base physical table. // For an index on view, the view header row needs to be locked. result = checkTableKeyInRegion(parentTableKey, region); if (result == null) { acquireLock(region, parentTableKey, locks); parentCacheKey = new ImmutableBytesPtr(parentTableKey); PTable parentTable = loadTable(env, parentTableKey, parentCacheKey, clientTimeStamp, clientTimeStamp); if (parentTable == null || isTableDeleted(parentTable)) { builder.setReturnCode(MetaDataProtos.MutationCode.PARENT_TABLE_NOT_FOUND); builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis()); done.run(builder.build()); return; } long parentTableSeqNumber; if (tableType == PTableType.VIEW && viewPhysicalTableRow != null && request.hasClientVersion()) { // Starting 4.5, the client passes the sequence number of the physical table in the table metadata. parentTableSeqNumber = MetaDataUtil.getSequenceNumber(viewPhysicalTableRow); } else if (tableType == PTableType.VIEW && !request.hasClientVersion()) { // Before 4.5, due to a bug, the parent table key wasn't available. // So don't do anything and prevent the exception from being thrown. parentTableSeqNumber = parentTable.getSequenceNumber(); } else { parentTableSeqNumber = MetaDataUtil.getParentSequenceNumber(tableMetadata); } // If parent table isn't at the expected sequence number, then return if (parentTable.getSequenceNumber() != parentTableSeqNumber) { builder.setReturnCode(MetaDataProtos.MutationCode.CONCURRENT_TABLE_MUTATION); builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis()); builder.setTable(PTableImpl.toProto(parentTable)); done.run(builder.build()); return; } } } // Load child table next ImmutableBytesPtr cacheKey = new ImmutableBytesPtr(tableKey); // Get as of latest timestamp so we can detect if we have a newer table that already // exists without making an additional query PTable table = loadTable(env, tableKey, cacheKey, clientTimeStamp, HConstants.LATEST_TIMESTAMP); if (table != null) { if (table.getTimeStamp() < clientTimeStamp) { // If the table is older than the client time stamp and it's deleted, // continue if (!isTableDeleted(table)) { builder.setReturnCode(MetaDataProtos.MutationCode.TABLE_ALREADY_EXISTS); builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis()); builder.setTable(PTableImpl.toProto(table)); done.run(builder.build()); return; } } else { builder.setReturnCode(MetaDataProtos.MutationCode.NEWER_TABLE_FOUND); builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis()); builder.setTable(PTableImpl.toProto(table)); done.run(builder.build()); return; } } // Add cell for ROW_KEY_ORDER_OPTIMIZABLE = true, as we know that new tables // conform the correct row key. The exception is for a VIEW, which the client // sends over depending on its base physical table. if (tableType != PTableType.VIEW) { UpgradeUtil.addRowKeyOrderOptimizableCell(tableMetadata, tableKey, clientTimeStamp); } // TODO: Switch this to HRegion#batchMutate when we want to support indexes on the // system table. Basically, we get all the locks that we don't already hold for all the // tableMetadata rows. This ensures we don't have deadlock situations (ensuring // primary and then index table locks are held, in that order). For now, we just don't support // indexing on the system table. This is an issue because of the way we manage batch mutation // in the Indexer. region.mutateRowsWithLocks(tableMetadata, Collections.<byte[]> emptySet(), HConstants.NO_NONCE, HConstants.NO_NONCE); // Invalidate the cache - the next getTable call will add it // TODO: consider loading the table that was just created here, patching up the parent table, and updating the cache Cache<ImmutableBytesPtr,PMetaDataEntity> metaDataCache = GlobalCache.getInstance(this.env).getMetaDataCache(); if (parentCacheKey != null) { metaDataCache.invalidate(parentCacheKey); } metaDataCache.invalidate(cacheKey); // Get timeStamp from mutations - the above method sets it if it's unset long currentTimeStamp = MetaDataUtil.getClientTimeStamp(tableMetadata); builder.setReturnCode(MetaDataProtos.MutationCode.TABLE_NOT_FOUND); builder.setMutationTime(currentTimeStamp); done.run(builder.build()); return; } finally { region.releaseRowLocks(locks); } } catch (Throwable t) { logger.error("createTable failed", t); ProtobufUtil.setControllerException(controller, ServerUtil.createIOException(SchemaUtil.getTableName(schemaName, tableName), t)); }}
上面就是远程的协处理器,里面相关的依赖表进行了逻辑判断,是否同名的父表已经存在,是否有其它表在并发创建了相同的表
等信息。
// Hack to get around the case when an SCN is specified on the connection. // In this case, we won't see the table we just created yet, so we hack // around it by forcing the compiler to not resolve anything. PostDDLCompiler compiler = new PostDDLCompiler(connection); //connection.setAutoCommit(true); // Execute any necessary data updates Long scn = connection.getSCN(); long ts = (scn == null ? table.getTimeStamp() : scn); // Getting the schema through the current connection doesn't work when the connection has an scn specified // Since the table won't be added to the current connection. TableRef tableRef = new TableRef(null, table, ts, false); byte[] emptyCF = SchemaUtil.getEmptyColumnFamily(table); MutationPlan plan = compiler.compile(Collections.singletonList(tableRef), emptyCF, null, null, tableRef.getTimeStamp()); return connection.getQueryServices().updateData(plan);
接下来,在上面的代码中,要执行相关的清理工作,如drop table column ,之类的,要把不是的字段进行清空,
还有更新一个phoenix的statics的数据统计表(这个是phoenix进行执行计划优化时用到的表的统计元数据信息)
connection.setAutoCommit(true); SQLException sqlE = null; /* * Handles: * 1) deletion of all rows for a DROP TABLE and subsequently deletion of all rows for a DROP INDEX; * 2) deletion of all column values for a ALTER TABLE DROP COLUMN * 3) updating the necessary rows to have an empty KV * 4) updating table stats */ long totalMutationCount = 0; for (final TableRef tableRef : tableRefs) { Scan scan = ScanUtil.newScan(context.getScan()); SelectStatement select = SelectStatement.COUNT_ONE; // We need to use this tableRef ColumnResolver resolver = new ColumnResolver() { @Override public List<TableRef> getTables() { return Collections.singletonList(tableRef); } @Override public java.util.List<PFunction> getFunctions() { return Collections.emptyList(); }; @Override public TableRef resolveTable(String schemaName, String tableName) throws SQLException { throw new UnsupportedOperationException(); } @Override public ColumnRef resolveColumn(String schemaName, String tableName, String colName) throws SQLException { PColumn column = tableName != null ? tableRef.getTable().getColumnFamily(tableName).getColumn(colName) : tableRef.getTable().getColumn(colName); return new ColumnRef(tableRef, column.getPosition()); } @Override public PFunction resolveFunction(String functionName) throws SQLException { throw new UnsupportedOperationException(); }; @Override public boolean hasUDFs() { return false; }; }; PhoenixStatement statement = new PhoenixStatement(connection); StatementContext context = new StatementContext(statement, resolver, scan, new SequenceManager(statement)); long ts = timestamp; // FIXME: DDL operations aren't transactional, so we're basing the timestamp on a server timestamp. // Not sure what the fix should be. We don't need conflict detection nor filtering of invalid transactions // in this case, so maybe this is ok. if (ts!=HConstants.LATEST_TIMESTAMP && tableRef.getTable().isTransactional()) { ts = TransactionUtil.convertToNanoseconds(ts); } ScanUtil.setTimeRange(scan, ts); if (emptyCF != null) { scan.setAttribute(BaseScannerRegionObserver.EMPTY_CF, emptyCF); } ServerCache cache = null; try { if (deleteList != null) { if (deleteList.isEmpty()) { scan.setAttribute(BaseScannerRegionObserver.DELETE_AGG, QueryConstants.TRUE); // In the case of a row deletion, add index metadata so mutable secondary indexing works /* TODO: we currently manually run a scan to delete the index data here ImmutableBytesWritable ptr = context.getTempPtr(); tableRef.getTable().getIndexMaintainers(ptr); if (ptr.getLength() > 0) { IndexMetaDataCacheClient client = new IndexMetaDataCacheClient(connection, tableRef); cache = client.addIndexMetadataCache(context.getScanRanges(), ptr); byte[] uuidValue = cache.getId(); scan.setAttribute(PhoenixIndexCodec.INDEX_UUID, uuidValue); } */ } else { // In the case of the empty key value column family changing, do not send the index // metadata, as we're currently managing this from the client. It's possible for the // data empty column family to stay the same, while the index empty column family // changes. PColumn column = deleteList.get(0); if (emptyCF == null) { scan.addColumn(column.getFamilyName().getBytes(), column.getName().getBytes()); } scan.setAttribute(BaseScannerRegionObserver.DELETE_CF, column.getFamilyName().getBytes()); scan.setAttribute(BaseScannerRegionObserver.DELETE_CQ, column.getName().getBytes()); } } List<byte[]> columnFamilies = Lists.newArrayListWithExpectedSize(tableRef.getTable().getColumnFamilies().size()); if (projectCF == null) { for (PColumnFamily family : tableRef.getTable().getColumnFamilies()) { columnFamilies.add(family.getName().getBytes()); } } else { columnFamilies.add(projectCF); } // Need to project all column families into the scan, since we haven't yet created our empty key value RowProjector projector = ProjectionCompiler.compile(context, SelectStatement.COUNT_ONE, GroupBy.EMPTY_GROUP_BY); // Explicitly project these column families and don't project the empty key value, // since at this point we haven't added the empty key value everywhere. if (columnFamilies != null) { scan.getFamilyMap().clear(); for (byte[] family : columnFamilies) { scan.addFamily(family); } projector = new RowProjector(projector,false); } // Ignore exceptions due to not being able to resolve any view columns, // as this just means the view is invalid. Continue on and try to perform // any other Post DDL operations. try { // Since dropping a VIEW does not affect the underlying data, we do // not need to pass through the view statement here. WhereCompiler.compile(context, select); // Push where clause into scan } catch (ColumnFamilyNotFoundException e) { continue; } catch (ColumnNotFoundException e) { continue; } catch (AmbiguousColumnException e) { continue; } QueryPlan plan = new AggregatePlan(context, select, tableRef, projector, null, OrderBy.EMPTY_ORDER_BY, null, GroupBy.EMPTY_GROUP_BY, null); try { ResultIterator iterator = plan.iterator(); try { Tuple row = iterator.next(); ImmutableBytesWritable ptr = context.getTempPtr(); totalMutationCount += (Long)projector.getColumnProjector(0).getValue(row, PLong.INSTANCE, ptr); } catch (SQLException e) { sqlE = e; } finally { try { iterator.close(); } catch (SQLException e) { if (sqlE == null) { sqlE = e; } else { sqlE.setNextException(e); } } finally { if (sqlE != null) { throw sqlE; } } } } catch (TableNotFoundException e) { // Ignore and continue, as HBase throws when table hasn't been written to // FIXME: Remove if this is fixed in 0.96 } } finally { if (cache != null) { // Remove server cache if there is one cache.close(); } } } final long count = totalMutationCount;
上面就是相关的统计,清理代码
这样一套流程下来,创建 CREATE_TABLE_METADATA SYSTEM_CATALOG表就成功了,
回到 ConnectionQueryServicesImpl.init 方法中,接着创建其它的元数据表
metaConnection.createStatement().executeUpdate(QueryConstants.CREATE_STATS_TABLE_METADATA); metaConnection.createStatement().executeUpdate(QueryConstants.CREATE_FUNCTION_METADATA);
当上面的sql执行完成后又回到 PhoenixDriver.getConnectionQueryServices
这样一个driver的对象的建立就完成了。
现在理清一下类的调用对象流程图
PhoenixDriver对象继承于 PhoenixEmbeddedDriver调用 createConnection方法
然后调用 ConnectionQueryServicesTestImpl.init(url, info);
- 然后创建 PhoenixConnection对象
- 然后创建元数据表 metaConnection.createStatement().executeUpdate(QueryConstants.CREATE_TABLE_METADATA);
- 然后创建 PhoenixStatement对象进行 executeUpdate
- 然后创建 ExecutableCreateTableStatement 对象 statm 里面创建 CreateTableCompiler
- 然后创建 MetaDataClient 对象 进行 client.createTable(create, splits, parent, viewStatement, viewType,
viewColumnConstants, isViewColumnReferenced) - 然后调用 createTableInternal方法
- 然后调用 connection.getQueryServices().createTable tableMetaData, 方法 进行真正表的创建
- 然后调用到 ConnectionQueryServicesImpl.createTable对象 再调用ensureTableCreated方法进行物理表的真正创建了
从上面的流程图可以看到,其实phoenix就是对一个sql进行解释,然后调用原生的hbase的api进行表的创建的过程。
- phoenix jdbc driver查询源码分析
- phoenix select 查询源码分析
- phoenix 如何优化成使用索引进行查询源码分析
- phoenix upsert 源码分析
- Hbase JDBC 中间件 phoenix 实现sql查询hbase
- JDBC-Driver、PostgreSQL的SQL语句参数上限错误分析及解决办法(源码)
- Phoenix 分析
- Mysql jdbc driver源码浅析(一)
- Android RIL Driver 源码分析
- phoenix-jdbc-pom
- Phoenix jdbc 插入数据
- Phoenix Jdbc 连接HBase
- 通过JDBC访问phoenix
- spring jdbc 源码 分析
- jdbc 驱动源码分析
- JDBC源码分析
- JDBC源码分析
- JDBC源码分析(1)
- 小技巧汇总(不定时更新···)
- 机房收费系统—结账
- label根据文本自适应高度
- 认识CoreData—初识CoreData
- 美军用核潜艇发动黑客攻击:逼近他国海岸窃听
- phoenix jdbc driver查询源码分析
- SWT中TEXT复制黏贴长度默认限制为10字节的问题
- java原始webservice生成客户端
- A Deeper Look at Saliency: Feature Contrast, Semantics, and Beyond
- css2-overflow 属性
- OCR技术使书籍报刊电子化
- 排序算法之直接插入排序-Java-version
- Heartbeat+DRDB+LVS+Keepalived+Ldirectord
- Android错误处理——Android读取txt文件乱码解决方案