You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2017/12/12 12:47:57 UTC
ignite git commit: IGNITE-6663: SQL: optimized PK index lookup (use
findOne instead of find(lower, upper)). This closes #3086.
Repository: ignite
Updated Branches:
refs/heads/master 2e65a7857 -> 850863e1c
IGNITE-6663: SQL: optimized PK index lookup (use findOne instead of find(lower, upper)). This closes #3086.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/850863e1
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/850863e1
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/850863e1
Branch: refs/heads/master
Commit: 850863e1c4ad794bd1147149a522cc126897d96d
Parents: 2e65a78
Author: gg-shq <ki...@gmail.com>
Authored: Tue Dec 12 15:47:50 2017 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Tue Dec 12 15:47:50 2017 +0300
----------------------------------------------------------------------
.../internal/util/OffheapReadWriteLock.java | 8 +-
.../processors/query/h2/database/H2Tree.java | 6 +-
.../query/h2/database/H2TreeIndex.java | 162 ++++++++++++-------
.../IgniteCacheAbstractFieldsQuerySelfTest.java | 43 ++++-
4 files changed, 151 insertions(+), 68 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/850863e1/modules/core/src/main/java/org/apache/ignite/internal/util/OffheapReadWriteLock.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/OffheapReadWriteLock.java b/modules/core/src/main/java/org/apache/ignite/internal/util/OffheapReadWriteLock.java
index ee6a04d..b119960 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/OffheapReadWriteLock.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/OffheapReadWriteLock.java
@@ -171,6 +171,8 @@ public class OffheapReadWriteLock {
lockObj.lock();
try {
+ // Note that we signal all waiters for this stripe. Since not all waiters in this
+ // stripe/index belong to this particular lock, we can't wake up just one of them.
writeConditions[idx].signalAll();
}
finally {
@@ -296,6 +298,8 @@ public class OffheapReadWriteLock {
* @param idx Lock index.
*/
private void signalNextWaiter(int writeWaitCnt, int readWaitCnt, int idx) {
+ // Note that we signal all waiters for this stripe. Since not all waiters in this stripe/index belong
+ // to this particular lock, we can't wake up just one of them.
if (writeWaitCnt == 0) {
Condition readCondition = readConditions[idx];
@@ -496,8 +500,10 @@ public class OffheapReadWriteLock {
}
/**
+ * Returns index of lock object corresponding to the stripe of this lock address.
+ *
* @param lock Lock address.
- * @return Lock monitor object.
+ * @return Lock monitor object that corresponds to the stripe for this lock address.
*/
private int lockIndex(long lock) {
return U.safeAbs(U.hash(lock)) & monitorsMask;
http://git-wip-us.apache.org/repos/asf/ignite/blob/850863e1/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
index 4aecab7..67bde69 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
@@ -238,13 +238,13 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
}
/**
- * Compare two rows.
+ * Compares two H2 rows.
*
* @param r1 Row 1.
* @param r2 Row 2.
- * @return Compare result.
+ * @return Compare result: see {@link Comparator#compare(Object, Object)} for values.
*/
- private int compareRows(GridH2Row r1, SearchRow r2) {
+ public int compareRows(SearchRow r1, SearchRow r2) {
if (r1 == r2)
return 0;
http://git-wip-us.apache.org/repos/asf/ignite/blob/850863e1/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index 5a336c5..edfaecf 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.processors.query.h2.database;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.NoSuchElementException;
+
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteSystemProperties;
@@ -30,8 +32,8 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
import org.apache.ignite.internal.processors.query.h2.H2Cursor;
-import org.apache.ignite.internal.processors.query.h2.database.io.H2RowLinkIO;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.database.io.H2RowLinkIO;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
import org.apache.ignite.internal.util.IgniteTree;
@@ -44,6 +46,7 @@ import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.index.SingleRowCursor;
import org.h2.message.DbException;
+import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
@@ -174,7 +177,16 @@ public class H2TreeIndex extends GridH2IndexBase {
H2Tree tree = treeForRead(seg);
- return new H2Cursor(tree.find(lower, upper, filter));
+ if (indexType.isPrimaryKey() && lower != null && upper != null && tree.compareRows(lower, upper) == 0) {
+ GridH2Row row = tree.findOne(lower, filter);
+
+ return (row == null) ? EMPTY_CURSOR : new SingleRowCursor(row);
+ }
+ else {
+ GridCursor<GridH2Row> cursor = tree.find(lower, upper, filter);
+
+ return new H2Cursor(cursor);
+ }
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
@@ -275,7 +287,7 @@ public class H2TreeIndex extends GridH2IndexBase {
H2Tree tree = treeForRead(seg);
- BPlusTree.TreeRowClosure<SearchRow, GridH2Row> filter = filter();
+ BPlusTree.TreeRowClosure<SearchRow, GridH2Row> filter = filterClosure();
return tree.size(filter);
}
@@ -284,47 +296,6 @@ public class H2TreeIndex extends GridH2IndexBase {
}
}
- /**
- * An adapter from {@link IndexingQueryCacheFilter} to {@link BPlusTree.TreeRowClosure} to
- * filter entries that belong to the current partition.
- */
- private static class PartitionFilterTreeRowClosure implements BPlusTree.TreeRowClosure<SearchRow, GridH2Row> {
- private final IndexingQueryCacheFilter filter;
-
- /**
- * Creates a {@link BPlusTree.TreeRowClosure} adapter based on the given partition filter.
- *
- * @param filter The partition filter.
- */
- public PartitionFilterTreeRowClosure(IndexingQueryCacheFilter filter) {
- this.filter = filter;
- }
-
- /** {@inheritDoc} */
- @Override public boolean apply(BPlusTree<SearchRow, GridH2Row> tree,
- BPlusIO<SearchRow> io, long pageAddr, int idx) throws IgniteCheckedException {
-
- H2RowLinkIO h2io = (H2RowLinkIO)io;
-
- return filter.applyPartition(
- PageIdUtils.partId(
- PageIdUtils.pageId(
- h2io.getLink(pageAddr, idx))));
- }
- }
-
- /**
- * Returns a filter to apply to rows in the current index to obtain only the
- * ones owned by the this cache.
- *
- * @return The filter, which returns true for rows owned by this cache.
- */
- @Nullable private BPlusTree.TreeRowClosure<SearchRow, GridH2Row> filter() {
- final IndexingQueryCacheFilter filter = partitionFilter(threadLocalFilter());
-
- return filter != null ? new PartitionFilterTreeRowClosure(filter) : null;
- }
-
/** {@inheritDoc} */
@Override public long getRowCountApproximation() {
return 10_000; // TODO
@@ -385,12 +356,12 @@ public class H2TreeIndex extends GridH2IndexBase {
@Nullable SearchRow last,
IndexingQueryFilter filter) {
try {
- IndexingQueryCacheFilter p = partitionFilter(filter);
+ IndexingQueryCacheFilter pf = partitionFilter(filter);
- GridCursor<GridH2Row> range = t.find(first, last, p);
+ GridCursor<GridH2Row> range = t.find(first, last, pf);
if (range == null)
- range = EMPTY_CURSOR;
+ range = GridH2IndexBase.EMPTY_CURSOR;
return new H2Cursor(range);
}
@@ -400,21 +371,6 @@ public class H2TreeIndex extends GridH2IndexBase {
}
/**
- * Filter which returns true for entries belonging to a particular partition.
- *
- * @param qryFilter Factory that creates a predicate for filtering entries for a particular cache.
- * @return The filter or null if the filter is not needed (e.g., if the cache is not partitioned).
- */
- @Nullable private IndexingQueryCacheFilter partitionFilter(@Nullable IndexingQueryFilter qryFilter) {
- if (qryFilter == null)
- return null;
-
- String cacheName = getTable().cacheName();
-
- return qryFilter.forCache(cacheName);
- }
-
- /**
* @param inlineIdxs Inline index helpers.
* @param cfgInlineSize Inline size from cache config.
* @return Inline size.
@@ -470,4 +426,86 @@ public class H2TreeIndex extends GridH2IndexBase {
private void dropMetaPage(String name, int segIdx) throws IgniteCheckedException {
cctx.offheap().dropRootPageForIndex(cctx.cacheId(), name + "%" + segIdx);
}
+
+ /**
+ * Returns a filter which returns true for entries belonging to a particular partition.
+ *
+ * @param qryFilter Factory that creates a predicate for filtering entries for a particular cache.
+ * @return The filter or null if the filter is not needed (e.g., if the cache is not partitioned).
+ */
+ @Nullable private IndexingQueryCacheFilter partitionFilter(@Nullable IndexingQueryFilter qryFilter) {
+ if (qryFilter == null)
+ return null;
+
+ String cacheName = getTable().cacheName();
+
+ return qryFilter.forCache(cacheName);
+ }
+
+ /**
+ * An adapter from {@link IndexingQueryCacheFilter} to {@link BPlusTree.TreeRowClosure} which
+ * filters entries that belong to the current partition.
+ */
+ private static class PartitionFilterTreeRowClosure implements BPlusTree.TreeRowClosure<SearchRow, GridH2Row> {
+ /** Filter. */
+ private final IndexingQueryCacheFilter filter;
+
+ /**
+ * Creates a {@link BPlusTree.TreeRowClosure} adapter based on the given partition filter.
+ *
+ * @param filter The partition filter.
+ */
+ public PartitionFilterTreeRowClosure(IndexingQueryCacheFilter filter) {
+ this.filter = filter;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<SearchRow, GridH2Row> tree,
+ BPlusIO<SearchRow> io, long pageAddr, int idx) throws IgniteCheckedException {
+
+ H2RowLinkIO h2io = (H2RowLinkIO)io;
+
+ return filter.applyPartition(
+ PageIdUtils.partId(
+ PageIdUtils.pageId(
+ h2io.getLink(pageAddr, idx))));
+ }
+ }
+
+ /**
+ * Returns a filter to apply to rows in the current index to obtain only the
+ * ones owned by the this cache.
+ *
+ * @return The filter, which returns true for rows owned by this cache.
+ */
+ @Nullable private BPlusTree.TreeRowClosure<SearchRow, GridH2Row> filterClosure() {
+ final IndexingQueryCacheFilter filter = partitionFilter(threadLocalFilter());
+
+ return filter != null ? new PartitionFilterTreeRowClosure(filter) : null;
+ }
+
+ /**
+ * Empty cursor.
+ */
+ public static final Cursor EMPTY_CURSOR = new Cursor() {
+ /** {@inheritDoc} */
+ @Override public Row get() {
+ throw DbException.convert(new NoSuchElementException("Empty cursor"));
+ }
+
+ /** {@inheritDoc} */
+ @Override public SearchRow getSearchRow() {
+ throw DbException.convert(new NoSuchElementException("Empty cursor"));
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean next() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean previous() {
+ return false;
+ }
+ };
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/850863e1/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
index 322598a..a285a8b 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
@@ -36,6 +36,7 @@ import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.CacheRebalanceMode;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.cache.affinity.AffinityKey;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
@@ -43,8 +44,11 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteKernal;
import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlIndexMetadata;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx;
import org.apache.ignite.internal.processors.datastructures.GridCacheAtomicLongValue;
import org.apache.ignite.internal.processors.datastructures.GridCacheInternalKeyImpl;
import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
@@ -663,8 +667,43 @@ public abstract class IgniteCacheAbstractFieldsQuerySelfTest extends GridCommonA
Collection<List<?>> res = qry.getAll();
- assert res != null;
- assert res.isEmpty();
+ assertNotNull(res);
+ assertTrue(res.isEmpty());
+ }
+
+ /**
+ * Verifies that exactly one record is found when we have equality comparison in where clause (which is supposed
+ * to use {@link BPlusTree#findOne(Object, Object)} instead of {@link BPlusTree#find(Object, Object, Object)}.
+ *
+ * @throws Exception If failed.
+ */
+ public void testSingleResultUsesFindOne() throws Exception {
+ QueryCursor<List<?>> qry =
+ intCache.query(sqlFieldsQuery("select _val from Integer where _key = 25"));
+
+ List<List<?>> res = qry.getAll();
+
+ assertNotNull(res);
+ assertEquals(1, res.size());
+ assertEquals(1, res.get(0).size());
+ assertEquals(25, res.get(0).get(0));
+ }
+
+ /**
+ * Verifies that zero records are found when we have equality comparison in where clause (which is supposed
+ * to use {@link BPlusTree#findOne(Object, Object)} instead of {@link BPlusTree#find(Object, Object, Object)}
+ * and the key is not in the cache.
+ *
+ * @throws Exception If failed.
+ */
+ public void testEmptyResultUsesFindOne() throws Exception {
+ QueryCursor<List<?>> qry =
+ intCache.query(sqlFieldsQuery("select _val from Integer where _key = -10"));
+
+ List<List<?>> res = qry.getAll();
+
+ assertNotNull(res);
+ assertEquals(0, res.size());
}
/** @throws Exception If failed. */