
このブログでは、アプリケーションの概要を紹介し、SingleStore の主要な機能の詳細を掘り下げて解説します。これらの機能こそが、1つのデータベースだけでこのようなタイプのアプリケーションを構築することを可能にしています。
アプリケーション概要
このデモでは、数百万人の加入者を持つ架空の通信事業者をシミュレーションし、リアルタイムで顧客ごとにターゲティングされたオファーを配信します。
広告のターゲティングは、人口統計情報、地理的位置、購買履歴など多様な要素に基づいて行われます。
SingleStore は、8,000万件の広告配信機会 をリアルタイムに評価し、3万〜5万件の広告・オファー をスマートフォンに同時配信するシミュレーションを実施しています。
このデジタルマーケティングアプリは、リアルタイムデータとバッチデータの両方を活用し、マッチングアルゴリズムと高度な顧客セグメンテーションに基づいてオファー通知を生成・配信します。
.png?width=1024&disable=upscale&auto=webp)
さらに、このアプリケーションには運用分析(Operational Analytics)の側面もあり、広告主向けのリアルタイムコンバージョン指標など、さまざまな統計情報を可視化できます。
以下は、アプリケーション内で表示されるダッシュボードの一例です。

1つのデータベースで OLTP(トランザクション処理)と OLAP(分析処理)の両方を、さまざまなデータタイプとともに扱えることは、SingleStore の主要な特長のひとつです。
ここからは、このアプリケーションを支える SingleStore の主要機能を見ながら、どのようにそれが実現されているのかを解説していきます。
スキーマ設計
SingleStore には、HTAP アプリケーションのパフォーマンスを最適化するために利用できる、特長の異なる3種類のテーブルタイプがあります。
Universal Storage
SingleStore のデフォルトのテーブルタイプは、特許技術である Universal Storage テーブルです。
これは、SingleStore の3つのストレージ層 ― メモリ、ディスク、オブジェクトストレージ ― を統合した構造になっています。
このテーブルタイプにデータを書き込むと、まずメモリに格納され、すぐにクエリ実行が可能になります。
メモリに先に書き込むことで、非常に高いスループットを実現します。
その後、データはディスク上のカラム型ストアにフラッシュ(書き出し)されます。
このレイヤーでは、データは通常75〜80% 圧縮されます。これにより、高速なスキャンが可能になると同時に、サブセグメントアクセスとハッシュインデックスを用いて単一行のデータを効率的に取得できます。
さらに、カラムグループインデックスを使用することで、OLTP クエリのパフォーマンスをさらに向上させることができます。
これらの特性により、Universal Storage テーブルは OLTP と OLAP の両方のワークロードで高い性能を発揮します。
このデモでは、OLAP タイプのクエリで使用される大量のデータ挿入が発生するテーブルに、このテーブルタイプ(Universal Storage)を採用しています。
たとえば、データが継続的に追加されていく「locations」テーブルがその一例です。
以下は、そのテーブルの DDL(データ定義言語)です。
1CREATE TABLE IF NOT EXISTS locations (2 city_id BIGINT NOT NULL,3 subscriber_id BIGINT NOT NULL,4 ts DATETIME(6) NOT NULL SERIES TIMESTAMP,5 lonlat GEOGRAPHYPOINT NOT NULL,6 7 -- open location code length 8 (275m resolution)8 olc_8 TEXT NOT NULL,9 10 SHARD KEY (city_id, subscriber_id),11 SORT KEY (ts),12 13 KEY (city_id, subscriber_id) USING HASH,14 KEY (olc_8) USING HASH15);
先ほどの CREATE TABLE DDL では、shard key(シャードキー) と sort key(ソートキー) が定義されていることに気づいたかもしれません。
これらは SingleStore において非常に重要な設計要素であり、パフォーマンスに大きく影響します。
シャードキー(shard key) は、SingleStore 内で行データをどのようにパーティションに分散させるかを制御します。
一方、ソートキー(sort key) は、各パーティション内でデータがどのような順序で格納されるかを決定し、クエリ実行時にスキャンされるデータ量に直接影響します。
さらに、locations テーブルは、リレーショナルデータ、地理空間データ(geo-spatial)、および時系列データ(time-series)を統合して扱うことで、SingleStore のマルチモデル対応の能力を示しています。
SingleStore は、構造化データ・半構造化データ・非構造化データをすべて1つのデータベースで処理できるため、さまざまなワークロードを統合的に扱うことが可能です。これにより、用途ごとに異なる専用データベースを使い分ける必要がなくなり、SingleStore の柔軟性と高い処理能力が際立ちます。
Rowstore テーブル
Universal Storage でも OLTP ワークロードを十分に処理できますが、さらに高いパフォーマンスが求められる場合には、インメモリ型の Rowstore テーブルを使用できます。
このテーブルはメモリ上に行形式(row format)で格納されるため、更新(update)や削除(delete)が頻繁に行われる高い同時実行性を持つ OLTP ワークロードに最適です。
そのため、このデモでは、書き込みの大部分が更新処理となる subscribers_last_notification テーブルにこのテーブルタイプを採用しています。
以下は、そのテーブルを作成するために使用された DDL です。
1CREATE ROWSTORE TABLE IF NOT EXISTS subscribers_last_notification (2 city_id BIGINT NOT NULL,3 subscriber_id BIGINT NOT NULL,4 last_notification DATETIME(6),5 6 PRIMARY KEY (city_id, subscriber_id),7 INDEX (last_notification)8);
Reference テーブル
SingleStore で利用できる最後のテーブルタイプが Reference テーブルです。これは、大規模なファクトテーブルと頻繁に結合されるディメンション系のテーブルに最適です。
このテーブルタイプでは、テーブル全体のコピーが各リーフノードに保持されるため、結合時にネットワークを介してデータを移動させる必要がなくなります。
その結果、結合処理の高速化とネットワーク負荷の軽減が実現されます。
このデモに登場する cities テーブルは、Reference テーブルの強力さを示す好例です。
1CREATE ROWSTORE REFERENCE TABLE IF NOT EXISTS cities (2 city_id BIGINT NOT NULL PRIMARY KEY,3 city_name TEXT NOT NULL,4 center GEOGRAPHYPOINT NOT NULL,5 diameter DOUBLE6);
この軽量なテーブルには4つのカラムがあり、アプリケーションの拡張性において非常に重要な役割を果たします。
各カラムには、頻繁に変更されることのないデータが格納されており、すべてのノード間で静的に保持されるテーブルとして理想的です。
たとえば、locations テーブルをこのテーブルと結合する場合、処理は各リーフノード上でローカルジョインとして完結します。
そのため、ネットワークを介してデータを移動させる必要がなく、高速で効率的な結合処理が実現されます。
データ取り込み(Ingest)
都市内を歩き回り、購買行動を行う加入者をシミュレートするために、Golang(Go言語)で作成したシミュレーターを使用して合成データ(synthetic data)を生成しています。
生成されたデータは Parquet ファイルとして書き出され、AWS S3 バケットにアップロードされます。
ここから、SingleStore へのデータ取り込みは Pipelines(パイプライン) を通じて行われます。
Pipelines は、大規模で並列的なデータ取り込みを行うよう設計されており、リアルタイムデータとバッチデータの両方に対応しています。
たとえば、Kafka のようなストリーミングデータソースや、S3 に保存されたファイルベースのバッチデータなど、さまざまなデータソースからの取り込みが可能です。
この柔軟性により、データがどのように生成・保存されていても、効率的かつシームレスに SingleStore に取り込むことが可能となっています。
SingleStore でパイプラインを作成するのは非常に簡単で、シンプルな構文によって開発者はすぐにデータ取り込みプロセスを構築できます。
以下は、このデモで使用されているパイプライン定義の一例です。
このステートメントでは、データの保存場所(location)、認証情報(credentials)、そして パイプラインの取り込み先(対象テーブルなど) を指定しています。
1CREATE OR REPLACE PIPELINE locations2AS LOAD DATA S3 'singlestore-realtime-digital-marketing/locations.*'3CREDENTIALS '{}'4CONFIG '{ "region": "us-east-1" }'5INTO PROCEDURE process_locations FORMAT PARQUET (6 subscriber_id <- subscriberid,7 offset_x <- offsetX,8 offset_y <- offsetY9);
通常、パイプラインはデータを直接挿入または更新する特定のテーブルを参照します。
しかし、書き込み処理に追加のロジックを組み込みたい場合は、パイプラインの出力先をストアドプロシージャに指定することもできます。
このデモではまさにその方法を採用しており、1つのパイプラインから複数のテーブルにデータを書き込む仕組みを構築しています。
以下は、locations パイプラインが書き込み先として利用しているストアドプロシージャの例です。
このプロシージャでは、まず subscribers テーブル内のユーザーの現在位置を更新し、同時に locations テーブルへ新しい位置情報データを挿入しています。
このようにして、リアルタイムにユーザーの位置を追跡しながら、履歴データを継続的に蓄積する仕組みを実現しています。
1DELIMITER //2 3CREATE OR REPLACE PROCEDURE process_locations (4 _batch QUERY(5 subscriber_id BIGINT NOT NULL,6 offset_x DOUBLE NOT NULL,7 offset_y DOUBLE NOT NULL8 )9)10 11AS12DECLARE13 _expanded QUERY(city_id BIGINT, subscriber_id BIGINT, lonlat GEOGRAPHYPOINT) = SELECT14 city_id, subscriber_id,15 GEOGRAPHY_POINT(16 GEOGRAPHY_LONGITUDE(center) + (offset_x * diameter),17 GEOGRAPHY_LATITUDE(center) + (offset_y * diameter)18 ) AS lonlat19 FROM _batch, cities;20 21BEGIN22 INSERT INTO subscribers (city_id, subscriber_id, current_location)23 SELECT city_id, subscriber_id, lonlat24 FROM _expanded25 ON DUPLICATE KEY UPDATE current_location = VALUES(current_location);26 27 INSERT INTO locations (city_id, subscriber_id, ts, lonlat, olc_8)28 SELECT29 city_id,30 subscriber_id,31 now(6) AS ts,32 lonlat,33 encode_open_location_code(lonlat, 8) AS olc_834 FROM _expanded;35 36END //
このデモのデータ取り込み(ingest)部分では、SingleStore の3層アーキテクチャによって、ストリーミングデータとバッチデータの両方を効率的に処理できることが示されています。
さらに、更新(update)や削除(delete)といった操作も高いパフォーマンスで実行できるため、大量データの取り込みや変化の激しい動的データを扱うユースケースに最適なソリューションとなっています。
クエリ
このリアルタイム・デジタルマーケティングアプリケーションは、ユーザーの行動やリアルタイムの位置情報に基づいて、ターゲット広告やオファーを配信する際の運用分析(Operational Analytics)の強力さも示しています。
このアプリケーションでは、次の3つの主要コンポーネントを活用しています。
- オファー(Offers). オファーは、オファーID、ベンダー、説明、期限といった詳細情報を含むテーブルに格納されています。
- セグメント(Segments). セグメントはインメモリテーブルで定義されており、顧客特性の高速な検索(ルックアップ)が可能です。
- マッチング(Matching). マッチング処理は 50ミリ秒未満で実行され、これまでの要素(オファーとセグメント)をリアルタイムの顧客データと組み合わせて、どのオファーが最も関連性が高いかを判定し、即座に通知を送信します。
これは、高度に最適化された SQL クエリによって実現されており、顧客情報・セグメント・オファーの各テーブルを結合し、複雑なフィルタリングとランキングロジックを適用して、最小限のレイテンシーで最適な結果を選定します。
これらの処理はすべて 100ミリ秒以内に完了します。
このアプリケーションのフロントエンドは React を使用して構築された シングルページWebアプリケーションです。
SingleStore の Data API と接続することで、シームレスにデータを取得できるようになっています。
ユーザーはアプリ上で関心のある都市を選択し、その都市でリアルタイムに配信されているオファーを地図上で視覚的に確認できます。
さらに、アプリには以下のような運用指標も表示されます。
- クエリ実行時間のメトリクス
- 取り込まれたデータ量
- 到達している加入者数
- 配信されたオファー数
これにより、リアルタイム分析とデータ可視化を一体化したインタラクティブな体験を提供しています。
このアプリケーションには アナリティクスタブ(Analytics Tab) も用意されており、オファーの成果を可視化できます。
このページでは、総コンバージョン率(gross conversion rate)と純コンバージョン率(net conversion rate)の両方をリアルタイムで表示します。
また、各企業ごとの成果を示すソート可能なダッシュボードも備えており、以下の指標に基づいて昇順・降順で並べ替えることができます。
これらの機能はすべて SingleStore の Data API によって実現されています。
この API により、ブラウザから直接 SQL クエリを実行できるため、別途バックエンドサーバーを用意する必要がなく、その分のレイテンシ(遅延)も排除されます。
このアーキテクチャにより、顧客の位置情報を地図上にリアルタイム表示したり、オファーの詳細を即時に更新・表示するといった、インタラクティブでリアルタイム性の高い操作が可能になっています。
SingleStore でリアルタイムアプリケーションを構築しよう
このアプリケーションは、SingleStore がトランザクション処理(OLTP)と分析処理(OLAP)の両方を1つのシステム内で処理できる能力を持つことを示しています。
数百万件の広告配信機会を評価し、数千件の広告やオファーをリアルタイムでシミュレートすることが可能です。
このような統合型の運用分析(Operational Analytics)アプローチにより、企業はデータドリブンな意思決定を行い、大規模かつパーソナライズされた体験をユーザーに提供することができます。
自分のリアルタイムアプリケーションを構築してみませんか?
SingleStore を使えば、無料で今すぐ始めることができます。










.png?width=24&disable=upscale&auto=webp)








