Apache Cassandra + Apache Ignite-最高の組み合わせ

Apache Cassandraは、人気のあるオープンソースの分散NoSQL分散ディスクデータベースの1つです。 Netflix、eBay、Expediaなどの大手企業がインフラストラクチャの主要部分で使用しており、その速度、数千のノード間で直線的にスケーリングする機能、異なるデータセンター間の「クラス最高」のレプリケーションで人気を博しています。



Apache Igniteは、JCache、SQL99、ACIDトランザクション、および基本的な機械学習代数をサポートする、RAM内のデータの分散ストレージとその上でのリアルタイムの分散コンピューティングのためのプラットフォームであるインメモリコンピューティングプラットフォームです。



Apache Cassandraは、その分野の古典的なソリューションです。 特殊なソリューションの場合と同様に、その利点は多くの妥協のおかげで達成されましたが、そのほとんどはディスクデータストレージの制限が原因です。 Cassandraは、残りを犠牲にしてできるだけ早く動作するように最適化されています。 トレードオフの例:ACIDトランザクションとSQLサポートの欠如、データが事前に適応されていない場合、任意のトランザクショントランザクションと分析トランザクションの不可能性。 これらの妥協は、ユーザーの自然な困難を引き起こし、製品の誤った使用とネガティブなエクスペリエンスにつながります。また、異なるタイプのストレージ間でデータを強制的に共有させ、インフラストラクチャを断片化し、アプリケーションのデータストレージのロジックを複雑にします。



問題の可能な解決策は、CassandraをApache Igniteと組み合わせて使用​​することです。 これにより、2つのシステムの共生による欠点を補いながら、Cassandraの主要な利点を節約できます。



どうやって? 読み続けてサンプルコードを確認してください。





Cassandraの制限



始めるために、Cassandraの主な制限について簡単に説明します。



  1. 帯域幅と応答時間は、ハードドライブまたはソリッドステートドライブの特性によって制限されます。
  2. 順次書き込みおよび読み取り用に最適化された特定のデータストレージ構造は、従来のリレーショナルデータ操作の最適なパフォーマンスに適合していません。 これにより、データの正規化やJOINを使用した効果的なマッチングができなくなり、たとえば、GROUP BYやORDERなどの操作に大きな制限が課されます。
  3. p。2の結果-より限定されたバリエーションを支持するSQLサポートの欠如-CQL。
  4. ACIDトランザクションの不足。


Cassandraを他の目的に使用したい、と私は完全に同意します。 私の目標は、これらの問題を解決すれば、Cassandraの「任命」を大幅に拡大できることを示すことです。 人と馬を組み合わせることで、人と馬を別々に扱う場合とはまったく異なる物事のリストをすでに持つことができるライダーが得られます。



これらの制限をどのように回避できますか?



一部はCassandraにあり、一部は必要な保証をサポートする他のシステムにある場合、古典的なオプションはデータの断片化です。



このアプローチの短所は、目に見えて見えます:アプリケーションの開発とサポートの複雑さの増加(したがって、潜在的に速度と品質の低下)。 マイクロサービスのアプリケーションまたはレイヤーレベルでさまざまなソースからの異なる情報を結合するよりも、1つのゲートをノックする方がはるかに簡単です。 また、2つのシステムのいずれかが劣化すると、重大なマイナスの結果につながる可能性があり、インフラストラクチャチームは1石で2羽の鳥を追いかけることを余儀なくされます。



アパッチ点火



別の方法は、Cassandraの上に別のシステムを配置し、それらの間で責任を共有することです。 Apache Igniteはこのスキームの理想的な候補だと思います。



  1. ディスクによって課せられたパフォーマンスの制限はなくなります。ApacheIgniteはRAMで動作しますが、今では高速なものはありません。 さらに、サーバープールに十分なRAM(Apache IgniteはCassandraのような分散システムです)を配置するのが実行可能なタスクであるため、価格が急激に低下しています。
  2. JOIN、GROUP BY、ORDER BY、INSERT、UPDATE、DELETE、MERGEなどを含む従来のSQL99を完全にサポートしているため、データを正規化し、分析を容易にし、RAMを使用する際のパフォーマンスを考慮して、HTAP、分析の可能性を開きます運用データに応じてリアルタイムで;
  3. JDBCおよびODBC標準のサポートにより、Tableauなどの既存のツールや、HibernateやSpring Dataなどのフレームワークとの統合が容易になります。
  4. フォールトトレランスとデータ複製を保証するためのACIDトランザクション、柔軟で詳細な設定のサポート。
  5. 分散コンピューティング、ストリーミングデータ処理、機械学習-利益をもたらす多くの新しいビジネスアプリケーションを簡単に実装できます。


