時系列データベース丨DolphinDBストリームコンピューティングエンジンがセンサーデータの異常検出を実現する方法

DolphinDBは、モノのインターネットでのセンサーデータの異常検出を含む、リアルタイムデータ処理用のストリームテーブルとストリームコンピューティングエンジンを提供します。組み込みの異常検出エンジンは、ほとんどの異常検出シナリオのニーズを満たすことができます。異常検出ロジックが複雑で特殊であり、標準化された異常検出エンジンが要件を満たせない場合、ユーザーはカスタムメッセージ処理機能を使用してそれを実装できます。


1.アプリケーション要件

監視システムは毎秒データを収集します。次の2つの異常検出要件があります。

  • 3分ごとに、センサーの温度が摂氏40度を2回超え、摂氏30度を3回超えると、システムはアラームを発します。
  • センサーネットワークが切断され、5分以内にデータがない場合、システムはアラームを発します。

上記のアラームは、異常が検出された場合、レコードがストリームデータテーブルに書き込まれることを意味します。


2.デザインのアイデア

分散時系列データベースDolphinDBのフローコンピューティングフレームワークは、現在、時系列集計エンジン断面集計エンジン異常検出エンジン、およびカスタムフローコンピューティングエンジンをサポートしてます

  • 時系列アグリゲーター:デバイスステータスの垂直集計計算(時系列で集計)を実行したり、複数のデバイス状態を水平方向に集計してから時間に従って集計したりできます。時系列集計は、スライディングウィンドウを使用したスト​​リーミング計算をサポートします。DolphinDBは、組み込みのウ​​ィンドウ集約機能のパフォーマンスを最適化し、シングルコアCPUは、1秒あたり約100万状態のタイミング集約を完了することができます。
  • Cross Sectional Aggregator(Cross Sectional Aggregator):スナップショットエンジンの拡張機能であり、デバイスのバッチの平均温度の計算など、デバイスのステータスに対して水平方向の集計計算を実行できます。
  • 異常検出エンジン:データがユーザー定義のアラームインジケータを満たしているかどうかをリアルタイムで検出できます。異常データが見つかった場合は、リアルタイムの監視とインターネットの早期警告のニーズを満たすためにテーブルに出力されます。物事。
  • カスタムストリームコンピューティングエンジン:上記の3つのエンジンのいずれも需要を満たすことができない場合、ユーザーはDolphinDBスクリプトまたはAPI言語を使用してメッセージ処理機能をカスタマイズすることもできます。

最初の要件では、センサーの温度が3分以内に異常になるとシステムがアラームを発し、異常検出エンジンが最適です。DolphinDBスクリプトを使用して式を記述し、例外ロジックを記述します。ただし、2番目の要件は適用されません。異常検出エンジンは、デバイスのグループ化によって処理されます。計算は、新しいデータが流入するたびにトリガーされます。または、集計計算は、一定の間隔で固定長の移動ウィンドウでのみ実行されます。センサーが新しいデータを生成しない場合、計算をトリガーすることはできません。解決策は、メッセージハンドラーをカスタマイズして、計算および検出することです。具体的な実現のアイデアは次のとおりです。Key-Valueメモリテーブルを使用して、各センサーの最新の取得時間を記録します。メッセージは、特定の時間間隔(たとえば、1秒)でメッセージ処理機能に入ります。メッセージ処理機能は、最初にKey-Valueメモリテーブルを更新し、次にこのテーブルの各デバイスによって記録された最新の収集時間が5分を超えているかどうかを確認し、超えている場合はアラームを発します。


3.詳細な実装手順


3.1入力および出力ストリームデータテーブルを定義します

まず、リアルタイムで収集されたセンサーデータを受信するストリーミングデータテーブルを定義し、enableTableShareAndPersistence関数を使用して、ストリーミングデータテーブルをハードディスクに共有および永続化します。cacheSizeパラメーターは、メモリーに保持されるデータの最大量を100万行に制限します。センサーデバイスには多くのインジケーターがありますが、この例には温度インジケーターのみが含まれているため、この例ではテーブル構造が簡略化されています。テーブル構造には、センサー番号deviceID、時間ts、および温度の3つの列のみが含まれます。コードは次のように表示されます。

st = streamTable(1000000:0、 `deviceID`ts`temperature、[INT、DATETIME、FLOAT])
enableTableShareAndPersistence(table = st、tableName =` sensor、asynWrite = false、compress = true、cacheSize = 1000000)

