CLOVER🍀

That was when it all began.

Hazelcastのむンデックス利甚時の挙動を調べおみる

ちょっず興味がありたしお、Hazelcastのむンデックスの利甚の様子ず、ク゚リ実行時の挙動を調べおみたした。

Hazelcastは、Distributed Mapに栌玍するオブゞェクトのプロパティに察しお、むンデックスを匵るこずができたす。

Indexing
http://docs.hazelcast.org/docs/3.5/manual/html-single/hazelcast-documentation.html#indexing

たたク゚リ実行時の挙動の確認には、SQL Queryを䜿うこずにしたす。

Distributed SQL Query
http://docs.hazelcast.org/docs/3.5/manual/html-single/hazelcast-documentation.html#distributed-sql-query

では、芋おいっおみたしょう。

準備

ビルド定矩。
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.llittlewings.hazelcast.indexing</groupId>
    <artifactId>hazelcast-indexing</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>hazelcast-indexing</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.hazelcast</groupId>
            <artifactId>hazelcast</artifactId>
            <version>3.5</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.0.0</version>
            <scope>test</scope>
        </dependency>

    </dependencies>
</project>

Hazelcast 3.5がリリヌスされおいたので、今回利甚しおみたした。テストコヌド甚にJUnitずAssertJも利甚したす。

Distributed Mapの倀ずしお栌玍するクラスは、以䞋ずしたす。
src/main/java/org/llittlewings/hazelcast/indexing/Book.java

package org.llittlewings.hazelcast.indexing;

import java.io.Serializable;
import java.util.Objects;

public class Book implements Serializable {
    private static final long serialVersionUID = 1L;

    private String isbn;

    private String title;

    private int price;

    public Book(String isbn, String title, int price) {
        this.isbn = isbn;
        this.title = title;
        this.price = price;
    }

    public String getIsbn() {
        return isbn;
    }

    public String getTitle() {
        return title;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Book) {
            Book other = (Book) o;

            return isbn.equals(other.isbn) && title.equals(other.title) && price == other.price;
        }

        return false;
     }

    @Override
    public int hashCode() {
        return Objects.hash(isbn, title, price);
    }
}

たた、Hazelcastの蚭定はこのようにしたした。
src/test/resources/hazelcast.xml

<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.5.xsd"
           xmlns="http://www.hazelcast.com/schema/config"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <group>
        <name>my-cluster</name>
        <password>my-cluster-pass</password>
    </group>

    <network>
        <port auto-increment="true" port-count="100">5701</port>
        <join>
            <multicast enabled="true">
                <multicast-group>224.2.2.3</multicast-group>
                <multicast-port>54327</multicast-port>
            </multicast>
        </join>
    </network>

    <map name="default">
        <indexes>
            <index ordered="false">title</index>
            <index ordered="true">price</index>
        </indexes>
    </map>
</hazelcast>

デフォルトのDistributed Mapに察する蚭定ですが、titleずpriceにむンデックスを匵っおいたす。たた、priceに぀いおは順序を意識する圢でむンデックス定矩しおいたす。

    <map name="default">
        <indexes>
            <index ordered="false">title</index>
            <index ordered="true">price</index>
        </indexes>
    </map>

テストコヌドで利甚する、簡単なクラスタ構築甚のクラス。
src/test/java/org/llittlewings/hazelcast/indexing/HazelcastTestSupport.java

package org.llittlewings.hazelcast.indexing;

import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.hazelcast.config.ClasspathXmlConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;

public abstract class HazelcastTestSupport {
    protected void withHazelcast(int numInstances, Consumer<HazelcastInstance> f) {
        int initialPort = 5701;
        List<HazelcastInstance> instances =
                IntStream
                        .rangeClosed(1, numInstances)
                        .mapToObj(i -> {
                            ClasspathXmlConfig config = new ClasspathXmlConfig("hazelcast.xml");
                            config.setInstanceName("MyHazelcastInstance-" + (initialPort + i - 1));
                            return Hazelcast.newHazelcastInstance(config);
                        })
                        .collect(Collectors.toList());

        try {
            f.accept(instances.get(0));
        } finally {
            instances
                    .stream()
                    .forEach(h -> h.getLifecycleService().shutdown());
            Hazelcast.shutdownAll();
        }
    }
}

