Salesforce Big Objectsマスターガイド:アーキテクトのための大規模データ管理術

Salesforce アーキテクトとして、エンタープライズレベルのソリューションを設計する上で、データ戦略は常に中心的な課題となります。特に、IoT、イベントログ、履歴データなど、日々膨大な量のデータが生成される現代において、これらをいかに効率的かつスケーラブルに管理するかは、システム全体のパフォーマンスと将来性に直結します。この記事では、Salesforce Platform上で大規模データを扱うための強力なソリューション、Big Objects (ビッグオブジェクト) に焦点を当て、そのアーキテクチャ上の意義、設計原理、そしてベストプラクティスを深く掘り下げていきます。


背景と応用シナリオ

Salesforceの標準オブジェクトやカスタムオブジェクトは、トランザクショナルなデータ(顧客情報、商談、ケースなど)を扱うのに非常に優れています。しかし、そのストレージアーキテクチャは、数十億レコードといった超大規模データの格納やクエリを前提として設計されていません。大量のデータをカスタムオブジェクトに格納しようとすると、パフォーマンスの低下、ガバナ制限、そして高額なストレージコストといった問題に直面します。

ここで登場するのが Big Objects です。Big Objectsは、Salesforce Platform上で数十億レコード以上のデータをネイティブに格納し、一貫したパフォーマンスで処理できるように設計された、特別なタイプのオブジェクトです。これらは、Apache HBaseのような実績のあるビッグデータ技術を基盤としています。

主な応用シナリオ

  • イベントモニタリング (Event Monitoring): ユーザーのログイン履歴、APIコール、Apex実行など、Salesforce組織内の詳細な監査ログを長期間保持する場合。
  • IoTデータ (Internet of Things): 無数のセンサーやデバイスから送られてくる時系列データを蓄積し、分析の基盤とする場合。
  • 顧客エンゲージメントの追跡: Webサイトのクリックストリーム、メールの開封履歴、アプリケーションの利用ログなど、顧客の360度ビューを構築するための詳細なインタラクションデータを格納する場合。
  • 履歴データのアーカイブ (Historical Data Archiving): 古くなった商談、ケース、活動履歴などを、標準オブジェクトからBig Objectsへ移動させることで、本番環境のパフォーマンスを維持しつつ、コンプライアンス要件のためにデータを保持する場合。

原理説明

Big Objectsを効果的に活用するためには、そのアーキテクチャ上の特性を理解することが不可欠です。

インデックスの重要性

Big Objectsの最大の特徴は、複合主キー (Composite Primary Key)、すなわちインデックスの設計にあります。標準オブジェクトのID項目とは異なり、Big Objectsのインデックスは複数の項目(最大5つ)を組み合わせて定義します。

このインデックスが、Big Objectsのレコードを一意に識別し、そしてクエリのパフォーマンスを決定づける唯一の手段となります。 SOQLクエリを実行する際、インデックスを構成するすべての項目をWHERE句で指定する必要があります。インデックスの一部しか指定しない、あるいは全く指定しないクエリは実行できません。この制約こそが、数十億レコードの中からでも高速にデータを取得できる秘訣です。

インデックスの項目は、定義時に順序が決定されます。クエリのフィルタリング条件を考慮し、最も絞り込みの効く項目(カーディナリティが高い項目)をインデックスの先頭に配置することが、設計上の重要なポイントとなります。

2種類のBig Objects

Big Objectsには、標準とカスタムの2種類が存在します。

  • 標準Big Objects (Standard Big Objects): Salesforceが提供するもので、主にField Audit TrailやEvent Monitoringのデータ格納に使用されます。例:`FieldHistoryArchive`。
  • カスタムBig Objects (Custom Big Objects): ユーザーが独自に定義できるBig Objectsです。API参照名は `__b` で終わります。この記事では主にこちらを扱います。

アーキテクチャ上の制約

Big Objectsは大規模データ処理に特化しているため、標準オブジェクトが持つ多くの機能が利用できません。アーキテクトはこれらの制約を十分に理解した上で設計を行う必要があります。

  • UI非対応: 標準のレコード詳細ページ、リストビュー、レポート、ダッシュボードは利用できません。データの可視化には、Lightning Web Components (LWC) や Visualforce を用いたカスタムUI開発が必要です。
  • 自動化ロジック非対応: トリガ、ワークフロールール、プロセスビルダー、フローは実行できません。
  • 一部のSOQL機能制限: `COUNT()`、`GROUP BY`、`ORDER BY`、`LIKE` といった集計やソート、部分一致検索は利用できません。これらの処理には、後述する Async SOQL (非同期SOQL) を使用します。
  • 共有・セキュリティ: レコードレベルの共有ルールは適用されません。オブジェクトレベルおよび項目レベルのセキュリティ(プロファイル/権限セット)のみが適用されます。

サンプルコード

ここでは、カスタムBig Objectの定義、データの挿入、そしてクエリの実行方法を具体的なコードで示します。

1. Big Objectの定義 (メタデータ)

