プライマリ・コンテンツに移動
Oracle® Fusion Middleware Oracle Coherenceでのアプリケーションの開発
12c (12.2.1.1)
E77322-02
目次へ移動
目次

前
次

20 基本的なキャッシュ操作の実行

この章では、CoherenceキャッシングAPIを使用して、基本的なキャッシュ操作を実行する手順について説明します。

この章の内容は次のとおりです。

20.1 NamedCache APIの概要

com.tangosol.net.NamedCache<K, V>インタフェースは、キャッシュ・インスタンスの取得と操作のためにアプリケーションによって使用される主要インタフェースです。NamedCache<K, V>インタフェースは、他のインタフェースを拡張し、Coherenceに固有でデータ・グリッド操作の実行に使用される追加のキャッシュ機能をそれぞれ提供します。

  • java.util.Map<K, V>get()put()remove()などの基本的なMapメソッド。

  • com.tangosol.net.cache.CacheMap<K, V> – キャッシュ内のキーのコレクションを取得(Mapとして)するため、およびオブジェクトをキャッシュに入れるためのメソッド。エントリをキャッシュに入れるときに、有効期限の値を追加することもできます。

  • com.tangosol.util.QueryMap<K, V> – キャッシュの問合せ用のメソッド。キャッシュ内のデータの問合せを参照してください。

  • com.tangosol.util.InvocableMap<K, V> – キャッシュ・データのサーバー側の処理用メソッド。キャッシュ内のデータの処理を参照してください。

  • com.tangosol.util.ObservableMap<K, V> – キャッシュ・イベントをリスニングするメソッド。(マップ・イベントの使用を参照してください)。

  • com.tangosol.util.ConcurrentMap<K, V>lock()unlock()などの同時アクセス用のメソッド。トランザクションの実行を参照してください。

NamedCacheおよび関連するインタフェースの詳細は、Oracle Coherence Java APIリファレンスを参照してください。

20.2 キャッシュ・インスタンスの取得

NamedCacheインスタンスへの参照を取得するために、アプリケーションはSession APIおよびCacheFactory APIを使用できます。Session APIは、キャッシュの取得に推奨される方法です。次の例では、デフォルトのセッション・プロバイダを使用してセッションを作成してから、Session.getCacheメソッドを使用してNamedCacheインスタンスへの参照を取得します。キャッシュの名前はパラメータとして含まれます。

import com.tangosol.net.*;
...
Session session = Session.create();
NamedCache<Object, Object> cache = session.getCache("MyCache");

CoherenceSessionクラスはSessionインタフェースを実装し、アプリケーションがnew演算子を使用できるようにします。次に例を示します。

import com.tangosol.net.*;
...
Session session = new CoherenceSession();
NamedCache<Object, Object> cache = session.getCache("MyCache");

次の例では、CacheFactory.getCacheメソッドを使用してNamedCacheインスタンスへの参照を取得し、キャッシュの名前をパラメータとして含めます。

import com.tangosol.net.*;
...
NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");

Session APIとCacheFactory APIの両方は、必要に応じて基礎となるキャッシュ・サービスを開始します。キャッシュ・インスタンスは、キャッシュ構成ファイル(デフォルトでcoherence-cache-config.xml)に定義されているキャッシュ・スキームを使用して作成されます。キャッシュ・スキームは、名前MyCacheにマップされます。キャッシュ・スキームおよびマッピングの定義の詳細は、キャッシュの構成を参照してください。

NamedCacheインスタンスでは、任意のタイプのキーと値を格納できます。ただし、キャッシュ・エントリを操作する場合は、型の安全性をアプリケーションで確保する必要があります。アプリケーションでは、特定の型のNamedCacheインスタンスを作成可能で、APIレベルの型チェックも提供されます。キャッシュの型チェックの詳細は、NameCache型チェックの使用を参照してください。

20.2.1 キャッシュされたオブジェクトの要件