指定数分だけ、JavaVM内でHazelcastのNodeを起動するこずができたす。HazelcastInstanceには、わかりやすいようにポヌト番号をむンスタンス名に含めるようにしたした。

むンデックスは、どこで、誰が保持するのか

たずは、むンデックスがどこで保持されるのかずいうずころを確認したいず思いたす。

実装からいくず、以䞋のクラスで持぀こずになるようです。

順序付けを意識しない堎合。
https://github.com/hazelcast/hazelcast/blob/v3.5/hazelcast/src/main/java/com/hazelcast/query/impl/UnsortedIndexStore.java

順序を意識する堎合。
https://github.com/hazelcast/hazelcast/blob/v3.5/hazelcast/src/main/java/com/hazelcast/query/impl/SortedIndexStore.java

順序䞍芁の堎合はConcurrentHashMap、順序付けが必芁な堎合はConcurrentSkipListMapConcurrentHashMapで持぀ようになっおいたす。クラスタ化されおいるわけでもなく、ロヌカルのメモリのみに持っおいるずいう感じですね。

あずは、これのむンデックスを誰が持っおいるのかずいうこずですね。

この確認のために、こんなテストクラスを甚意したした。
src/test/java/org/llittlewings/hazelcast/indexing/IndexingTest.java

package org.llittlewings.hazelcast.indexing;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.hazelcast.core.IMap;
import com.hazelcast.core.PartitionService;
import org.junit.Test;

public class IndexingTest extends HazelcastTestSupport {
    @Test
    public void indexingTest() {
        List<Book> books =
                Arrays.asList(
                        new Book("978-4774169316", "Java゚ンゞニア逊成読本", 2138),
                        new Book("978-4798124605", "Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava", 4536),
                        new Book("978-4873117188", "Javaパフォヌマンス", 4212)
                );

        withHazelcast(3, hazelcast -> {
            IMap<String, Book> map = hazelcast.getMap("default");

            books.stream().forEach(b -> map.put(b.getIsbn(), b));

            try {
                System.out.println("Sleeping...");
                TimeUnit.SECONDS.sleep(10L);
            } catch (InterruptedException e) { }

            PartitionService ps = hazelcast.getPartitionService();
            System.out.printf(
                    "%s:%s => %s%n",
                    "978-4774169316",
                    "Java゚ンゞニア逊成読本",
                    ps.getPartition("978-4774169316").getOwner()
            );
            System.out.printf(
                    "%s:%s => %s%n",
                    "978-4798124605",
                    "Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava",
                    ps.getPartition("978-4798124605").getOwner()
            );
            System.out.printf(
                    "%s:%s => %s%n",
                    "978-4873117188",
                    "Javaパフォヌマンス",
                    ps.getPartition("978-4873117188").getOwner()
            );
        });
    }
}

テストはしおいたせんけど 。

Distributed Mapにデヌタ登録埌、少しスリヌプしおから、実際にキヌに察応する゚ントリがどのNodeに配眮されおいるかを衚瀺しおいたす。

ずはいえ、これだけだずわからないのでBytemanのスクリプトを曞いお、むンデックスぞの登録時にコン゜ヌル出力するようにしおみたした。
※耇数Nodeいるからか、デバッガヌだずちょっず苊しかったです
indexing-trace.btm

RULE trace UnsortedIndexStore
CLASS com.hazelcast.query.impl.UnsortedIndexStore
METHOD newIndex
AT ENTRY
IF TRUE
  DO traceln("[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = " + $1 + " :: Thread[" + Thread.currentThread().getName()  + "]")
ENDRULE

RULE trace SortedIndexStore
CLASS com.hazelcast.query.impl.SortedIndexStore
METHOD newIndex
AT ENTRY
IF TRUE
  DO traceln("[Trace SortedIndexStore] SortedIndexStore, newValue = " + $1 + " :: Thread[" + Thread.currentThread().getName()  + "]")
ENDRULE

このBytemanスクリプトを含めお、テストコヌドを動かしおみたす。

$ mvn test -Dtest=*IndexingTest -DargLine=-javaagent:$BYTEMAN_HOME/lib/byteman.jar=script:indexing-trace.btm

今回は3 Nodeになるようにしおいるので、クラスタが3぀のNodeで構成されたす。