Big Objectは、メタデータAPIまたはSalesforce DXを用いて定義します。以下は、Webサイトのアクセスログを格納する `Website_Access_Log__b` というBig Objectの定義ファイル (`.object` ファイル) の例です。

<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
    <deploymentStatus>Deployed</deploymentStatus>
    <fields>
        <fullName>Account_Id__c</fullName>
        <label>Account Id</label>
        <length>18</length>
        <required>true</required>
        <type>Text</type>
        <unique>false</unique>
    </fields>
    <fields>
        <fullName>Accessed_At__c</fullName>
        <label>Accessed At</label>
        <required>true</required>
        <type>DateTime</type>
    </fields>
    <fields>
        <fullName>URL__c</fullName>
        <label>URL</label>
        <length>255</length>
        <required>false</required>
        <type>Text</type>
        <unique>false</unique>
    </fields>
    <fields>
        <fullName>User_Agent__c</fullName>
        <label>User Agent</label>
        <length>255</length>
        <required>false</required>
        <type>Text</type>
        <unique>false</unique>
    </fields>
    <indexes>
        <fullName>WebsiteAccessLogIndex</fullName>
        <label>Website Access Log Index</label>
        <fields>
            <name>Account_Id__c</name>
            <sortDirection>ASC</sortDirection>
        </fields>
        <fields>
            <name>Accessed_At__c</name>
            <sortDirection>DESC</sortDirection>
        </fields>
    </indexes>
    <label>Website Access Log</label>
    <pluralLabel>Website Access Logs</pluralLabel>
</CustomObject>

この例では、`Account_Id__c` (テキスト型) と `Accessed_At__c` (日時型) を組み合わせた複合インデックス `WebsiteAccessLogIndex` を定義しています。これにより、「特定の取引先 (`Account_Id__c`) の、ある時点 (`Accessed_At__c`) のアクセスログ」を高速に特定できます。インデックスの順序 (`sortDirection`) も指定可能です。

2. Apexによるデータ挿入

Big Objectsへのデータ挿入は、Apexの `Database.insertImmediate()` メソッドを使用します。通常のDML `insert` はサポートされていません。このメソッドは同期的であり、ガバナ制限も独自のもの(1トランザクションあたり最大50,000レコード)が適用されます。

// 挿入するBig Objectレコードのリストを作成
List<Website_Access_Log__b> logsToInsert = new List<Website_Access_Log__b>();

// ログデータを生成
Website_Access_Log__b log1 = new Website_Access_Log__b(
    Account_Id__c = '001xx000003DHPPAA4',
    Accessed_At__c = Datetime.now(),
    URL__c = '/products/item-a',
    User_Agent__c = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...'
);
logsToInsert.add(log1);

Website_Access_Log__b log2 = new Website_Access_Log__b(
    Account_Id__c = '001xx000003DHPPAA4',
    Accessed_At__c = Datetime.now().addSeconds(-10),
    URL__c = '/products/item-b',
    User_Agent__c = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...'
);
logsToInsert.add(log2);

// Database.insertImmediateを使用してレコードを即時挿入
// このメソッドはBig Objects専用
Database.SaveResult[] results = Database.insertImmediate(logsToInsert);

// 結果をチェック
for (Database.SaveResult res : results) {
    if (res.isSuccess()) {
        System.debug('Successfully inserted Big Object record. ID: ' + res.getId());
    } else {
        for (Database.Error err : res.getErrors()) {
            System.debug('Error inserting Big Object record: ' + err.getStatusCode() + ': ' + err.getMessage());
            System.debug('Fields that affected the error: ' + err.getFields());
        }
    }
}

3. SOQLによるデータクエリ

前述の通り、SOQLクエリではインデックスを構成する全ての項目をWHERE句で指定する必要があります。この例では `Account_Id__c` と `Accessed_At__c` の両方をフィルタ条件に含めます。

// クエリ対象の取引先IDと日時を定義
Id targetAccountId = '001xx000003DHPPAA4';
Datetime targetDateTime = Datetime.newInstance(2023, 10, 27, 10, 0, 0);

// インデックス項目を全てWHERE句に指定してクエリを実行
// このクエリは特定のレコードをピンポイントで取得するのに適している
List<Website_Access_Log__b> logs = [
    SELECT Account_Id__c, Accessed_At__c, URL__c, User_Agent__c
    FROM Website_Access_Log__b
    WHERE Account_Id__c = :targetAccountId
    AND Accessed_At__c = :targetDateTime
];

for(Website_Access_Log__b log : logs){
    System.debug('Found Log: ' + log.URL__c);
}

4. Async SOQLによる集計クエリ

特定の期間のログを全て取得したり、集計したりする場合、通常のSOQLでは対応できません。このような大規模なデータセットに対するクエリには Async SOQL (非同期SOQL) を使用します。Async SOQLはバックグラウンドで実行され、結果を別のSalesforceオブジェクト(標準またはカスタム)に格納します。

