CQL3のCassandra 2.0のスキーマシミュレーションの例

前の記事で、Cassandraがデータを保存する方法を明快に説明しました。 少なくとも目を走らせることを強くお勧めします。 この記事では、 次の記事で使用する単純なデータベースを作成します。これは、データの選択/取得に完全に専念します。



挑戦する



広告をスピンする広告ネットワークがあるとします。 人々はバナーをクリックし、広告顧客は支払い、私たち(ネットワーク)、再販業者(販売業者)、および広告ホスティングホスティング会社はこれに収入を得ます。 広告スロットの再販業者は20%働いています。 この割合はさまざまな要因によって増加しますが、最も重要なことは、一定ではなく、たとえば1か月前のクリックに新しい割合を適用できることです。



必要なのは、各リセラーの収入を任意の期間ですばやくカウントできること、クリックのスケジュールをリアルタイムで維持することです。





免責事項






教育プログラム





6つのノードがあると仮定しましょう。



cqlsh


cqlsh



実行します。

 Connected to Test Cluster at localhost:9160. [cqlsh 4.1.0 | Cassandra 2.0.2 | CQL spec 3.1.1 | Thrift protocol 19.38.0] cqlsh>
      
      





キースペースを作成する


キースペース(データベース)を作成します。

 CREATE KEYSPACE ad_network WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '3' }; USE ad_network;
      
      





replication_factor



は、文字列を保存するノードの数です。



モデルを作成する





リセラー表


リセラーテーブルを作成し、データを入力します。 表には、再販業者の金利の変化の履歴が保存されます。

 CREATE TABLE reseller ( id text, effective_since text, -- day in the format of 'YYYY-MM-DD' reward_percent float, -- value from 0.0 to 1.0 PRIMARY KEY (id, effective_since) --   (id)    (effective_since) ) WITH CLUSTERING ORDER BY (effective_since DESC); --    LIMIT 1  INSERT INTO reseller (id, effective_since, reward_percent) VALUES ('supaboobs', '2011-02-13', 0.2); INSERT INTO reseller (id, effective_since, reward_percent) VALUES ('supaboobs', '2012-01-22', 0.25); INSERT INTO reseller (id, effective_since, reward_percent) VALUES ('supaboobs', '2013-11-30', 0.3);
      
      





3行作成したようです。 しかし、 前の記事を読んだ人は 'supaboobs'



配布キーと3つのクラスターキー '2011-02-13'



'2012-01-22'



'2013-11-30'



1行作成したことを知っています 。 この行と後続のすべての行は、6つのノードのうち3つに保存されます。



内容を見てみましょう:

 cqlsh:ad_network> SELECT * FROM reseller WHERE id='supaboobs'; id | effective_since | reward_percent -----------+-----------------+---------------- supaboobs | 2013-11-30 | 0.3 supaboobs | 2012-01-22 | 0.25 supaboobs | 2011-02-13 | 0.2
      
      







将来、現在の金利が必要になったときに、次のことを行います。

 cqlsh:ad_network> SELECT * FROM reseller WHERE id = 'supaboobs' LIMIT 1; id | effective_since | reward_percent -----------+-----------------+---------------- supaboobs | 2013-11-30 | 0.3
      
      







Ad_clickテーブル


これで、バナーのクリックを保存します。

列:販売代理店ID、日(検索を高速化するため)、日付+クリック時間、バナーID、クリックの総費用。

 CREATE TABLE ad_click ( reseller_id text, day text, -- day in the format of 'YYYY-MM-DD' time timestamp, ad_id text, amount float, PRIMARY KEY ((reseller_id, day), time, ad_id) --   (reseller_id, day)    (time, ad_id) ) WITH CLUSTERING ORDER BY (time DESC); --     
      
      