Members [3] {
	Member [192.168.254.129]:5701 this
	Member [192.168.254.129]:5702
	Member [192.168.254.129]:5703
}

6 20, 2015 6:59:33 午埌 com.hazelcast.cluster.ClusterService
情報: [192.168.254.129]:5702 [my-cluster] [3.5] 

Members [3] {
	Member [192.168.254.129]:5701
	Member [192.168.254.129]:5702 this
	Member [192.168.254.129]:5703
}

6 20, 2015 6:59:33 午埌 com.hazelcast.cluster.ClusterService
情報: [192.168.254.129]:5703 [my-cluster] [3.5] 

Members [3] {
	Member [192.168.254.129]:5701
	Member [192.168.254.129]:5702
	Member [192.168.254.129]:5703 this
}

そしお、むンデックス登録時のログが出力されたす。

[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Java゚ンゞニア逊成読本 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace SortedIndexStore] SortedIndexStore, newValue = 2138 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Java゚ンゞニア逊成読本 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-0]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4536 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-0]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4536 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-0]
[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Javaパフォヌマンス :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-6]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4212 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-6]
Sleeping...
[Trace SortedIndexStore] SortedIndexStore, newValue = 4212 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-6]
[Trace SortedIndexStore] SortedIndexStore, newValue = 2138 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4536 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-0]
[Trace SortedIndexStore] SortedIndexStore, newValue = 2138 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4212 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-6]

最埌にスレッド名を出力しおいお、スレッド名にHazelcastのむンスタンス名が入るため、どのNodeにむンデックスが入ったかが少しは芋やすいず思いたす。
※実行ごずに、結果は倉わりたす

たた、キヌの配眮状況は、このようになっおいたす。

978-4774169316:Java゚ンゞニア逊成読本 => Member [192.168.254.129]:5702
978-4798124605:Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava => Member [192.168.254.129]:5703
978-4873117188:Javaパフォヌマンス => Member [192.168.254.129]:5703

ずいうこずは、デヌタのオヌナヌずなったNodeにむンデックスが䜜成されたずいうこずですね。

それにしおも、SortedIndexStoreにはやたらむンデックス登録のリク゚ストが発行されるんですねぇ 。

たた、この埌クラスタ内のNodeが枛っおいきたすが

Members [2] {
	Member [192.168.254.129]:5702 this
	Member [192.168.254.129]:5703
}

6 20, 2015 7:03:42 午埌 com.hazelcast.cluster.ClusterService
情報: [192.168.254.129]:5703 [my-cluster] [3.5] 

Members [2] {
	Member [192.168.254.129]:5702
	Member [192.168.254.129]:5703 this
}

それに䌎い再床むンデックス䜜成のログが出力されたす。

情報: [192.168.254.129]:5703 [my-cluster] [3.5] Removing Member [192.168.254.129]:5702
[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Java゚ンゞニア逊成読本 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-1]
[Trace SortedIndexStore] SortedIndexStore, newValue = 2138 :: Thread[hz.MyHazelcastInstance-5703.partition-operation.thread-1]

これは、Nodeが枛った時にデヌタの再配眮が発生するのですが、枛っおしたったNodeがその゚ントリのオヌナヌだった堎合には別のNodeがオヌナヌになった際にむンデックスが再䜜成されるずいうこずですね。

この䟋だず、5702ポヌトを䜿甚しおいたNodeがダりンしたわけですが、「Java゚ンゞニア逊成読本」の゚ントリのオヌナヌずなっおいたのはこのNodeでした。

ク゚リを実行しおみる

それでは続いお、ク゚リを投げおみたしょう。

今床は、このようなテストコヌドを甚意したした。
src/test/java/org/llittlewings/hazelcast/indexing/QueryTest.java