このスキームでは、Apache Igniteは、永続的な不揮発性ストレージのレイヤーとして機能するApache Cassandraを上回ります。 Apache Igniteの次のバージョンでは、ディスク、遅延実行、およびエンドツーエンドSQLによるメモリ拡張をサポートする独自の永続化ソリューションが提供されるという事実にもかかわらず、Cassandraは長年にわたる開発、普及、共有の能力に優れているため、この役割で引き続き興味深い可能性がありますすべての卵を、必要のない1つのバスケットに入れないでください。



Apache Igniteクラスターは、クエリの実行が必要なデータのすべてまたは一部(たとえばアーカイブデータを除く)をApache Cassandraから吸収します。その後、ライトスルーモードで動作し、APIまたはSQL読み取り要求を個別に処理し、複製しますCassandraに同期または非同期モードで書き込み要求を書き込み、ディスクに安全に保存します。



さらに、これらのデータはリアルタイムで分析され、Tableauなどの視覚化ツールを使用したり、分散型機械学習アルゴリズムを適用したり、店頭を形成したりできます。



そして、例で?



次に、Apache CassandraとApache Igniteの単純な「合成」統合の例を示して、これがどのように機能し、一定のボイラープレートコードが必要な場合でもまったく複雑ではないことを示します。



最初に、Cassandraで必要なテーブルを作成し、データを入力してから、Javaプロジェクトを初期化し、DTOクラスを作成します。その後、Cassandraで動作するようにApache Igniteを構成します。



Mac OS Sierra、Cassandra 3.10、Apache Ignite 2.0を使用します。 Linuxでは、コマンドは類似している必要があります。



Cassandra:テーブルとデータ



開始するには、 リンクをクリックするかcurl / wgetを使用して、Cassandraディストリビューションを〜/ Downloadsディレクトリにアップロードします。



次に、ディレクトリに移動して解凍します。



$ cd ~/Downloads $ tar xzvf apache-cassandra-3.10-bin.tar.gz $ cd apache-cassandra-3.10
      
      





デフォルト設定でCassandraを実行します。テストするにはこれで十分です。



 $ bin/cassandra
      
      





次に、Cassandraインタラクティブシェルを起動し、テストデータ構造を作成します(通常のサロゲートIDをキーとして選択します-Cassandraのテーブルの場合、後続のデータ抽出に関してより意味のあるキーを選択するのが理にかなっていますが、例を単純化します):



 $ cd ~/Downloads/apache-cassandra-3.10 $ bin/cqlsh
      
      





 CREATE KEYSPACE IgniteTest WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1}; USE IgniteTest; CREATE TABLE catalog_category (id bigint primary key, parent_id bigint, name text, description text); CREATE TABLE catalog_good (id bigint primary key, categoryId bigint, name text, description text, price bigint, oldPrice bigint); INSERT INTO catalog_category (id, parentId, name, description) VALUES (1, NULL, ' ', '     !'); INSERT INTO catalog_category (id, parentId, name, description) VALUES (2, 1, '', '  !'); INSERT INTO catalog_category (id, parentId, name, description) VALUES (3, 1, ' ', ' !'); INSERT INTO catalog_good (id, categoryId, name, description, price, oldPrice) VALUES (1, 2, ' Buzzword', '  2027!', 1000, NULL); INSERT INTO catalog_good (id, categoryId, name, description, price, oldPrice) VALUES (2, 2, ' Foobar', '  !', 300, 900); INSERT INTO catalog_good (id, categoryId, name, description, price, oldPrice) VALUES (3, 2, ' Barbaz', '   !', 500000, 300000); INSERT INTO catalog_good (id, categoryId, name, description, price, oldPrice) VALUES (4, 3, ' Habr#', ', , !', 10000, NULL);
      
      