キャッシュのキーおよび値はシリアライズ可能である必要があります(例: java.io.SerializableまたはCoherence Portable Object Formatのシリアライズ)。さらに、キャッシュ・キーはhashCode()equals()メソッドの実装を提供する必要があり、これらのメソッドはクラスタ・ノード間で一貫性のある結果を返す必要があります。これは、hashCode()およびequals()の実装は、オブジェクトのシリアライズ可能な状態(つまり、オブジェクトの一時的でないフィールド)にのみ基づいている必要があることを示しています。StringIntegerDateなどのほとんどの組込みのJava型はこの要件を満たしています。一部のキャッシュの実装(特にパーティション・キャッシュ)では、等価の検証にキー・オブジェクトのシリアライズされた形式を使用し、equals()trueを返すキーは、同様の方法でシリアライズされる必要があり、ほとんどの組込みのJava型はこの要件を満たしています。シリアライズの詳細は、次を参照してください

20.3 キャッシュput操作の実行

基本的なキャッシュput操作は、Mapインタフェースで定義されているputメソッドを使用して実行されます。putメソッドは、エントリをキャッシュに追加して、指定したキーの前の値を返します。次に例を示します。

String key = "k1";
String value = "Hello World!";
        
NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");
cache.put(key, value);

putAllメソッドは、単一のバルク・ロード操作で複数のエントリをキャッシュに追加する場合に使用され、エントリはMap型のデータ構造内にある必要があります。putAllメソッドの使用およびバルク・ロード操作の詳細は、キャッシュの事前ロードを参照してください。

20.4 キャッシュget操作の実行

基本的なキャッシュget操作は、Mapインタフェースで定義されているgetメソッドを使用して実行されます。マップされている値は、指定したキーで返されます。次に例を示します。

String key = "k1";
String value = "Hello World!";
        
NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");
cache.put(key, value);
System.out.println(cache.get(key));

20.5 キャッシュ削除操作の実行

基本的なキャッシュ削除操作は、Mapインタフェースで定義されているremoveメソッドを使用して実行されます。指定したキーのマッピングはキャッシュから削除され、前の値が返されます。次に例を示します。

String key = "k1";
String value = "Hello World!";

NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");
cache.put(key, value);
System.out.println(cache.get(key));
cache.remove(key);

20.6 デフォルトのマップ操作の使用

java.util.Mapインタフェースには、putIfAbsentreplaceAllmergeなどの操作を実行するためのデフォルトのメソッドが含まれます。Coherenceでは、これらのメソッド実装をオーバーライドして、これらの操作が分散環境で適切に実行されるようにします。エントリ・プロセッサを使用して、ラムダ式を活用できるように、メソッドが再実装されています。メソッドは、NamedCacheインタフェースを使用する場合に使用できます。次に例を示します。

String key = "k1";
String value = "Hello World!";

NamedCache<Object, Object> cache = CacheFactory.getCache("hello-example");
cache.putIfAbsent(key, value);
System.out.println(cache.get(key));

ラムダ式は、必要な処理をエントリで実行する場合にも使用できます。次に例を示します。

cache.replaceAll((key, value) ->
   {
   value.setLastName(value.getLastName().toUpperCase());
   return value;
   });

使用可能なデフォルトのメソッドの詳細は、MapインタフェースのJavaDocを参照してください。エントリ・プロセッサの詳細は、ターゲット、パラレルおよび問合せベースの処理のためのエージェントの使用を参照してください。

20.7 キャッシュの事前ロード

キャッシュの事前ロードは、データがアプリケーションで使用される前にキャッシュに移入する場合に使用される一般的なシナリオです。

20.7.1 キャッシュへのデータのバルク・ロード

putメソッドを使用して、キャッシュにデータをバルク・ロードできます。ただし、特にパーティション・キャッシュおよびレプリケート・キャッシュでは、putをコールするたびにネットワーク・トラフィックが発生する場合があります。また、putをコールするたびにキャッシュで置換されたばかりのオブジェクトが返され(java.util.Mapインタフェースで定義されている)、不要なオーバーヘッドが追加されます。

public static void bulkLoad(NamedCache cache, Connection conn)
    {
    Statement s;
    ResultSet rs;
    
    try
        {
        s = conn.createStatement();
        rs = s.executeQuery("select key, value from table");
        while (rs.next())
            {
            Integer key   = new Integer(rs.getInt(1));
            String  value = rs.getString(2);
            cache.put(key, value);
            }
        ...
        }
    catch (SQLException e)
        {...}
    }

