データの永続性 – フラット ファイルだけではうまくいかない理由

プログラマーはデータ ストレージとしてフラット ファイルをよく利用しますが、その理由はすばやく簡単に設定できるためです。しかし、Zen のようなデータベースを使用することも簡単で、それにより、コードやデータベース管理タスクが大幅に簡素化されます。また、Zen は、IoT エッジ コンピューティングの展開で遭遇する可能性があるような、複数レベルの分散データ環境でのデータの永続化を容易にします。

このチュートリアルでは、データの永続性、フラット ファイルの使用で発生する問題について、および Zen を使用して、フラット ファイルでは行えない方法でデータをさまざまなレベルで永続化させる方法について説明します。

右側の「このページの内容」では当ページで扱うトピックを示しています。

データの永続性とデータ アクセス

データの永続性とは何でしょうか? データの永続性とは、データを作成したプログラムの外部でデータを保持することです。保持されたデータは、他のプロセスで読み取ったり、他のシステムに送信したりすることができます。システムが操作を続行して新しいデータを生成するにつれ、その情報は意思決定や他のプロセスの推進のために分析される場合があります。データは短期保管に保持されてから、長期保管用またはログ記録用の中央データ ウェアハウスに統合される必要がある場合があります。

IoT のシナリオでは、一部の分析および意思決定は、データが生成される場所の近くで発生する必要があります。患者を監視する医療機器を想像してください。機器は、患者から刻一刻と得られる測定値に基づいて、直ちに意思決定する必要がある場合があります。たとえば、患者の体温が安全範囲より高くなった場合、近くのスタッフに警告音を鳴らす必要があるかもしれません。

長期にわたって生成されたデータは、メモリに保持できる以上のものになる場合があるため、監視する患者の位置から迅速に取得できる近くの保存場所に書き出す必要があります。機器が動作し続けると、それに格納されるデータの量は増加します。より大きなデータ コレクションを操作することは、以前は問題にならなかったかもしれない新たな課題をもたらします。

複数のデバイスからの測定値は、それらをより分析または累算することができ、格納用またはその他の処理用のクラウドに転送する準備ができるゲートウェイによって収集することができます。

データは無限の速度で移動しません。ネットワーク上を移動するデータ パケットのせいで時間がかかります。意思決定するためにリモートの場所にデータを送信することは、帯域幅およびレイテンシへの要求を高める結果になります。イベントに対応して直ちに操作を実行する必要があるときは、エッジの近くにコンピューティング能力を置くことで、これらの要求を減少させます。

また、データが生成される場所にコンピューティング能力を配置することは、信頼性も向上させます。コンピューティングが中央に位置するソリューションでは、接続の切断により、リモートの場所での操作が中断される可能性があります。処理能力がそれを必要とする場所の近くにある場合は、接続に関係なく操作を続行できます。

また、エッジ デバイスはさまざまなオペレーティング システムで実行することができ、一様でないオペレーティング システム固有の形式でデータを共有したり、さまざまなプロトコルを呼び出したりすることができます。このような異なる環境からの情報の収集および共有は、複雑になる可能性があります。たとえば、他のデバイスから読み書きできる共有ファイル システムが必要になるかもしれません。これにより、データ共有の実装に複雑さと障害点が追加されます。つまり、クライアントがファイルの全体または一部にアクセスできないことが検出されたときは、別のクライアントがそのファイルで作業中である可能性があります。さらに、クライアントがファイルを操作していて、そのクライアントが失敗した場合や、故障によって通信が中断し、ファイルの一部しか更新されなかった場合には、データが破損する可能性があります。

フラット ファイルの使用の明白な簡単さは、結局、オペレーティング システムやデータ統合問題の相違を考慮に入れると、著しく複雑になることがわかります。

フラット ファイルでのデータの永続化

フラット ファイルは一般的に、ローカルでのデータのキャプチャや、あるシステムから別のシステムへのデータの移動に使用されます。フラット ファイルを読み取るには、転送されるデータ型を知っている必要があります。これは、ファイル内のエンコードから目的のデータ型を推測できない可能性があるためです。次の例では、構造体はデバイスのセンサーからの値を保持します。測定値はローカルのフラット ファイルに書き込まれます。

struct Vital {
    int ID;
    int bmp;
    float temperature;
    int respitoryRate;
    int oxygenLevel;
    float systolicPressure;
    float diastolicPressure;
};

void saveReadings(std::vector<Vital > readings)
{
    ofstream exportFile("sensorReadings.csv");
    for (auto current = readings.begin(); current != readings.end(); ++current)
    {
        exportFile << current->time
            << ", " << current->sensorID
            << ", " << current->temperature
            << ", " << current->systolic
            << ", " << current->diastolic
            << ", " << current->oxygen<< endl;
    }
    exportFile.close();
}