すべてのデータが正しく書き込まれていることを確認します。



cqlsh:ignitetest> SELECT * FROM catalog_category;



id | description | name | parentId

----+--------------------------------------------+--------------------+-----------

1 | ! | | null

2 | ! | | 1

3 | ! | | 1



(3 rows)

cqlsh:ignitetest> SELECT * FROM catalog_good;



id | categoryId | description | name | oldPrice | price

----+-------------+---------------------------+----------------------+-----------+--------

1 | 2 | 2027! | Buzzword | null | 1000

2 | 2 | ! | Foobar | 900 | 300

4 | 3 | , , ! | Habr# | null | 10000

3 | 2 | ! | Barbaz | 300000 | 500000



(4 rows)








Javaプロジェクトの初期化



Igniteを使用するには、2つの方法があります。ディストリビューションキットをignite.apache.orgからダウンロードするか、必要なJarファイルを独自のクラスとXMLで構成して添付するか、IgniteをJavaプロジェクトの依存関係として使用できます。 この記事では、2番目のオプションを検討します。



新しいプロジェクトを作成しましょう-私の意見では、最も幅広い聴衆のための古典的でわかりやすいツールとしてmavenを使用します。



リストに応じて:





メインのApache Igniteクラスを含むignite-coreも一時的にロードされます。



 <dependencies> <dependency> <groupId>org.apache.ignite</groupId> <artifactId>ignite-spring</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.apache.ignite</groupId> <artifactId>ignite-cassandra-store</artifactId> <version>2.0.0</version> </dependency> </dependencies>
      
      