かわりに、ConcurrentMap.putAllメソッドを使用すると、キャッシュのロードを大幅に効率化できます。次に例を示します。

public static void bulkLoad(NamedCache cache, Connection conn)
    {
    Statement s;
    ResultSet rs;
    Map       buffer = new HashMap();

    try
        {
        int count = 0;
        s = conn.createStatement();
        rs = s.executeQuery("select key, value from table");
        while (rs.next())
            {
            Integer key   = new Integer(rs.getInt(1));
            String  value = rs.getString(2);
            buffer.put(key, value);

            // this loads 1000 items at a time into the cache
            if ((count++ % 1000) == 0)
                {
                cache.putAll(buffer);
                buffer.clear();
                }
            }
        if (!buffer.isEmpty())
            {
            cache.putAll(buffer);
            }
        ...
        }
    catch (SQLException e)
        {...}
    }

20.7.2 分散バルク・ロードの実行

Coherenceのパーティション・キャッシュに大量のデータ・セットを事前移入する際は、Coherenceのクラスタ・メンバーに作業を分散すると効率が向上する可能性があります。分散ロードを使用すると、クラスタのネットワーク帯域幅およびCPU処理能力の集積を活用することによって、データ・スループット率を高めることができます。分散ロードの実行時には、アプリケーションで次の事項を決定する必要があります。

  • ロードを実行するクラスタのメンバー

  • メンバー間でのデータ・セットの分割方法

メンバーの選択および作業の分割時は、基礎となるデータ・ソース(データベースやファイル・システムなど)に対する負荷をアプリケーションで考慮する必要があります。たとえば、問合せを同時に実行するメンバーが多すぎると、1つのデータベースでは対応しきれなくなる場合があります。

20.7.2.1 分散バルク・ロードの例

この項では、簡単な分散ロードを実行する一般的な手順の概要について説明します。この例では、データがファイルに保存されていて、クラスタ内で記憶域が有効なすべてのメンバーに分散されることを前提としています。

  1. 記憶域が有効なメンバーのセットを取得します。たとえば、次の方法ではgetStorageEnabledMembersメソッドを使用して、分散キャッシュ内で記憶域が有効なメンバーを取得します。

    protected Set getStorageMembers(NamedCache cache)
            {
            return ((PartitionedService) cache.getCacheService())
               .getOwnershipEnabledMembers();
            }
    
  2. 記憶域が有効なクラスタのメンバー間で作業を分割します。たとえば、次のルーチンでは、メンバーに割り当てられたファイルの一覧が含まれるマップが、メンバーをキーとして返されます。

    protected Map<Member, List<String>> divideWork(Set members, List<String> fileNames)
            {
            Iterator i = members.iterator();
            Map<Member, List<String>> mapWork = new HashMap(members.size());
            for (String sFileName : fileNames)
                {
                Member member = (Member) i.next();
                List<String> memberFileNames = mapWork.get(member);
                if (memberFileNames == null)
                    {
                    memberFileNames = new ArrayList();
                    mapWork.put(member, memberFileNames);
                    }
                memberFileNames.add(sFileName);
    
                // recycle through the members
                if (!i.hasNext())
                    {
                    i = members.iterator();
                    }
                }
            return mapWork;
            }
    
  3. 各メンバーへのロードを実行するタスクを起動します。たとえば、タスクの起動にはCoherenceのInvocationServiceなどを使用します。この場合、LoaderInvocableの実装では、memberFileNamesを反復して各ファイルを処理し、その内容をキャッシュにロードする必要があります。通常、クライアント上で実行されるキャッシュ処理は、LoaderInvocableを使用して実行する必要があります。

    public void load()
            {
            NamedCache cache = getCache();
    
            Set members = getStorageMembers(cache);
    
            List<String> fileNames = getFileNames();
    
            Map<Member, List<String>> mapWork = divideWork(members, fileNames);
    
            InvocationService service = (InvocationService)
                    CacheFactory.getService("InvocationService");        
    
            for (Map.Entry<Member, List<String>> entry : mapWork.entrySet())
                {
                Member member = entry.getKey();
                List<String> memberFileNames = entry.getValue();
    
                LoaderInvocable task = new LoaderInvocable(memberFileNames, cache.getCacheName());
                service.execute(task, Collections.singleton(member), this);
                }
            }