しかし、IoT デバイスの記憶域に制限がある場合、つまり、別のデバイスと置き換えたりデータを共有したりする必要がある場合にはどうなるでしょうか? IoT デバイスがゲートウェイにデータをプッシュしたり、ゲートウェイがデバイスをポーリングしてデータを読み取ったりすることができます。いずれの場合も、コンピューターは別のコンピューターからデータを読み取る必要があります。

プログラムがリモート ファイルにアクセスする方法は、オペレーティング システムによって異なります。両方のコンピューターが Windows を実行している場合は、コンピューター名または IP アドレスとファイル共有の名前を含むパスを構成することでファイルを読み取ることができます。ファイル共有を有効にして共有を設定するには、最初にコンピューターの構成が必要になる場合があります。

std::wstring machineName = L"192.168.1.23";
std::wstring folderName = L"sharedFiles";
std::wstring fileName = L"\\\\" + machineName + L"\\" + folderName +
    L"\\sensorReadings.csv";
//create an input file stream from this file name
wifstream sourceFile(fileName);

両方のコンピューターが Linux を実行している場合、ファイル システムの一部をネットワーク経由で利用できるようにするには、ネットワーク ファイル システム(NFS)や Samba などの追加のソフトウェアを構成するか、またはそのファイル システムを、共有ファイルにアクセスする必要のあるデバイスのファイル システムにマウントします。マウントされたファイル システムのパスも構成の決定によって異なります。これらすべてのソリューションはプラットフォームに依存します。混合プラットフォームの環境は、複雑な組み合わせのソリューションになる可能性があります。

デバイスが相互作用するとき、複数のデバイスまたはプロセスがファイルを読み取り、同時にファイルに書き込む場合があります。読み取りと書き込みが重なった場合、プロセスは、以前のバージョンのファイルと、ファイルの読み取り中に行われた更新が混合された一貫性のないデータを取得する可能性があります。ファイルに書き込んでも、データが実際に記憶域に保持されていることは保証されません。ファイルのデータの書き込みは、記憶域にコミットされる前にファイル キャッシュ内に存在している場合があります。

格納されたデータの量が増えると、他の課題が発生する可能性があります。  データ内の一連のレコードを見つけるには、すべてのレコードを読み取り、それらを一度に 1 つずつチェックする必要があるため、レコード数が多くなると処理が遅くなります。ネットワーク上の別の場所からファイルを読み取る場合は、さらに遅くなる可能性があります。

また、オペレーティング システムにはファイル サイズの制限があります。大量のデータの場合、アプリケーションはデータを複数のファイルに分割する必要があるかもしれません。突然、単一のフラット ファイルで作業する単純さが失われました。必要な一連のレコードを見つけるには、データの並べ替えも必要になります。最適化された並べ替えアルゴリズムを使用しても、大量のデータ セットの並べ替えは時間がかかる可能性があります。

幸いにも、より良い方法があります。

Zen によるデータの永続化

Zen 製品は、複数のプラットフォームにわたるデータの取得を単純化します。ソフトウェアはさまざまなプラットフォームおよびプログラミング言語と互換性があるため、対象とするプラットフォームに関係なく、同様の一連のデータ管理上の利点を確保するソリューションを得られます。

Zen SDK を使用すると、プログラムは、同じデバイスで実行されている Zen データのインスタンスや、同じネットワーク上の別のデバイスのインスタンスに一貫した方法で接続することができます。複数のユーザーのアクセス問題およびデータ整合性の問題はすべて Zen が処理するため、これらの処理のためにアプリケーションが複雑になることについて心配する必要はありません。

環境にデバイスが追加されるにつれ、それらのいずれかで障害や中断が発生する可能性が高まります。このような障害によるデータの破損および不整合は、フラット ファイルで検討する必要のある潜在的な問題です。

Zen は、データ トランザクションの 4 つの基本原則 ACID(Atomicity:原子性、Consistency:一貫性、Isolation:独立性、Durability:永続性)に準拠するように設計されています。このすべてを Zen が自動的に処理するため、アプリケーションはデータ収集と分析タスクに集中することができます。

Zen データ ファイルはデータを表現する方法が共通しているため、データをある形式から別の形式に変換する ETL オペレーションのオーバーヘッドなしに、あるシステムから別のシステムに移動することができます。データの送信元が小さな IoT デバイスであろうと強力なサーバーであろうとデータの形式は同じです。つまり、Zen はサポートするすべてのプラットフォームで同じデータ形式を使用します。また、Zen は以前のバージョンとの互換性を保持しているため、データの移行を必要とせずに Zen ソフトウェアをアップグレードすることができます。