次に、Javaの世界でCassandraテーブルを表すDTOクラスを作成します。



 import org.apache.ignite.cache.query.annotations.QuerySqlField; public class CatalogCategory { @QuerySqlField private long id; @QuerySqlField private Long parentId; @QuerySqlField private String name; @QuerySqlField private String description; // public getters and setters } public class CatalogGood { @QuerySqlField private long id; @QuerySqlField private long categoryId; @QuerySqlField private String name; @QuerySqlField private String description; @QuerySqlField private long price; @QuerySqlField private long oldPrice; // public getters and setters }
      
      





...またはKotlinで
 import org.apache.ignite.cache.query.annotations.QuerySqlField data class CatalogCategory(@QuerySqlField var id: Long, @QuerySqlField var parentId: Long?, @QuerySqlField var name: String?, @QuerySqlField var description: String?) { constructor() : this(0, null, null, null) } data class CatalogGood(@QuerySqlField var id: Long, @QuerySqlField var categoryId: Long, @QuerySqlField var name: String?, @QuerySqlField var description: String?, @QuerySqlField var price: Long, @QuerySqlField var oldPrice: Long) { constructor() : this(0, 0, null, null, 0, 0) }
      
      







SQLクエリに参加するフィールドに@QuerySqlFieldアノテーションを付けます。 フィールドがこの注釈でマークされていない場合、SQLを使用して抽出したり、フィールドでフィルターしたりすることはできません。



また、この例の範囲外のインデックスとフルテキストインデックスを定義するためのより細かい設定を行うこともできます。 Apache IgniteでのSQLのセットアップについての詳細は、ドキュメントの対応するセクションを参照してください



Apache Igniteの構成



src / main / resourcesで、apacheignite-cassandra.xmlファイルに構成を作成します(名前は任意に選択されます)。 完全な構成を提供しますが、これは非常に膨大です。その後、部分的に検討します。



 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.apache.ignite.cache.store.cassandra.datasource.DataSource" name="cassandra"> <property name="contactPoints" value="127.0.0.1"/> </bean> <bean class="org.apache.ignite.configuration.IgniteConfiguration"> <property name="cacheConfiguration"> <list> <bean class="org.apache.ignite.configuration.CacheConfiguration"> <property name="name" value="CatalogCategory"/> <property name="writeThrough" value="true"/> <property name="sqlSchema" value="catalog_category"/> <property name="indexedTypes"> <list> <value type="java.lang.Class">java.lang.Long</value> <value type="java.lang.Class">com.gridgain.test.model.CatalogCategory</value> </list> </property> <property name="cacheStoreFactory"> <bean class="org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory"> <property name="dataSource" ref="cassandra"/> <property name="persistenceSettings"> <bean class="org.apache.ignite.cache.store.cassandra.persistence.KeyValuePersistenceSettings"> <constructor-arg type="java.lang.String"><value><![CDATA[ <persistence keyspace="IgniteTest" table="catalog_category"> <keyPersistence class="java.lang.Long" strategy="PRIMITIVE" column="id"/> <valuePersistence class="com.gridgain.test.model.CatalogCategory" strategy="POJO"/> </persistence>]]></value></constructor-arg> </bean> </property> </bean> </property> </bean> <bean class="org.apache.ignite.configuration.CacheConfiguration"> <property name="name" value="CatalogGood"/> <property name="readThrough" value="true"/> <property name="writeThrough" value="true"/> <property name="sqlSchema" value="catalog_good"/> <property name="indexedTypes"> <list> <value type="java.lang.Class">java.lang.Long</value> <value type="java.lang.Class">com.gridgain.test.model.CatalogGood</value> </list> </property> <property name="cacheStoreFactory"> <bean class="org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory"> <property name="dataSource" ref="cassandra"/> <property name="persistenceSettings"> <bean class="org.apache.ignite.cache.store.cassandra.persistence.KeyValuePersistenceSettings"> <constructor-arg type="java.lang.String"><value><![CDATA[ <persistence keyspace="IgniteTest" table="catalog_good"> <keyPersistence class="java.lang.Long" strategy="PRIMITIVE" column="id"/> <valuePersistence class="com.gridgain.test.model.CatalogGood" strategy="POJO"/> </persistence>]]></value></constructor-arg> </bean> </property> </bean> </property> </bean> </list> </property> </bean> </beans>
      
      





設定形式はSpring Beansです。



構成は、Cassandraとの通信を確立するためのデータソースの定義と、この例ではCassandraのテーブルに完全に対応する作業キャッシュの指定に限定されるApache Ignite設定の定義の2つのセクションに分けることができます。



設定の最初の部分は簡潔です:



  <bean class="org.apache.ignite.cache.store.cassandra.datasource.DataSource" name="cassandra"> <property name="contactPoints" value="127.0.0.1"/> </bean>
      
      





Cassandraデータソースを定義し、接続の確立を試みることができるアドレスを指定します。



次は、Apache Igniteの構成です。 このテストの一環として、デフォルト設定からの逸脱は最小限になるため、 cacheConfiguration



プロパティのみを再定義します。このプロパティには、クラスターで実行されているキャッシュのリストが含まれます。



  <bean class="org.apache.ignite.configuration.IgniteConfiguration"> <property name="cacheConfiguration"> <list> ... </list> </property> </bean>
      
      





最初のキャッシュは、catalog_categoryテーブルです。



  <bean class="org.apache.ignite.configuration.CacheConfiguration"> <property name="name" value="CatalogCategory"/> ... </bean>
      
      





その中で読み書きモードとライトスルーモードを有効にし(キャッシュに何かを書き込むと、Cassandraで書き込み操作が自動的に複製されます)、catalog_categoryスキームがSQLで使用されることを示し、このキャッシュに格納されるタイプをリストします。 SQLを介してアクセスするために処理されます。 タイプは常にキーと値のペアで示されるため、リストアイテムの数は常に偶数である必要があります。



  <property name="readThrough" value="true"/> <property name="writeThrough" value="true"/> <property name="sqlSchema" value="catalog_category"/> <property name="indexedTypes"> <list> <value type="java.lang.Class">java.lang.Long</value> <value type="java.lang.Class">com.gridgain.test.model.CatalogCategory</value> </list> </property>
      
      





最後に、Cassandraとリンクしましょう。2つの主要なサブセクションがあります。 最初に、以前に作成したDataSource:cassandraへのリンクを提供します。 次に、CassandraテーブルとIgnite Key-Valueレコードを相関させる方法を指定する必要があります。 これはpersistenceSettings



プロパティを通じて行われます。このプロパティでは、マッピング構成で外部XMLファイルを参照する方が適切ですが、簡単にするために、このXMLをCDATA要素としてSpring構成に直接埋め込みます。



  <property name="cacheStoreFactory"> <bean class="org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory"> <property name="dataSource" ref="cassandra"/> <property name="persistenceSettings"> <bean class="org.apache.ignite.cache.store.cassandra.persistence.KeyValuePersistenceSettings"> <constructor-arg type="java.lang.String"><value><![CDATA[ <persistence keyspace="IgniteTest" table="catalog_category"> <keyPersistence class="java.lang.Long" strategy="PRIMITIVE" column="id"/> <valuePersistence class="com.gridgain.test.model.CatalogCategory" strategy="POJO"/> </persistence>]]></value></constructor-arg> </bean> </property> </bean> </property>
      
      





マッピングの構成は非常に直感的に見えます。



 <persistence keyspace="IgniteTest" table="catalog_category"> <keyPersistence class="java.lang.Long" strategy="PRIMITIVE" column="id"/> <valuePersistence class="com.gridgain.test.model.CatalogCategory" strategy="POJO"/> </persistence>
      
      





トップレベル( persistence



タグ)で、 Keyspace



(この場合はIgniteTest)とTable



catalog_category



)が指定されます。これらは相関します。 次に、Igniteキャッシュのキーがプリミティブ型で、Cassandraテーブルのid列に対応するLong型であることが示されます。 値はCatalogCategory