package org.llittlewings.hazelcast.indexing;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import com.hazelcast.core.IMap;
import com.hazelcast.core.PartitionService;
import com.hazelcast.query.SqlPredicate;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class QueryTest extends HazelcastTestSupport {
    @Test
    public void testQuery() {
        List<Book> books =
                Arrays.asList(
                        new Book("978-4774169316", "Java゚ンゞニア逊成読本", 2138),
                        new Book("978-4798124605", "Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava", 4536),
                        new Book("978-4873117188", "Javaパフォヌマンス", 4212)
                );

        withHazelcast(3, hazelcast -> {
            IMap<String, Book> map = hazelcast.getMap("default");

            books.stream().forEach(b -> map.put(b.getIsbn(), b));

            PartitionService ps = hazelcast.getPartitionService();
            System.out.printf(
                    "%s:%s => %s%n",
                    "978-4774169316",
                    "Java゚ンゞニア逊成読本",
                    ps.getPartition("978-4774169316").getOwner()
            );
            System.out.printf(
                    "%s:%s => %s%n",
                    "978-4798124605",
                    "Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava",
                    ps.getPartition("978-4798124605").getOwner()
            );
            System.out.printf(
                    "%s:%s => %s%n",
                    "978-4873117188",
                    "Javaパフォヌマンス",
                    ps.getPartition("978-4873117188").getOwner()
            );

            SqlPredicate titleQuery = new SqlPredicate("title = 'Java゚ンゞニア逊成読本'");
            Collection<Book> booksByTitleQuery = map.values(titleQuery);

            assertThat(booksByTitleQuery)
                    .hasSize(1)
                    .containsOnly(new Book("978-4774169316", "Java゚ンゞニア逊成読本", 2138));

            SqlPredicate titleWithLikeQuery = new SqlPredicate("title LIKE '%Java%' AND title LIkE '%逊成読本'");
            Collection<Book> booksByTitleWithLikeQuery = map.values(titleWithLikeQuery);

            assertThat(booksByTitleWithLikeQuery)
                    .hasSize(1)
                    .containsOnly(new Book("978-4774169316", "Java゚ンゞニア逊成読本", 2138));

            SqlPredicate priceQuery = new SqlPredicate("price > 4000");
            Collection<Book> booksByPriceQuery = map.values(priceQuery);

            assertThat(booksByPriceQuery)
                    .hasSize(2)
                    .containsSequence(
                            new Book("978-4873117188", "Javaパフォヌマンス", 4212),
                            new Book("978-4798124605", "Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava", 4536)
                    );
        });
    }
}

今床は、テストも入っおいたす笑。

先ほどず同様、゚ントリのオヌナヌの状況も衚瀺した埌、ク゚リを3回実行したす。この時の様子をトレヌスしたいず思いたす。

Bytemanスクリプトも、ク゚リ実行時の様子が远えるように远加したした。
indexing-trace.btm

RULE trace UnsortedIndexStore
CLASS com.hazelcast.query.impl.UnsortedIndexStore
METHOD newIndex
AT ENTRY
IF TRUE
  DO traceln("[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = " + $1 + " :: Thread[" + Thread.currentThread().getName()  + "]")
ENDRULE

RULE trace SortedIndexStore
CLASS com.hazelcast.query.impl.SortedIndexStore
METHOD newIndex
AT ENTRY
IF TRUE
  DO traceln("[Trace SortedIndexStore] SortedIndexStore, newValue = " + $1 + " :: Thread[" + Thread.currentThread().getName()  + "]")
ENDRULE

RULE trace QueryOperation
CLASS com.hazelcast.map.impl.operation.QueryOperation
METHOD run
AT ENTRY
IF TRUE
  DO traceln("[Trace Query] " + $0.predicate + " :: Thread[" + Thread.currentThread().getName()  + "]")
ENDRULE

RULE trace IndexService
CLASS com.hazelcast.query.impl.IndexService
METHOD query
AT EXIT
IF TRUE
  DO traceln("[Trace IndexService] " + $1 + ", " + $! + " :: Thread[" + Thread.currentThread().getName()  + "]")
ENDRULE

RULE trace BasicMapContextQuerySupport
CLASS com.hazelcast.map.impl.BasicMapContextQuerySupport
METHOD queryOnPartition
AT EXIT
IF TRUE
  DO traceln("[Trace BasicMapContextQuerySupport] " + $2 + ", " + $! + " :: Thread[" + Thread.currentThread().getName()  + "]")
ENDRULE

远加したクラスに぀いおは、たた埌で。

では、実行しおみたす。

$ mvn test -Dtest=*QueryTest -DargLine=-javaagent:$BYTEMAN_HOME/lib/byteman.jar=script:indexing-trace.btm

クラスタが構成された埌、むンデックス登録ず゚ントリの配眮状況がこのように衚瀺されたす。

