Skip to content

PHOENIX-6458 Using global indexes for queries with uncovered columns - #1256

Merged
kadirozde merged 1 commit into
apache:masterfrom
kadirozde:6458
Feb 24, 2022
Merged

PHOENIX-6458 Using global indexes for queries with uncovered columns#1256
kadirozde merged 1 commit into
apache:masterfrom
kadirozde:6458

Conversation

@kadirozde

@kadirozdekadirozde commented Jun 28, 2021

Copy link
Copy Markdown
Contributor

The Phoenix query optimizer does not use a global index for a query with the columns that are not covered by the global index if the query does not have the corresponding index hint for this index. With the index hint, the optimizer rewrites the query where the index is used within a subquery. With this subquery, the row keys of the index rows that satisfy the subquery are retrieved by the Phoenix client and then pushed into the Phoenix server caches of the data table regions. Finally, on the server side, data table rows are scanned and joined with the index rows using HashJoin. Based on the selectivity of the original query, this join operation may still result in scanning a large amount of data table rows.

Eliminating these data table scans would be a significant improvement. To do that, instead of rewriting the query, the Phoenix optimizer simply treats the global index as a covered index for the given query. With this, the Phoenix query optimizer chooses the index table for the query especially when the index row key prefix length is greater than the data row key prefix length for the query. On the server side, the index table is scanned using index row key ranges implied by the query and the index row keys are then mapped to the data table row keys (please note an index row key includes all the data row key columns). Finally, the corresponding data table rows are scanned using server-to-server RPCs. PHOENIX-6458 (this PR) retrieves the data table rows one by one using the HBase get operation. PHOENIX-6501 replaces this get operation with the scan operation to reduce the number of server-to-server RPC calls.