データを追加します。

 INSERT INTO ad_click (reseller_id, day, time, ad_id, amount) VALUES ('supaboobs', '2013-11-28', '2013-11-28 02:16:52', '890_567_234', 0.005); INSERT INTO ad_click (reseller_id, day, time, ad_id, amount) VALUES ('supaboobs', '2013-11-28', '2013-11-28 07:17:35', '890_567_234', 0.005); INSERT INTO ad_click (reseller_id, day, time, ad_id, amount) VALUES ('supaboobs', '2013-11-29', '2013-11-29 17:18:51', '890_567_211', 0.0075); INSERT INTO ad_click (reseller_id, day, time, ad_id, amount) VALUES ('supaboobs', '2013-11-29', '2013-11-29 22:20:37', '890_567_211', 0.0075); INSERT INTO ad_click (reseller_id, day, time, ad_id, amount) VALUES ('supaboobs', '2013-11-30', '2013-11-30 11:21:56', '890_567_234', 0.005); INSERT INTO ad_click (reseller_id, day, time, ad_id, amount) VALUES ('supaboobs', '2013-12-01', '2013-12-01 12:21:59', '890_567_010', 0.01);
      
      





それらを見てみましょう。

 cqlsh:ad_network> SELECT * FROM ad_click; reseller_id | day | time | ad_id | amount -------------+------------+---------------------+-------------+-------- supaboobs | 2013-12-01 | 2013-12-01 12:21:59 | 890_567_010 | 0.01 supaboobs | 2013-11-30 | 2013-11-30 11:21:56 | 890_567_234 | 0.005 supaboobs | 2013-11-28 | 2013-11-28 07:17:35 | 890_567_234 | 0.005 supaboobs | 2013-11-28 | 2013-11-28 02:16:52 | 890_567_234 | 0.005 supaboobs | 2013-11-29 | 2013-11-29 22:20:37 | 890_567_211 | 0.0075 supaboobs | 2013-11-29 | 2013-11-29 17:18:51 | 890_567_211 | 0.0075
      
      





複合配布キー (reseller_id, day)



、実際に4行が作成されました(理由がわかりにくい場合は、 前の記事を読んでください。すべてが適切に配置されます)。 再販業者ごとに毎日新しい行を作成し、データを入力することがわかりました。 クラスターキーも複合time, ad_id



です。



Amount_by_dayテーブル


金利は1日に1回しか変更できないため、これで数ミリ秒とプロセッサー時間を稼ぐことができます。 同じお金を保存する別のテーブルを作成しましょう。ただし、クリックに分割することはありません。

 CREATE TABLE amount_by_day ( reseller_id text, day text, -- day in the format of 'YYYY-MM-DD' amount double, PRIMARY KEY (reseller_id, day) --   (reseller_id)    (day) ) WITH CLUSTERING ORDER BY (day DESC); --    
      
      





1日1回記入する必要があります。 システムの別のコードがad_click



からデータを収集し、要約してamount_by_day



書き込みます。



クリック数テーブル


当然、どのバナーをクリックしたかを知ることが重要です。 ただし、6つすべてのノードでSELECT COUNT(0) FROM ad_click WHERE ad_id='...'



を実行するとコストが高くなり(CQLにCOUNT



操作がない)、C *にはcounterなどのようなものがあります。 s。



カウンターは列のタイプ、つまり timestamp



text



double



などのように構文的に使用されtext



。ただし、制限があります。 テーブルに少なくとも1つのカウンターがある場合、他のすべての列もカウンター型でなければなりません(もちろん、PRIMARY KEYを除く)。 テーブルを作成しましょう:

 CREATE TABLE clicks_per_ad ( ad_id text, clicks counter, PRIMARY KEY (ad_id)); --   (ad_id),   
      
      





そのため、表ではclicks



列の値を変更できます。

 cqlsh:ad_network> SELECT * FROM clicks_per_ad; (0 rows) cqlsh:ad_network> UPDATE clicks_per_ad SET clicks = clicks + 1 WHERE ad_id = '890_567_234'; cqlsh:ad_network> SELECT * FROM clicks_per_ad; ad_id | clicks -------------+-------- 890_567_234 | 1 (1 rows) cqlsh:ad_network> UPDATE clicks_per_ad SET clicks = clicks + 1 WHERE ad_id = '890_567_234'; cqlsh:ad_network> SELECT * FROM clicks_per_ad; ad_id | clicks -------------+-------- 890_567_234 | 2 (1 rows) cqlsh:ad_network> UPDATE clicks_per_ad SET clicks = 0 WHERE ad_id = '890_567_234'; cqlsh:ad_network> SELECT * FROM clicks_per_ad; ad_id | clicks -------------+-------- 890_567_234 | 0 (1 rows)
      
      