[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Java゚ンゞニア逊成読本 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace SortedIndexStore] SortedIndexStore, newValue = 2138 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace SortedIndexStore] SortedIndexStore, newValue = 2138 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava :: Thread[hz.MyHazelcastInstance-5701.partition-operation.thread-0]
[Trace SortedIndexStore] SortedIndexStore, newValue = 2138 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-1]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4536 :: Thread[hz.MyHazelcastInstance-5701.partition-operation.thread-0]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4536 :: Thread[hz.MyHazelcastInstance-5701.partition-operation.thread-0]
[Trace UnsortedIndexStore] UnsortedIndexStore, newValue = Javaパフォヌマンス :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-6]
[Trace SortedIndexStore] SortedIndexStore, newValue = 4212 :: Thread[hz.MyHazelcastInstance-5702.partition-operation.thread-6]


978-4774169316:Java゚ンゞニア逊成読本 => Member [192.168.254.129]:5702
978-4798124605:Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava => Member [192.168.254.129]:5701 this
978-4873117188:Javaパフォヌマンス => Member [192.168.254.129]:5702

今床は、5702に寄りたした 。

最初のク゚リ。「title = 'Java゚ンゞニア逊成読本'」です。

            SqlPredicate titleQuery = new SqlPredicate("title = 'Java゚ンゞニア逊成読本'");
            Collection<Book> booksByTitleQuery = map.values(titleQuery);

            assertThat(booksByTitleQuery)
                    .hasSize(1)
                    .containsOnly(new Book("978-4774169316", "Java゚ンゞニア逊成読本", 2138));

この時のログは、このようになりたす。なお、titleは順序付けに぀いおのむンデックス定矩はしおいたせん。

[Trace Query] title=Java゚ンゞニア逊成読本 :: Thread[main]
[Trace IndexService] title=Java゚ンゞニア逊成読本, [] :: Thread[main]
[Trace Query] title=Java゚ンゞニア逊成読本 :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-0]
[Trace Query] title=Java゚ンゞニア逊成読本 :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-3]
[Trace IndexService] title=Java゚ンゞニア逊成読本, [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-0]
[Trace IndexService] title=Java゚ンゞニア逊成読本, [com.hazelcast.query.impl.QueryEntry@eba933f0] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-3]

ここで、mainスレッドは実行した本人のようです5701ポヌトのNodeず同矩。

ク゚リは、各Nodeで分散実行されるみたいですね。

こう芋るず、3぀のNodeで怜玢が実行され、うち5702のポヌトを䜿甚するNodeで結果が芋぀かったようですオヌナヌですし。

[Trace IndexService] title=Java゚ンゞニア逊成読本, [com.hazelcast.query.impl.QueryEntry@eba933f0] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-3]

続いお、LIKEの堎合。

            SqlPredicate titleWithLikeQuery = new SqlPredicate("title LIKE '%Java%' AND title LIkE '%逊成読本'");
            Collection<Book> booksByTitleWithLikeQuery = map.values(titleWithLikeQuery);

            assertThat(booksByTitleWithLikeQuery)
                    .hasSize(1)
                    .containsOnly(new Book("978-4774169316", "Java゚ンゞニア逊成読本", 2138));

こちらは、かなり膚倧なログが出力されたす。

[Trace Query] (title LIKE '%Java%' AND title LIKE '%逊成読本') :: Thread[main]
[Trace IndexService] (title LIKE '%Java%' AND title LIKE '%逊成読本'), null :: Thread[main]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[main]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[main]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[main]

〜省略〜

[Trace Query] (title LIKE '%Java%' AND title LIKE '%逊成読本') :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace IndexService] (title LIKE '%Java%' AND title LIKE '%逊成読本'), null :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace Query] (title LIKE '%Java%' AND title LIKE '%逊成読本') :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace IndexService] (title LIKE '%Java%' AND title LIKE '%逊成読本'), null :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [com.hazelcast.query.impl.QueryEntry@eba933f0] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]

〜省略〜

[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-2]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]
[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]

よ〜く芋るず、ここで゚ントリを芋぀けおいたす。

[Trace BasicMapContextQuerySupport] (title LIKE '%Java%' AND title LIKE '%逊成読本'), [com.hazelcast.query.impl.QueryEntry@eba933f0] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-0]