Comment threadphoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java Outdated
if (dataRegion != null) {
joinResult = dataRegion.get(get);
} else {
TableName dataTable =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this part is even still needed for local indexes. (but that's a separate issue)

@stoty

Copy link
Copy Markdown
Contributor

💔 -1 overall

VoteSubsystemRuntimeComment
+0 🆗reexec6m 40sDocker mode activated.
_ Prechecks _
+1 💚dupname0m 0sNo case conflicting files found.
+1 💚hbaseanti0m 0sPatch does not have any anti-patterns.
+1 💚@author0m 0sThe patch does not contain any @author tags.
-1 ❌test4tests0m 0sThe patch doesn't appear to include any new or modified tests. Please justify why no new tests are needed for this patch. Also please list what manual steps were performed to verify this patch.
_ master Compile Tests _
+1 💚mvninstall24m 22smaster passed
+0hbaserecompile31m 27sHBase recompiled.
+1 💚compile1m 4smaster passed
+1 💚checkstyle1m 46smaster passed
+1 💚javadoc0m 55smaster passed
+0 🆗spotbugs3m 20sphoenix-core in master has 965 extant spotbugs warnings.
_ Patch Compile Tests _
+1 💚mvninstall16m 38sthe patch passed
+0hbaserecompile26m 58sHBase recompiled.
+1 💚compile1m 4sthe patch passed
+1 💚javac1m 4sthe patch passed
-1 ❌checkstyle1m 46sphoenix-core: The patch generated 129 new + 4749 unchanged - 112 fixed = 4878 total (was 4861)
+1 💚whitespace0m 0sThe patch has no whitespace issues.
+1 💚javadoc0m 51sthe patch passed
-1 ❌spotbugs3m 31sphoenix-core generated 1 new + 963 unchanged - 2 fixed = 964 total (was 965)
_ Other Tests _
-1 ❌unit1m 45sphoenix-core in the patch failed.
+1 💚asflicense0m 14sThe patch does not generate ASF License warnings.
79m 43s
ReasonTests
FindBugsmodule:phoenix-core
org.apache.phoenix.schema.IndexDataColumnRef doesn't override ColumnRef.equals(Object) At IndexDataColumnRef.java:At IndexDataColumnRef.java:[line 1]
Failed junit testsphoenix.compile.QueryOptimizerTest
phoenix.compile.TenantSpecificViewIndexCompileTest
SubsystemReport/Notes
DockerClientAPI=1.41 ServerAPI=1.41 base: https://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/1/artifact/yetus-general-check/output/Dockerfile
GITHUB PR#1256
Optional Testsdupname asflicense javac javadoc unit spotbugs hbaserebuild hbaseanti checkstyle compile
unameLinux f6d075d7d9a5 4.15.0-136-generic #140-Ubuntu SMP Thu Jan 28 05:20:47 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev/phoenix-personality.sh
git revisionmaster / fcdf5bc
Default JavaPrivate Build-1.8.0_242-8u242-b08-0ubuntu3~16.04-b08
checkstylehttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/1/artifact/yetus-general-check/output/diff-checkstyle-phoenix-core.txt
spotbugshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/1/artifact/yetus-general-check/output/new-spotbugs-phoenix-core.html
unithttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/1/artifact/yetus-general-check/output/patch-unit-phoenix-core.txt
Test Resultshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/1/testReport/
Max. process+thread count431 (vs. ulimit of 30000)
modulesC: phoenix-core U: phoenix-core
Console outputhttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/1/console
versionsgit=2.7.4 maven=3.3.9 spotbugs=4.1.3
Powered byApache Yetus 0.12.0 https://yetus.apache.org

This message was automatically generated.

table = ServerUtil.ConnectionFactory.
getConnection(ServerUtil.ConnectionType.INDEX_WRITER_CONNECTION, environment).
getTable(TableName.valueOf(dataTableName));
joinResult = table.get(get);

@lhofhansllhofhanslJun 28, 2021

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is doing a remote get for each row that matches the index. The index would have to be very selective for this to be an improvement.

I was hoping we'd come up with some smart batching. But that's tricky to do here. :)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not sure if I do it in this PR or open a separate jira for that. We can buffer lots of data row keys in memory and then use a skip scan filter and even multiple threads to issue a separate scan for each data table region. Essentially, this is what we do for reverse index verification. However, it requires quite a bit code refactoring. Please note this PR updates both client and server code. If we leave the improvement to the second PR, that will update only the server side. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's perfectly fine to do in a separate PR.

And, yes, doing buffering will require quite some refactoring, since you (a) have to do it at a place where you still know the rows involved, and (b) a level higher than here, so that you can buffer.

The only concern I have that in case using an index can be significantly slower than a full scan (this is true even for local indexes). For example a query of the form SELECT colA FROM table WHERE colB > 0; assuming colB is not selective will take longer. Note that FAST_DIFF (unfortunately Phoenix' default) is particularly slow for Get operations. My guess is that with defaults if the WHERE is not 99% - 99.9% selective, the query might be slower.

In any case :) Another PR is cool.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have created https://issues.apache.org/jira/browse/PHOENIX-6501 for this improvement

@stoty

Copy link
Copy Markdown
Contributor

💔 -1 overall

VoteSubsystemRuntimeComment
+0 🆗reexec5m 37sDocker mode activated.
_ Prechecks _
+1 💚dupname0m 0sNo case conflicting files found.
+1 💚hbaseanti0m 0sPatch does not have any anti-patterns.
+1 💚@author0m 0sThe patch does not contain any @author tags.
-1 ❌test4tests0m 0sThe patch doesn't appear to include any new or modified tests. Please justify why no new tests are needed for this patch. Also please list what manual steps were performed to verify this patch.
_ master Compile Tests _
+1 💚mvninstall24m 35smaster passed
+0hbaserecompile36m 10sHBase recompiled.
+1 💚compile1m 25smaster passed
+1 💚checkstyle2m 9smaster passed
+1 💚javadoc1m 4smaster passed
+0 🆗spotbugs4m 17sphoenix-core in master has 965 extant spotbugs warnings.
_ Patch Compile Tests _
+1 💚mvninstall18m 52sthe patch passed
+0hbaserecompile34m 14sHBase recompiled.
+1 💚compile1m 20sthe patch passed
+1 💚javac1m 20sthe patch passed
-1 ❌checkstyle2m 14sphoenix-core: The patch generated 128 new + 4749 unchanged - 112 fixed = 4877 total (was 4861)
+1 💚whitespace0m 0sThe patch has no whitespace issues.
+1 💚javadoc0m 59sthe patch passed
-1 ❌spotbugs4m 13sphoenix-core generated 1 new + 963 unchanged - 2 fixed = 964 total (was 965)
_ Other Tests _
-1 ❌unit1m 57sphoenix-core in the patch failed.
+1 💚asflicense0m 16sThe patch does not generate ASF License warnings.
93m 33s
ReasonTests
FindBugsmodule:phoenix-core
org.apache.phoenix.schema.IndexDataColumnRef doesn't override ColumnRef.equals(Object) At IndexDataColumnRef.java:At IndexDataColumnRef.java:[line 1]
Failed junit testsphoenix.compile.TenantSpecificViewIndexCompileTest
phoenix.compile.QueryOptimizerTest
SubsystemReport/Notes
DockerClientAPI=1.41 ServerAPI=1.41 base: https://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/2/artifact/yetus-general-check/output/Dockerfile
GITHUB PR#1256
Optional Testsdupname asflicense javac javadoc unit spotbugs hbaserebuild hbaseanti checkstyle compile
unameLinux 15c712d84ee3 4.15.0-136-generic #140-Ubuntu SMP Thu Jan 28 05:20:47 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev/phoenix-personality.sh
git revisionmaster / fcdf5bc
Default JavaPrivate Build-1.8.0_242-8u242-b08-0ubuntu3~16.04-b08
checkstylehttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/2/artifact/yetus-general-check/output/diff-checkstyle-phoenix-core.txt
spotbugshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/2/artifact/yetus-general-check/output/new-spotbugs-phoenix-core.html
unithttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/2/artifact/yetus-general-check/output/patch-unit-phoenix-core.txt
Test Resultshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/2/testReport/
Max. process+thread count516 (vs. ulimit of 30000)
modulesC: phoenix-core U: phoenix-core
Console outputhttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/2/console
versionsgit=2.7.4 maven=3.3.9 spotbugs=4.1.3
Powered byApache Yetus 0.12.0 https://yetus.apache.org

This message was automatically generated.

@comnetwork

comnetwork commented Jun 29, 2021

Copy link
Copy Markdown
Contributor

@kadirozde@lhofhansl FYI.

1.You said "Phoenix client does not use a global index for the queries with the columns that are not covered by the global index" is not right , In QueryOptimizer.addPlan, for the sql with the columns that are not covered by the global index, if user specify a Index Hint and there exists where clause, the sql would be rewritten as
"SELECT /*+ NO_INDEX / K,V1,V2 FROM T WHERE ("K" IN ((SELECT /+ INDEX(T IDX) */ ":K" FROM "IDX" WHERE "0:V1" = 'bar')) AND V2 = 'foo') " (k is pk of T , v1 is in IDX and v2 is not), you may consider compatibility with exising code.

2.Whether or not scaning the gobal index and retrieving the corresponding rows from the data table is better than just scaning the data table is a complex problem, because there are many factors we need to consider such as Network cost, random disk access cost , data distribution , column selective etc. You said "It is expected that such performance improvement will happen when the index row key prefix length is greater than the data row key prefix length for a given query" is extremely insufficient. Lack of a CBO framework in Phoenix, seems that it is sensible to be conservative, I think it is better to left whether or not select this strategy to user by user specifying the Index Hint just as the existing code.

@stoty

Copy link
Copy Markdown
Contributor

💔 -1 overall

VoteSubsystemRuntimeComment
+0 🆗reexec6m 26sDocker mode activated.
_ Prechecks _
+1 💚dupname0m 0sNo case conflicting files found.
+1 💚hbaseanti0m 0sPatch does not have any anti-patterns.
+1 💚@author0m 0sThe patch does not contain any @author tags.
+1 💚test4tests0m 0sThe patch appears to include 2 new or modified test files.
_ master Compile Tests _
+1 💚mvninstall24m 27smaster passed
+0hbaserecompile31m 28sHBase recompiled.
+1 💚compile1m 7smaster passed
+1 💚checkstyle1m 50smaster passed
+1 💚javadoc0m 53smaster passed
+0 🆗spotbugs3m 21sphoenix-core in master has 965 extant spotbugs warnings.
_ Patch Compile Tests _
+1 💚mvninstall16m 54sthe patch passed
+0hbaserecompile27m 15sHBase recompiled.
+1 💚compile1m 6sthe patch passed
+1 💚javac1m 6sthe patch passed
-1 ❌checkstyle1m 50sphoenix-core: The patch generated 63 new + 4814 unchanged - 47 fixed = 4877 total (was 4861)
+1 💚whitespace0m 0sThe patch has no whitespace issues.
+1 💚javadoc0m 51sthe patch passed
-1 ❌spotbugs3m 31sphoenix-core generated 1 new + 963 unchanged - 2 fixed = 964 total (was 965)
_ Other Tests _
-1 ❌unit124m 10sphoenix-core in the patch failed.
+1 💚asflicense0m 46sThe patch does not generate ASF License warnings.
204m 29s
ReasonTests
FindBugsmodule:phoenix-core
org.apache.phoenix.schema.IndexDataColumnRef doesn't override ColumnRef.equals(Object) At IndexDataColumnRef.java:At IndexDataColumnRef.java:[line 1]
Failed junit testsphoenix.end2end.index.GlobalIndexOptimizationIT
phoenix.end2end.index.IndexUsageIT
phoenix.end2end.DerivedTableIT
phoenix.end2end.UserDefinedFunctionsIT
phoenix.end2end.IndexToolIT
phoenix.end2end.RowValueConstructorOffsetIT
phoenix.end2end.ViewIT
phoenix.end2end.DistinctPrefixFilterIT
phoenix.end2end.ExplainPlanWithStatsEnabledIT
phoenix.end2end.DefaultColumnValueIT
SubsystemReport/Notes
DockerClientAPI=1.41 ServerAPI=1.41 base: https://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/3/artifact/yetus-general-check/output/Dockerfile
GITHUB PR#1256
Optional Testsdupname asflicense javac javadoc unit spotbugs hbaserebuild hbaseanti checkstyle compile
unameLinux 90ed47f9bc26 4.15.0-136-generic #140-Ubuntu SMP Thu Jan 28 05:20:47 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev/phoenix-personality.sh
git revisionmaster / fcdf5bc
Default JavaPrivate Build-1.8.0_242-8u242-b08-0ubuntu3~16.04-b08
checkstylehttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/3/artifact/yetus-general-check/output/diff-checkstyle-phoenix-core.txt
spotbugshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/3/artifact/yetus-general-check/output/new-spotbugs-phoenix-core.html
unithttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/3/artifact/yetus-general-check/output/patch-unit-phoenix-core.txt
Test Resultshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/3/testReport/
Max. process+thread count12127 (vs. ulimit of 30000)
modulesC: phoenix-core U: phoenix-core
Console outputhttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/3/console
versionsgit=2.7.4 maven=3.3.9 spotbugs=4.1.3
Powered byApache Yetus 0.12.0 https://yetus.apache.org

This message was automatically generated.

@kadirozde

Copy link
Copy Markdown
ContributorAuthor

@kadirozde@lhofhansl FYI.

1.You said "Phoenix client does not use a global index for the queries with the columns that are not covered by the global index" is not right , In QueryOptimizer.addPlan, for the sql with the columns that are not covered by the global index, if user specify a Index Hint and there exists where clause, the sql would be rewritten as
"SELECT /*+ NO_INDEX / K,V1,V2 FROM T WHERE ("K" IN ((SELECT /+ INDEX(T IDX) */ ":K" FROM "IDX" WHERE "0:V1" = 'bar')) AND V2 = 'foo') " (k is pk of T , v1 is in IDX and v2 is not), you may consider compatibility with exising code.

What I meant is that by default the uncovered global index is not used. One can construct a query plan manually using hints as you pointed it out to use the uncovered global index. Please note that you can also construct a SQL join statement and achieve the same thing.

2.Whether or not scaning the gobal index and retrieving the corresponding rows from the data table is better than just scaning the data table is a complex problem, because there are many factors we need to consider such as Network cost, random disk access cost , data distribution , column selective etc. You said "It is expected that such performance improvement will happen when the index row key prefix length is greater than the data row key prefix length for a given query" is extremely insufficient. Lack of a CBO framework in Phoenix, seems that it is sensible to be conservative, I think it is better to left whether or not select this strategy to user by user specifying the Index Hint just as the existing code.

I agree that there is no guarantee that the index always performs better. However, based on my experience, it will perform better in most of the cases in practice. This is because the index PK is designed by the user who knows the use case (the type and shape of queries) and the user wants that the index should be used if the index row key prefix length is greater than the data row key prefix length for a given query in general. I understand your concern here and please help me out on how to proceed here. I can add a config param to use uncovered indexes without a specific hint. This mean that we will preserve the existing behavior if the config param is not specified. Would that address your concern?

@comnetwork

comnetwork commented Jun 30, 2021

Copy link
Copy Markdown
Contributor

@kadirozde ,thank you for reply.

However, based on my experience, it will perform better in most of the cases in practice. This is because the index PK is designed by the user who knows the use case (the type and shape of queries) and the user wants that the index should be used if the index row key prefix length is greater than the data row key prefix length for a given query in general.

If there is just one index, your said may be right, but if there is lots of index, it is hard to make sure what is the user's intention, may be the user just leave out some columns in the global index.

I understand your concern here and please help me out on how to proceed here. I can add a config param to use uncovered indexes without a specific hint. This mean that we will preserve the existing behavior if the config param is not specified. Would that address your concern?

In my opinion, your implemention now is better than the existing implemention which rewrite the sql with the columns that are not covered by the global index as InSubquery , you could remove the existing implemention and replace with your new implemention which also repsect the Index Hint and at the same time avoid to give user two different chocies to achieve the same purpose.

In short , I think if we could not make sure the index performs better, we would better be conservative and let the user to decide rather than making decisions for the user.

In any case, you may also let the user know you scaning the gobal index and retrieving the corresponding rows from the data table when they execute explain sql.

@lhofhansl

lhofhansl commented Jul 7, 2021

Copy link
Copy Markdown
Contributor

If you do SELECT count(uncovered_column) FROM T WHERE covered_column = xyz, the global uncovered index is not used even when you hint it as expected (I just verified that current 5.x. Phoenix).

I found that uncovered local indexes (that's what I tested) are sometimes much slower than doing to a full table scan. That happens when there is a WHERE clause that an index could be used for, but the WHERE restriction is not selective.

(As noted above, FAST_DIFF (Phoenix' default) is actually the worst choice since SEEKs are slow with it. ROW_INDEX_V1 with ZSTD compression are far better. I blogged about this here: https://hadoop-hbase.blogspot.com/2018/10/apache-hbase-and-apache-phoenix-more-on.html a while ago: With FAST_DIFF the WHERE clause needed to be 0.5% (return 1/200 of the data) to be effective. With ROW_INDEX_V1 + ZSTD that was 10%.)

This is at best as good as uncovered local indexing, and probably worse since we need to go remote for each row, unless we do batching. And the batches would still be requiring a SKIP_SCAN, which in the general case is still very slow for FAST_DIFF.
So I expect with defaults the WHERE clause would need to be somewhere between 1/1000 and 1/300 hundred selective for this to be improvement.

Anyway... I think we should check this in. Presumable folks would create uncovered global indexes only when they know what they are doing.

@comnetwork

Copy link
Copy Markdown
Contributor

@lhofhansl ,thank you for reply,
Yes , I agree that we should check this in because this implemention is better than existing code which rewrite the sql as InSubquery, but I think we should merge the two different implemention in order to not give user two different chocies to achieve the same purpose. One way is remove the existing code and switch to this new implemention, and in the same time , because we could not make sure the index performs better, we should provide an index hint (or just use the existing index hint) or config param to disable it.
And furthermore, we should illustratethat we using index and lookupback in the explain

@virajjasanivirajjasani left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left minor comments/questions, overall looks good based on my understanding (Release note: improvement applicable only if clients and servers both are upgraded with this patch).

@@ -156,7 +155,7 @@ public static PTable createProjectedTable(SelectStatement select, StatementConte
}
// add LocalIndexDataColumnRef

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: IndexDataColumnRef in place of LocalIndexDataColumnRef?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@@ -1168,8 +1168,8 @@ public ColumnRef resolveColumn(String schemaName, String tableName, String colNa
colRef = super.resolveColumn(schemaName, tableName, colName);
} catch (ColumnNotFoundException e) {
// This could be a ColumnRef for local index data column.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we can remove local reference here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Comment on lines +586 to +612
try {
table = environment.getConnection().getTable(dataTable);
joinResult = table.get(get);
} finally {
if (table != null) table.close();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to replace with try-with-resources?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

Comment on lines +596 to +597
try {
table = environment.getConnection().getTable(dataTable);
table = ServerUtil.ConnectionFactory.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here reg try-with-resources

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Comment on lines +84 to 89
@Deprecated
public static final String LOCAL_INDEX_FILTER = "_LocalIndexFilter";
@Deprecated
public static final String LOCAL_INDEX_LIMIT = "_LocalIndexLimit";
@Deprecated
public static final String LOCAL_INDEX_FILTER_STR = "_LocalIndexFilterStr";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe these deprecated fields can be removed only after we come to a major release (e.g 6.x/7.x) where server running on that release can no longer be directly supported by client version <= 4.16/4.17, is that correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so too.

@kadirozde

Copy link
Copy Markdown
ContributorAuthor

@lhofhansl ,thank you for reply,
Yes , I agree that we should check this in because this implemention is better than existing code which rewrite the sql as InSubquery, but I think we should merge the two different implemention in order to not give user two different chocies to achieve the same purpose. One way is remove the existing code and switch to this new implemention, and in the same time , because we could not make sure the index performs better, we should provide an index hint (or just use the existing index hint) or config param to disable it.
And furthermore, we should illustratethat we using index and lookupback in the explain

@Lars already enhanced the explain plan to indicate the server side merge for uncovered columns in PHOENIX-6409. Regarding to merging this PR and the existing subquery implementation without creating compatibility issues, it is a bit tricky for me now. I need to spend time to figure that out. Would you please create a jira for that? If you like to implement that jira, you would be more than welcome, @comnetwork.

@stoty

Copy link
Copy Markdown
Contributor

💔 -1 overall

VoteSubsystemRuntimeComment
+0 🆗reexec5m 0sDocker mode activated.
_ Prechecks _
+1 💚dupname0m 0sNo case conflicting files found.
+1 💚hbaseanti0m 0sPatch does not have any anti-patterns.
+1 💚@author0m 0sThe patch does not contain any @author tags.
+1 💚test4tests0m 0sThe patch appears to include 2 new or modified test files.
_ master Compile Tests _
+1 💚mvninstall22m 12smaster passed
+0hbaserecompile28m 37sHBase recompiled.
+1 💚compile1m 2smaster passed
+1 💚checkstyle2m 26smaster passed
+1 💚javadoc0m 51smaster passed
+0 🆗spotbugs3m 5sphoenix-core in master has 964 extant spotbugs warnings.
_ Patch Compile Tests _
+1 💚mvninstall13m 29sthe patch passed
+0hbaserecompile23m 35sHBase recompiled.
+1 💚compile1m 6sthe patch passed
+1 💚javac1m 6sthe patch passed
-1 ❌checkstyle2m 36sphoenix-core: The patch generated 129 new + 4748 unchanged - 113 fixed = 4877 total (was 4861)
+1 💚whitespace0m 0sThe patch has no whitespace issues.
+1 💚javadoc0m 50sthe patch passed
-1 ❌spotbugs3m 21sphoenix-core generated 1 new + 962 unchanged - 2 fixed = 963 total (was 964)
_ Other Tests _
-1 ❌unit115m 19sphoenix-core in the patch failed.
+1 💚asflicense0m 46sThe patch does not generate ASF License warnings.
188m 42s
ReasonTests
FindBugsmodule:phoenix-core
org.apache.phoenix.schema.IndexDataColumnRef doesn't override ColumnRef.equals(Object) At IndexDataColumnRef.java:At IndexDataColumnRef.java:[line 1]
Failed junit testsphoenix.end2end.ViewIT
phoenix.end2end.DistinctPrefixFilterIT
phoenix.end2end.ExplainPlanWithStatsEnabledIT
phoenix.end2end.index.GlobalIndexOptimizationIT
phoenix.end2end.RowValueConstructorOffsetIT
phoenix.end2end.DefaultColumnValueIT
phoenix.end2end.index.IndexUsageIT
phoenix.end2end.IndexToolIT
phoenix.end2end.DerivedTableIT
phoenix.end2end.UserDefinedFunctionsIT
SubsystemReport/Notes
DockerClientAPI=1.41 ServerAPI=1.41 base: https://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/4/artifact/yetus-general-check/output/Dockerfile
GITHUB PR#1256
Optional Testsdupname asflicense javac javadoc unit spotbugs hbaserebuild hbaseanti checkstyle compile
unameLinux 34763af58f8a 4.15.0-112-generic #113-Ubuntu SMP Thu Jul 9 23:41:39 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev/phoenix-personality.sh
git revisionmaster / 6842c1d
Default JavaPrivate Build-1.8.0_242-8u242-b08-0ubuntu3~16.04-b08
checkstylehttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/4/artifact/yetus-general-check/output/diff-checkstyle-phoenix-core.txt
spotbugshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/4/artifact/yetus-general-check/output/new-spotbugs-phoenix-core.html
unithttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/4/artifact/yetus-general-check/output/patch-unit-phoenix-core.txt
Test Resultshttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/4/testReport/
Max. process+thread count10804 (vs. ulimit of 30000)
modulesC: phoenix-core U: phoenix-core
Console outputhttps://ci-hadoop.apache.org/job/Phoenix/job/Phoenix-PreCommit-GitHub-PR/job/PR-1256/4/console
versionsgit=2.7.4 maven=3.3.9 spotbugs=4.1.3
Powered byApache Yetus 0.12.0 https://yetus.apache.org

This message was automatically generated.

@kadirozde

Copy link
Copy Markdown
ContributorAuthor

@lhofhansl , @comnetwork, I have done some performance testing on a cluster with 15 region servers. I created a data table with 16 million rows. Each row is about 2500 bytes. The row key of this table is composed of four fields (VARCHAR, INTEGER, TIMESTAMP, VARCHAR). I run the same test without an index, with a covered index and with an uncovered index. The timestamp field is indexed. The query used in the test returned N rows that fall in to the a supplied timestamp range, where N is supplied as the limit parameter. The query returns four fields. The query times in ms are as follows:

limit covered uncovered no index
1 212 252 4404
10 215 256 5375
100 215 310 5169
1000 232 1125 4698
10000 433 7325 6440
100000 1588 67002 6789

It is clear that if the number of selected rows is large (in this case 10000 or more) the uncovered index starts to perform worse than the full table scan. No sure if these results are generalizable. Instead of using an uncovered index by default, I will add a logic to use an uncovered index only if it is given as a hint.

@kadirozde

Copy link
Copy Markdown
ContributorAuthor

@kadirozde@lhofhansl FYI.

1.You said "Phoenix client does not use a global index for the queries with the columns that are not covered by the global index" is not right , In QueryOptimizer.addPlan, for the sql with the columns that are not covered by the global index, if user specify a Index Hint and there exists where clause, the sql would be rewritten as "SELECT /*+ NO_INDEX / K,V1,V2 FROM T WHERE ("K" IN ((SELECT /+ INDEX(T IDX) */ ":K" FROM "IDX" WHERE "0:V1" = 'bar')) AND V2 = 'foo') " (k is pk of T , v1 is in IDX and v2 is not), you may consider compatibility with exising code.

2.Whether or not scaning the gobal index and retrieving the corresponding rows from the data table is better than just scaning the data table is a complex problem, because there are many factors we need to consider such as Network cost, random disk access cost , data distribution , column selective etc. You said "It is expected that such performance improvement will happen when the index row key prefix length is greater than the data row key prefix length for a given query" is extremely insufficient. Lack of a CBO framework in Phoenix, seems that it is sensible to be conservative, I think it is better to left whether or not select this strategy to user by user specifying the Index Hint just as the existing code.

@comnetwork@lhofhansl, I have updated the PR such that the uncovered global indexes will be used only when the index hint is provided as @comnetwork suggested.

@kadirozde
kadirozde marked this pull request as draft January 28, 2022 05:31
@kadirozde
kadirozde marked this pull request as ready for review February 22, 2022 05:50
@lhofhansl

Copy link
Copy Markdown
Contributor

Sorry for the late reply. I have not looked the updated PR, gating this on a hint seems fine. Let's make this hint is for global indexes only, and does not apply to local indexes.

}
}
} else if (ScanUtil.isUncoveredGlobalIndex(scan)) {
byte[] dataTableName = scan.getAttribute(PHYSICAL_DATA_TABLE_NAME);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a test where the index table has a different Physical Table name and data table has a different physical table name?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I understood this comment. Data and index tables have different physical tables always.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code reminded me a test case where we have separate physical and logical table names for (case1. data table and case2. index table). I was just asking to make sure that we are not breaking anything.
Something like this:
You have an index hint for a global index which has a different physical table name (logical name appears in the hint but its PHYSICAL_TABLE_NAME in syscat points to different hbase table). I think your code doesn't break this case but I wanted to make sure. Does this make sense?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I think this concern is outside the context of this PR as this PR does not change the existing index hint implementation or how we pass the data table physical name via a scan attribute.

}
if (ScanUtil.isLocalIndex(scan) && !ScanUtil.isAnalyzeTable(scan)) {
if ((ScanUtil.isLocalIndex(scan)
|| ScanUtil.isUncoveredGlobalIndex(scan))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You seem to have this in Line 137 and in GroupedAggregateRegion... as well. I think it is better to have a ScanUtil.isLocalOrUncoveredGlobalIndex

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add it

int clientVersion = ScanUtil.getClientVersion(scan);
List<IndexMaintainer> indexMaintainers =
IndexUtil.deSerializeIndexMaintainersFromScan(scan);
indexMaintainer = indexMaintainers.get(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we getting the first one? I see that we used to do this before and it is not new but I am confused why how we sort these

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These methods can serialize/deserialize more than one index maintainers. I do not know if there is a case where we do that actually. As far as know, we only need to do this only for one index maintainer.

serializeIndexMaintainerIntoScan(scan, dataTable);
// Set view constants if exists.
serializeViewConstantsIntoScan(scan, dataTable);
if (table.getIndexType() == IndexType.LOCAL) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need this for global uncovered ones too?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We populate scan attributes for global indexes at the constructor of the TableResultIterator using ScanUtil#setScanAttributesForClient in general when the table to be scanned is a global index table.

" SERVER FILTER BY FIRST KEY ONLY\n" +
" DYNAMIC SERVER FILTER BY \\(\"" + dataTableName + ".K1\", \"" + dataTableName + ".K2\"\\) IN \\(\\(\\$\\d+.\\$\\d+, \\$\\d+.\\$\\d+\\)\\)";
assertTrue("Expected:\n" + expected + "\ndid not match\n" + actual, Pattern.matches(expected, actual));
//assertTrue("Expected:\n" + expected + "\ndid not match\n" + actual, Pattern.matches(expected, actual));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: commented code. Forgotten?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it was. I will fix it

@kadirozde

Copy link
Copy Markdown
ContributorAuthor

Yes, the index hint is required only for global indexes.

@gokcenigokceni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kadirozde

Copy link
Copy Markdown
ContributorAuthor

@gokceni, Thank you for approving the updated version. @lhofhansl and @comnetwork, thank you for reviewing the earlier versions and I have updated the PR based on your comments. I am going to merge this now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@kadirozde@stoty@comnetwork@lhofhansl@virajjasani@gokceni