20.8 キャッシュのクリア

キャッシュの内容は、NamedCacheインタフェースで定義されるclearまたはtruncateメソッドを使用してクリアできます。clearメソッドにより、メモリーおよびCPUオーバーヘッドが増加するため、分散キャッシュのクリアには一般にお薦めしません。別の方法として、truncateメソッドを使用できます。次に例を示します。

String key = "k1";
String value = "Hello World!";
        
NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");
cache.put(key, value);
System.out.println(cache.get(key));

Cache.truncate;

truncateメソッドでは、メモリーおよびCPUリソースを効率的に使用できるため、大きなキャッシュやリスナーを使用するキャッシュをクリアする場合に最適なオプションになります。truncateメソッドは、フロント・マップおよびバック・マップの両方をクリアするため、ニア・キャッシュのクリアにも最適です。truncateメソッドでエントリが削除されたかどうかは、リスナー、トリガーおよびインターセプタで監視できません。ただし、CacheLifecycleEventイベントが発生し、この操作の実行がすべてのサブスクライバに通知されます。

20.9 キャッシュの解放

キャッシュが不要になったアプリケーションでは、CacheFactory.releaseメソッドを使用して、指定されたキャッシュのインスタンスに関連付けられているローカル・リソースを解放するようにしてください。キャッシュを解放すると、キャッシュは使用できなくなりますが、クラスタ全体でのキャッシュへの他の参照またはキャッシュの内容は影響を受けません。次に例を示します。

String key = "k1";
String value = "Hello World!";
        
NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");
cache.put(key, value);
System.out.println(cache.get(key));

CacheFactory.releaseCache(cache);

20.10 キャッシュの破棄

CacheFactoryクラスを使用して作成されるキャッシュは、CacheFactory.destroyメソッドを使用して破棄できます。destroyメソッドは、クラスタ全体で指定されたキャッシュを破棄します。キャッシュへの参照が無効化され、キャッシュされたデータがクリアされて、すべてのリソースが解放されます。NamedCache.clearメソッドは、分散環境でメモリーとCPUの両方の負荷が高くなるタスクになる可能性があるため、多くの場合destroyメソッドの方が適しています。次に例を示します。

String key = "k1";
String value = "Hello World!";
        
NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");
cache.put(key, value);
System.out.println(cache.get(key));

CacheFactory.destroyCache(cache);

20.11 セッションのクローズ

セッションが不要になった場合、アプリケーションはそのセッションをクローズする必要があります。セッションがクローズされると、そのセッションに関連付けられたすべてのリソースもクローズされ、それらのリソースへの参照が無効になります。アプリケーションがクローズされたセッションまたはそのリソースを使用しようとすると、不正な状態に関する例外がスローされます。

セッションをクローズするには、closeメソッドを使用します。次に例を示します。

String key = "k1";
String value = "Hello World!";

try(Session session = Session.create()) {
   NamedCache<Object, Object> cache = session.getCache("MyCache");
   cache.put(key, value);
   System.out.println(cache.get(key));
   session.close();
} 
catch (Exception e){
}

20.12 NameCache操作の非同期実行

com.tangosol.net.AsyncNameCache<K, Vインタフェースにより、キャッシュ操作をパラレルに完了できます。このインタフェースでは、Java CompletableFutureクラスを使用し、このクラスでは、完了または例外(あるいは両方)のコールバックを提供して、複数の非同期コールを連鎖して順に実行し、パラレルに実行しているすべてのコールが完了するまで待機します。NameCache操作を非同期に実行すると、スループットが改善し、ユーザー・インタフェースの応答性が向上します。Coherenceの例では、非同期キャッシュ操作の実行の追加の例を提供します。詳細は、『Oracle Coherenceのインストール』を参照してください。