其次定义报警输出流数据表用于异常检测引擎的输出。按照DolphinDB用户手册中对创建异常检测引擎函数createAnomalyDetectionEngine各参数的说明,异常引擎对输出表的格式有严格要求,即它的第一列必须是时间类型,用于存放检测到异常的时间戳,并且该列的数据类型需与输入表的时间列一致。如果keyColumn(分组列)参数不为空,那么第二列为keyColumn,在本例中,分组列为传感器编号deviceID。之后的两列分别为int类型和string/symbol类型,用于记录异常的类型(在metrics中的下标)和异常的内容。建表代码如下:

share streamTable(1000:0, `time`deviceID`anomalyType`anomalyString, [DATETIME,INT,INT, SYMBOL]) as warningTable

3.2 创建异常检测引擎,实现传感器温度异常报警的功能

异常检测引擎中,设置异常指标为sum(temperature > 40) > 2 && sum(temperature > 30) > 3 ,分组列(keyColumn)为传感器编号deviceID,数据窗口windowSize为180秒,计算的时间间隔step为30秒。这些参数如何设置可参考异常检测引擎。代码如下:

engine = createAnomalyDetectionEngine(name="engine1", metrics=<[sum(temperature > 40) > 2 && sum(temperature > 30) > 3  ]>,dummyTable=sensor, outputTable=warningTable, timeColumn=`ts, keyColumn=`deviceID, windowSize = 180, step = 30)
subscribeTable(tableName="sensor", actionName="sensorAnomalyDetection", offset=0, handler= append!{engine}, msgAsTable=true)

3.3 创建自定义消息处理函数,实现传感器离线报警的功能

第二个需求,需要保存每个传感器的最新数据采集时间,用于判断是否已有5分钟未采集数据。本例采用键值内存表保存每个设备的最新状态,并以传感器编号deviceID作为主键。键值表中,基于键值的查找和更新具有非常高的效率。收到传感器数据时,用append!函数更新键值表中的记录。如果新记录中的主键值不存在于表中,那么往表中添加新的记录;如果新记录的主键值与已有记录的主键值重复时,会更新表中该主键值对应的记录。

在输出异常信息到报警输出流数据表时,异常的类型anomalyType因为上节异常检测引擎已用0,所以这里设为1。异常的内容设为空。

配置函数subscribeTable的参数throttle和batchSize,可以达到批量处理消息提升性能的目的。参数throttle决定handler间隔多久时间处理一次消息,本例中设定为每秒处理一次。这里要注意当消息的数量达到batchSize时,即便间隔时间没到也会处理进来的消息,所以需要将batchSize设置为一个比较大的数。示例代码如下,其中传感器数deviceNum假设为3:

t=keyedTable(`deviceID,100:0,`deviceID`time,[INT,DATETIME])
deviceNum=3
insert into t values(1..deviceNum,take(now().datetime(),deviceNum))
def checkNoData (mutable keyedTable, mutable outputTable, msg) {
	keyedTable.append!(select deviceID, ts from msg)
	warning = select now().datetime(), deviceID, 1 as anomalyType, "" as anomalyString from keyedTable where time < datetimeAdd(now().datetime(), -5, "m")
	if(warning.size() > 0) outputTable.append!(warning)
}
subscribeTable(tableName="sensor", actionName="noData", offset=0,handler=checkNoData{t, warningTable}, msgAsTable=true, batchSize=1000000, throttle=1)


4. 模拟写入与验证

假设3个传感器,一秒钟采集一次数据,前一分钟所有设备都有数据,1分钟后第3个设备无数据。示例代码如下:

def writeData(){
	deviceNum = 3
	for (i in 0:60) {
		data = table(take(1..deviceNum, deviceNum) as deviceID, take(now().datetime(), deviceNum) as ts, rand(10..41, deviceNum) as temperature)
		sensor.append!(data)
		sleep(1000)
	}
	deviceNum = 2
	for (i in 0:600) {
		data = table(take(1..deviceNum, deviceNum) as deviceID ,take(now().datetime(), deviceNum) as ts, rand(10..45,deviceNum) as temperature)
		sensor.append!(data)
		sleep(1000)
	}	
}
submitJob("simulateData", "simulate sensor data", writeData)

运行后,查询报警输出表warningTable,可看到结果示例如下:

47c4f8756c9257c7c933403c4f000cc0.png


附录

测试代码


おすすめ

転載: blog.51cto.com/15022783/2675837