64 テラバイトまでのデータ セットを管理する Zen の機能により、データをファイル システムに書き込む方法を整理する必要性から解放されます。ネイティブのファイル パスがサポートされる一方、リモート システムで開くデータ ファイルの場所を指定する追加の方法があります。このファイル識別子は次のようになります。

btrv://host/?file=c:/data/Vitals.mkd

さまざまなオペレーティング システムを実行しているデバイスが、ファイルに対して同じ識別子を使用することができます。Zen は、異なるオペレーティング システムからでもファイルにアクセスできるよう対処しています。次のコードは、別のコンピューター上の Zen データ ファイルを開きます。

BtrieveFile btrieveFile;
const FILE_NAME = " btrv://User@VitalServer/?file=c:/data/Vitals.mkd&pwd=P@ssw0rd "; Btrieve::StatusCode status = btrieveClient.FileOpen(&btrieveFile, FILE_NAME, "My0wner!", Btrieve::OPEN_MODE_NORMAL);

レコードをファイルに書き込むのに必要となるのは、1 つの呼び出しだけです。

Btrieve::StatusCode status = btrieveFile.RecordCreate((char*)&vital, sizeof(vital));

成功を示すステータス コードが返されたら、レコードは記憶域にコミットされました。レコードは次のように読み取られます。

void readAllRecords(BtrieveFile& btrieveFile,vector<Vital>& vitalList )
{
    // Error handling removed for clarity

    Btrieve::StatusCode status;
    Vital vital;
    int bytesRead = btrieveFile.RecordRetrieveFirst(Btrieve::INDEX_NONE,
        (char*)&vital, sizeof(Vital));
    status = btrieveFile.GetLastStatusCode();
    while (status == Btrieve::STATUS_CODE_NO_ERROR )
    {
        vitalList.push_back(event);
        btrieveFile.RecordRetrieveNext((char*)&vital, sizeof(Vital));
        status = btrieveFile.GetLastStatusCode();
    }
}

データ ストレージに Zen を使用する強みの 1 つは、データにインデックスを付けられることです。インデックスを使用すると、レコードの並べ替えと検索がフラット ファイルの場合よりはるかに簡単になります。レコードを格納されている順序で一度に 1 つずつ読み取るほかに、Zen データは、レコード内の 1 つのフィールドに基づく並べ替え順序で読み取ることもできます。

フラット ファイルでこれを行うには、開発者はすべてのレコードを読み取って並べ替えを実行する必要があります。Zen では、並べ替えに使用するフィールドを使ってインデックスを作成します。

最も高い体温を示したバイタル測定値に注目したいとしましょう。次のコードは、体温が最も高いレコードから最も低いレコードの順に読み取るためのインデックスを作成します。

void createTemperatureIndex(BtrieveFile& btrieveFile, Btrieve::Index indexNumber)
{
    BtrieveIndexAttributes temperatureIndexAttributes;
    BtrieveKeySegment temperatureKeySegment; 
    temperatureKeySegment.SetField( offsetof(Vital, temperature),
        4, Btrieve::DATA_TYPE_INTEGER );
    temperatureKeySegment.SetDescendingSortOrder(true);
    temperatureIndexAttributes.AddKeySegment(&temperatureKeySegment);
    btrieveFile.IndexCreate(&temperatureIndexAttributes);  
}
//Creating the index and assign it to the identifier Index_9
createTemperatureIndex(myFile, Btrieve::Index_9);

以前のコード例では、Index パラメーターで Btrieve::INDEX_NONE を渡して、Btrieve ファイルをインデックスなしで読み取りました。代わりに、体温インデックスの作成で割り当てた識別子を使用すると、体温順でレコードを読み取ることが可能になります。

btrieveFile.RecordRetrieveFirst(Btrieve::Index_9, (char*)&vital, sizeof(Vital));

これははるかに高速で、ファイル全体を読み取って並べ替えるより少ないリソースで済みます。

まとめ

マルチプラットフォーム環境で信頼性の高いデータ永続性を確保することは、重要な問題です。フラット ファイルは簡単なソリューションのように思われますが、複数のプロセスやデバイスでファイルを共有する必要がある環境で発生する可能性のある問題に対処していません。対照的に、Zen データベースは、データを共有および保存するための信頼性が高く、高速で、移植可能なソリューションを提供しています。

Zen が IoT 環境でのデータ管理にどのように役立つかについては、Actian Zen ページをご覧ください。

Zen 製品の評価版は、Actian Zen v15 評価版ページから入手できます。