非同期キャッシュ操作を実行するには、CompletableFutureクラスを使用して、Futureインタフェースを実装する必要があります。次に例を示します。

import com.tangosol.net.AsyncNamedCache;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
 
public class HelloWorld {
 
    public static void main(String[] args) throws ExecutionException,
       InterruptedException {
       
       String key = "k1";
       String value = "Hello World!";
       
       NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache");
       AsyncNamedCache<Object, Object> as = cache.async();
       
       Future future = as.put(key, value);
       future.get();
             
       CompletableFuture cf = as.get(key);
        
       System.out.println(cf.get());
        
       CompletableFuture cfremove = as.remove(key);
        
       System.out.print("Removing key/value: " + cfremove.get() + "\n" );
       System.out.println("The key/value is: " + future.get());
    }
}

20.13 NameCache型チェックの使用

Coherenceでは、明示的な型を使用してSessionまたはCacheFactory APIのいずれかを使用している場合、厳密に型指定されたNamedCacheインスタンスをリクエストできます。デフォルトでは、NamedCache<Object, Object>インスタンスが返されます。これは、NamedCacheインスタンスを作成および使用するための最も柔軟なメカニズムですが、キャッシュ・インスタンスとのやり取りに必要なキーと値の型をアプリケーションで設定する必要があります。たとえば、任意の型のオブジェクトを格納する場合、アプリケーションでは次を使用できます。

NamedCache<Object,Object> cache = session.getCache("MyCache");

または

NamedCache<Object,Object> cache = CacheFactory.getCache("MyCache");

getCacheメソッドは、型チェックすることなく、必要に応じて特定の型のNamedCacheインスタンスをリクエストする場合に使用できます。TypeAssertionインタフェースは、getCacheメソッドともに使用され、NamedCacheインスタンスで使用されるキーと値の型の正確性をアサートします。このメソッドは、キャッシュがRAW型を使用する必要があることをアサートする場合に使用できます。次に例を示します。

NamedCache<Object, Object> cache = session.getCache("MyCache",
 TypeAssertion.withRawTypes());

同様に、CacheFactoryを使用している場合、

NamedCache<Object, Object> cache = CacheFactory.getCache("MyCache",
 TypeAssertion.withRawTypes());

型の安全性を強化するために、キャッシュを作成して、キャッシュで使用されるキーと値の型を明示的にアサートできます。たとえば、キャッシュを作成して、キーと値の型がStringである必要があることをアサートするには、アプリケーションで次を使用できます。

NamedCache<String, String> cache = session.getCache("MyCache",
 TypeAssertion.withTypes(String.class, String.class));

NameCacheインスタンスは、アサートされた型に準拠する必要はなく、無視することもできますが、コンパイル時に警告メッセージが表示されます。次に例を示します。

NamedCache cache = session.getCache("MyCache",
 TypeAssertion.withTypes(String.class, String.class));

同様に、RAW型を使用し、ネーム・キャッシュ・インスタンスで特定の型を使用できることをアサートすることを、アプリケーションで選択することもできます。ただし、どちらの場合も、型がチェックされないままでは、エラーが発生することがあります。次に例を示します。

NamedCache<String, String> cache = session.getCache("MyCache",
 TypeAssertion.withRawTypes());

型の安全性を最も強くするには、特定の型をキャッシュ定義の一部として、キャッシュ構成ファイルに宣言することもできます。キャッシュ定義の部分として構成された型と異なる型をアプリケーションが使用すると、ランタイム・エラーが発生します。次の例では、String型のキーと値のみをサポートするキャッシュを構成します。

<cache-mapping>
   <cache-name>MyCache</cache-name>
   <scheme-name>distributed</scheme-name>
   <key-type>String</key-type>
   <value-type>String</value-type>
</cache-mapping>

最後に、明示的な型チェックの無効化をアプリケーションで選択できます。型チェックが無効の場合、型の安全性はアプリケーションで確保する必要があります。

NamedCache<Object, Object> cache = session.getCache("MyCache",
   TypeAssertion.withoutTypeChecking());