クラスで、 Reflection



stategy="POJO"



)を使用してCassandraテーブルの列から形成する必要があります。



ドキュメントの対応するセクションで、この例の範囲外のより細かいマッピング設定の詳細を読むことができます



商品に関するデータを含む2番目のキャッシュの構成も同様です。



打ち上げ



開始するには、クラスcom.gridgain.test.Starter



作成します。



 package com.gridgain.test; import org.apache.ignite.Ignite; import org.apache.ignite.Ignition; public class Starter { public static void main(String... args) throws Exception { final Ignite ignite = Ignition.start("apacheignite-cassandra.xml"); ignite.cache("CatalogCategory").loadCache(null); ignite.cache("CatalogGood").loadCache(null); } }
      
      





ここでは、 Ignition.start(...)



命令を使用してApache Igniteノードを開始し、クラスパスにあるapacheignite-cassandra.xmlファイルを構成ソースとして指定します。



SQL



JDBCをサポートする任意のクライアントを使用して、組み込みのIntelliJ IDEAやSquirrelSQLなどのSQLクエリを実行できます。 後者の場合、たとえば、Apache Igniteドライバーを追加する必要があります(これはignite-core Jarファイルにあり、配布パッケージの一部としてダウンロードできます)。







jdbc:ignite:// localhost / CatalogGoodという形式のURLを使用して新しい接続を作成します。ここで、localhostはApache Igniteノードのいずれかのアドレスであり、CatalogGoodはデフォルトで要求が送信されるキャッシュです。







可能なSQLクエリの例:



 SELECT cg.name goodName, cg.price goodPrice, cc.name category, pcc.name parentCategory FROM catalog_category.CatalogCategory cc JOIN catalog_category.CatalogCategory pcc ON cc.parentId = pcc.id JOIN catalog_good.CatalogGood cg ON cg.categoryId = cc.id;
      
      





goodName goodPrice カテゴリー parentCategory
バズワード冷蔵庫 1000 冷蔵庫 家電製品
フーバー冷蔵庫 300 冷蔵庫 家電製品
冷蔵庫バルバズ 500,000 冷蔵庫 家電製品
Habr Machine# 10,000 洗濯機 家電製品


 SELECT cc.name, AVG(cg.price) avgPrice FROM catalog_category.CatalogCategory cc JOIN catalog_good.CatalogGood cg ON cg.categoryId = cc.id WHERE cg.price <= 100000 GROUP BY cc.id;
      
      





お名前 avgPrice
冷蔵庫 650
洗濯機 10,000


おわりに



この簡単な例では、Apache Cassandraの上でApache Igniteを使用して、ACIDトランザクションとRAM速度で分散SQLエンジンを上げる方法を見ることができます。



既存のインフラストラクチャまたはグリーンフィールドプロジェクトでApache Cassandraに直面した場合、この記事を覚えておいてください。また、CassandraはIgniteの趣味に優れていることを思い出してください。 または、IgniteとCassandraの長所を使用して、たとえば、モノのインターネットの世界から、ある種のプロジェクトを作成しようとすることもできます。



All Articles