したがって、 signed int



れていれば、何でも考慮できます。 つまり 例外的な整数も考慮できますが、範囲は-2 ^ 63-+ 2 ^ 63です。



最初はテーブルに行がなかったが、 UPDATE



コマンドの後に突然行が表示されたことは注目に値します。 これはCQLの機能です。 INSERT



UPDATE



は基本的に同じコマンドです。 C *にデータが既に存在する(または「まだ」ではない)場合、データを更新/挿入しない機会があることを予約します。 これは「軽量トランザクション」と呼ばれ、通常のデータ書き込み操作に比べて動作が遅くなります。



もちろん、クリック数は任意の基準(キー)で収集できます。 例:

 CREATE TABLE clicks_per_reseller_per_day ( reseller_id text, day text, -- day in the format of 'YYYY-MM-DD' clicks counter, PRIMARY KEY ((reseller_id, day))); --   (reseller_id, day),    CREATE TABLE clicks_per_reseller ( reseller_id text, clicks counter, PRIMARY KEY (reseller_id)); --   (reseller_id),   
      
      







テキストIDについて少し


RDBMSでは、int型の一意の識別子を文字列に割り当てるために使用されます。 識別子をテキストにすると、意味のあるものになりますか? はい。生産性が低下するためです。 個人的に、それは私を大いに落胆させました。 私たちは、運転免許証のIDが数字であり、保険証券のIDが数字であるという事実に慣れています。 しかし、パスポートIDなどの文字を混ぜなければならないことがよくあります-2文字と6桁、多くの場合は文字またはハイフンを含む家番号など。



C *では、RDBMSとは異なり、加速を行わないため、キーとしてドライナンバーを使用することは一般的ではありません。 はい。C*の自動インクリメントはありません(ただし、一意のIDが突然必要な場合はtimeuuidがあります)。 異常は、列タイプreseller_id



としてtext



見える場合がありtext



。 C *では、パーティションキーはハッシュを比較して検索されます。 つまり 文字列の直接比較は行われません。つまり、パフォーマンスは低下しません。



データ記録



ワンクリックを記録するには、 4回もの UPDATE



操作を行う必要があります。 私は狂っていません。 C *への書き込みは操作が速すぎるため、パフォーマンスは低下しません。 6ノードでの書き込み速度は、RDBMSは言うまでもなく、 MongoDBの100倍、またはHBaseの2〜5倍です。 C *の最近のバージョンでは、ディスクスペース( 圧縮圧縮 )を個別に最適化できるため、すべてのハードディスクスペースは問題ありません。



すべてのINSERTが機能することを確認するために、BATCHなどがあります。 残念ながら、それらは同じ配布キー内で(同じ行内で)動作します。

 BEGIN BATCH -- INSERT, UPDATE, DELETE ... APPLY BATCH;
      
      







バッチは、RDBMSのトランザクションの代替ではありません。 それらの助けを借りて、C *は複数のコマンドではなく、1つのパッケージですべてのコマンドを転送するため、ネットワークが最適化されます。 バッチには2つのタイプがあります。

  1. ログに記録されていない-BEGIN UNLOGGED BATCH-通常のバッチ。 ただし、コーディネーターノード(そのノードに来たCQLコマンドと他のノードとの通信を担当するノード)がバッチの途中で停止すると、データの整合性が損なわれる可能性があります。
  2. アトミック(アトミック)-開始BEGIN BATCH



    -この場合、C *はすべてのデータが書き込まれるか、何も書き込まれないことを確認します。 ただし、この操作は約30%遅くなります。




データの読み取り



次の記事は 、このトピックに専念しています。 SELECT ... FROM ... WHERE



操作には、RDBMSと比較して多くの制限があるため、これには特に注意を払う必要があります。



おわりに



この投稿の目的は、Kassandraでデータベースをモデル化するアプローチがRDBMSとどのように異なるかを示すことでした。 ご覧のように、アプローチは根本的に異なります。 また、CQLとSQLの類似性に混乱しないでください。実際、構文のみが一致します。 ここではいくつかのトリックと特徴的な機能のみを示しましたが、さらに多くの機能があります。



サイクルの前の記事

シリーズの次の記事



All Articles