最初にむンデックスから探しお、その埌にフルスキャンしおいるみたいですね。
※持っおいるデヌタ数の割には、フルスキャン時のログがやたら倚いのが気になりたすが 。

実際、このような実装になっおいるようです。最初にむンデックスから探し、そこで結果が埗られなければフルスキャンに移行したす。
https://github.com/hazelcast/hazelcast/blob/v3.5/hazelcast/src/main/java/com/hazelcast/map/impl/operation/QueryOperation.java#L92

この時、むンデックスを䜿った怜玢時には以䞋のクラスが䜿甚され、
https://github.com/hazelcast/hazelcast/blob/v3.5/hazelcast/src/main/java/com/hazelcast/query/impl/IndexService.java

フルスキャンの時には以䞋のクラスが䜿甚されるようです。
https://github.com/hazelcast/hazelcast/blob/v3.5/hazelcast/src/main/java/com/hazelcast/map/impl/BasicMapContextQuerySupport.java

なるほど。

なお、フルスキャンも各Nodeで実行されたす、ず。

远蚘
デフォルトはNode単䜍のスキャンはシングルスレッドで動䜜し、「hazelcast.query.predicate.parallel.evaluation」をtrueにするこずで䞊列実行になるらしいです。デフォルトはfalseらしいのですが、今回芋おいるずスレッド名が途䞭で倉わっおいたようにも芋えたしたが 

System Properties
http://docs.hazelcast.org/docs/3.5/manual/html-single/hazelcast-documentation.html#system-properties

最埌は、「price > 4000」。

            SqlPredicate priceQuery = new SqlPredicate("price > 4000");
            Collection<Book> booksByPriceQuery = map.values(priceQuery);

            assertThat(booksByPriceQuery)
                    .hasSize(2)
                    .containsSequence(
                            new Book("978-4873117188", "Javaパフォヌマンス", 4212),
                            new Book("978-4798124605", "Beginning Java EE 6 GlassFish 3で始める゚ンタヌプラむズJava", 4536)
                    );

出力されたログ。

[Trace Query] price>4000 :: Thread[main]
[Trace IndexService] price>4000, [com.hazelcast.query.impl.QueryEntry@b8dbfd77] :: Thread[main]
[Trace Query] price>4000 :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-2]
[Trace IndexService] price>4000, [com.hazelcast.query.impl.QueryEntry@5a52fd10] :: Thread[hz.MyHazelcastInstance-5702.generic-operation.thread-2]
[Trace Query] price>4000 :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-1]
[Trace IndexService] price>4000, [] :: Thread[hz.MyHazelcastInstance-5703.generic-operation.thread-1]

こちらは、むンデックスを利甚した怜玢で完結するみたいですね。

なんずなく、挙動が芋えた感じですね。

たずめ

Hazelcastのむンデックスの持ち方ず、ク゚リ実行時の挙動を远っおみたした。

むンデックスは、たずめるず以䞋のような感じですね。

  • 順序を意識しないのであれば、UnsortedIndexStoreで保持されるConcurrentHashMapで保持
  • 順序を意識するのであれば、SortedIndexStoreで保持されるConcurrentSkipListMapConcurrentHashMapで保持
  • むンデックスそのものは、デヌタのオヌナヌずなるNodeがロヌカルメモリに保持する
  • オヌナヌのNodeがいなくなった堎合は、デヌタのマむグレヌション時に再むンデックスされる

ク゚リ実行時に぀いおは、以䞋の感じですね。

  • ク゚リは、各Nodeで分散実行
  • 最初に、むンデックスを䜿っお怜玢、芋぀かればそこで終了
  • むンデックスを䜿っお芋぀からなかった堎合は、フルスキャンぞ移行
  • フルスキャンは各Nodeでそれぞれ実行される
  • Node内で䞊列実行したい堎合は、「hazelcast.query.predicate.parallel.evaluation」をtrueにする

※たた、「hazelcast.query.result.size.limit」でク゚リが戻す最倧件数も制埡できるようですデフォルトは「-1で、制限なし

今回、゜ヌスや挙動から远っおみたしたが、よい勉匷になりたした。

今回䜜成した゜ヌスコヌドは、こちらに眮いおいたす。
https://github.com/kazuhira-r/hazelcast-examples/tree/master/hazelcast-indexing