⚠️ 未找到官方文档支持 (Async SOQLの具体的なApex実行コードは、公式ドキュメントではREST API経由での実行が主として紹介されています。以下のApexコードは、その概念を示すための表現であり、`BigObjectAsyncUtils` のようなクラスは公式には存在しません。実際には、REST APIをコールアウトするApexクラスを実装する必要があります。)

// ターゲットオブジェクトを定義 (クエリ結果を格納するカスタムオブジェクト)
String targetObject = 'Async_SOQL_Result__c';
// ターゲットオブジェクトの項目とBig Objectの項目をマッピング
Map<String, String> fieldMapping = new Map<String, String>{
    'Source_Account_Id__c' => 'Account_Id__c',
    'Access_URL__c' => 'URL__c',
    'Access_Count__c' => 'COUNT(Id)' // Async SOQLでは集計関数が利用可能
};

// Async SOQLクエリを構築
String asyncQuery = 'SELECT Account_Id__c, URL__c, COUNT(Id) ' +
                    'FROM Website_Access_Log__b ' +
                    'WHERE Accessed_At__c >= LAST_N_DAYS:30 ' +
                    'GROUP BY Account_Id__c, URL__c';

// 実際にはREST API経由でジョブを起動する
// 以下は概念コード
// String jobId = BigObjectAsyncUtils.runQuery(targetObject, fieldMapping, asyncQuery);
// System.debug('Async SOQL Job Started. Job ID: ' + jobId);

Async SOQLの利用には、REST APIの知識が不可欠です。アーキテクトは、この非同期処理パターンをシステム連携やデータ分析パイプラインに組み込む設計を行う必要があります。


注意事項

Big Objectsを本番環境で活用する上で、アーキテクトが特に注意すべき点を以下にまとめます。

  • インデックスの不変性: Big Objectに一度でもレコードを挿入すると、そのインデックスの定義(構成項目、順序)は変更できなくなります。設計段階でクエリパターンを徹底的に洗い出し、最適なインデックスを定義することが極めて重要です。
  • 権限設定: プロファイルまたは権限セットで、対象のBig Objectに対する「参照」および「作成」権限をユーザーに付与する必要があります。項目レベルセキュリティも同様に設定可能です。
  • APIとガバナ制限: データ挿入には、`Database.insertImmediate` の他に Bulk API 2.0 が利用できます。大量のデータをロードする場合はBulk APIが推奨されます。Async SOQLにも、1日に実行できるクエリ数などの制限が存在します。
  • データ削除: `Database.deleteImmediate()` を使用してApexからレコードを削除できますが、これもインデックスを完全に指定する必要があります。範囲を指定した一括削除は難しく、データ保持期間を考慮したパーティショニング戦略(例:月ごとにBig Objectを分けるなど)を検討する価値があります。
  • エラーハンドリング: `Database.insertImmediate` は部分的な成功が可能です。戻り値の `SaveResult` をループ処理し、失敗したレコードに対するリトライロジックやエラー通知の仕組みを堅牢に実装する必要があります。

まとめとベストプラクティス

Big Objectsは、Salesforce Platformのデータ管理能力を飛躍的に向上させる強力なツールですが、その特性は標準/カスタムオブジェクトとは大きく異なります。アーキテクトとして成功に導くためのベストプラクティスは以下の通りです。

  1. 目的の明確化: Big Objectsは「Write-Once, Read-Infrequently」(一度書き込んだら、更新はせず、たまに参照する)性質のデータに最適です。頻繁に更新されたり、複雑なリレーションを必要としたりするデータには向きません。適切なユースケースを見極めることが第一歩です。
  2. インデックスこそが全て: データモデルを設計する際、まず「どのようにこのデータをクエリするか?」を自問してください。その答えが、インデックスの設計そのものになります。将来の利用パターンを予測し、拡張性のあるインデックスを設計します。
  3. データライフサイクル管理: データの挿入、参照、アーカイブ、そして最終的な削除までのライフサイクル全体を計画します。特に、UIがないことを前提としたデータアクセス方法(カスタムコンポーネント、API連携、BIツール連携など)と、削除戦略を早期に固めることが重要です。
  4. 適切なツール選択: データの可視化や分析には、Tableau や Tableau CRM (旧 Einstein Analytics) がBig Objectsとネイティブに連携できるため、強力な選択肢となります。大量データロードにはBulk API 2.0、複雑な集計にはAsync SOQL(をコールする外部システムやApex)といったように、用途に応じて最適なツールを選択します。
  5. プロトタイピングと負荷試験: 本番導入前に、想定されるデータ量とクエリパターンでプロトタイプを作成し、パフォーマンスを検証します。特に、インデックス設計がクエリパフォーマンスに与える影響を実測することは、手戻りを防ぐ上で不可欠です。

Salesforce Big Objectsをマスターすることは、データ集約型アプリケーションの構築や、エンタープライズ全体のデータ戦略を策定する上で、アーキテクトにとって必須のスキルです。その制約を正しく理解し、特性を最大限に活かすことで、Salesforceを単なるCRMから、真のデータプラットフォームへと昇華させることができるでしょう。

コメント