六月婷婷AV,国产偷窥猎奇福利二区,日韩三级片。,好吊色网站,日韩成人中文在线视频,国产亚洲午夜啪啪,亚洲欧美另类国产精品,国产成人av1,任你艹在线观看

高級(jí)功能

連續(xù)查詢(xún)(Continuous Query)

連續(xù)查詢(xún)是 TDengine 定期自動(dòng)執(zhí)行的查詢(xún),采用滑動(dòng)窗口的方式進(jìn)行計(jì)算,是一種簡(jiǎn)化的時(shí)間驅(qū)動(dòng)的流式計(jì)算。針對(duì)庫(kù)中的表或超級(jí)表,TDengine 可提供定期自動(dòng)執(zhí)行的連續(xù)查詢(xún),用戶(hù)可讓 TDengine 推送查詢(xún)的結(jié)果,也可以將結(jié)果再寫(xiě)回到 TDengine 中。每次執(zhí)行的查詢(xún)是一個(gè)時(shí)間窗口,時(shí)間窗口隨著時(shí)間流動(dòng)向前滑動(dòng)。在定義連續(xù)查詢(xún)的時(shí)候需要指定時(shí)間窗口(time window, 參數(shù)interval)大小和每次前向增量時(shí)間(forward sliding times, 參數(shù)sliding)。

TDengine 的連續(xù)查詢(xún)采用時(shí)間驅(qū)動(dòng)模式,可以直接使用 TAOS SQL 進(jìn)行定義,不需要額外的操作。使用連續(xù)查詢(xún),可以方便快捷地按照時(shí)間窗口生成結(jié)果,從而對(duì)原始采集數(shù)據(jù)進(jìn)行降采樣(down sampling)。用戶(hù)通過(guò) TAOS SQL 定義連續(xù)查詢(xún)以后,TDengine 自動(dòng)在最后的一個(gè)完整的時(shí)間周期末端拉起查詢(xún),并將計(jì)算獲得的結(jié)果推送給用戶(hù)或者寫(xiě)回 TDengine。

TDengine 提供的連續(xù)查詢(xún)與普通流計(jì)算中的時(shí)間窗口計(jì)算具有以下區(qū)別:

  • 不同于流計(jì)算的實(shí)時(shí)反饋計(jì)算結(jié)果,連續(xù)查詢(xún)只在時(shí)間窗口關(guān)閉以后才開(kāi)始計(jì)算。例如時(shí)間周期是 1 天,那么當(dāng)天的結(jié)果只會(huì)在 23:59:59 以后才會(huì)生成。
  • 如果有歷史記錄寫(xiě)入到已經(jīng)計(jì)算完成的時(shí)間區(qū)間,連續(xù)查詢(xún)并不會(huì)重新進(jìn)行計(jì)算,也不會(huì)重新將結(jié)果推送給用戶(hù)。對(duì)于寫(xiě)回 TDengine 的模式,也不會(huì)更新已經(jīng)存在的計(jì)算結(jié)果。
  • 使用連續(xù)查詢(xún)推送結(jié)果的模式,服務(wù)端并不緩存客戶(hù)端計(jì)算狀態(tài),也不提供 Exactly-Once 的語(yǔ)意保證。如果用戶(hù)的應(yīng)用端崩潰,再次拉起的連續(xù)查詢(xún)將只會(huì)從再次拉起的時(shí)間開(kāi)始重新計(jì)算最近的一個(gè)完整的時(shí)間窗口。如果使用寫(xiě)回模式,TDengine 可確保數(shù)據(jù)寫(xiě)回的有效性和連續(xù)性。

使用連續(xù)查詢(xún)

下面以智能電表場(chǎng)景為例介紹連續(xù)查詢(xún)的具體使用方法。假設(shè)我們通過(guò)下列 SQL 語(yǔ)句創(chuàng)建了超級(jí)表和子表:

create table meters (ts timestamp, current float, voltage int, phase float) tags (location binary(64), groupId int);
create table D1001 using meters tags ("Beijing.Chaoyang", 2);
create table D1002 using meters tags ("Beijing.Haidian", 2);
...

我們已經(jīng)知道,可以通過(guò)下面這條 SQL 語(yǔ)句以一分鐘為時(shí)間窗口、30秒為前向增量統(tǒng)計(jì)這些電表的平均電壓。

select avg(voltage) from meters interval(1m) sliding(30s);

每次執(zhí)行這條語(yǔ)句,都會(huì)重新計(jì)算所有數(shù)據(jù)。 如果需要每隔 30 秒執(zhí)行一次來(lái)增量計(jì)算最近一分鐘的數(shù)據(jù),可以把上面的語(yǔ)句改進(jìn)成下面的樣子,每次使用不同的 startTime 并定期執(zhí)行:

select avg(voltage) from meters where ts > {startTime} interval(1m) sliding(30s);

這樣做沒(méi)有問(wèn)題,但 TDengine 提供了更簡(jiǎn)單的方法,只要在最初的查詢(xún)語(yǔ)句前面加上 create table {tableName} as 就可以了,例如:

create table avg_vol as select avg(voltage) from meters interval(1m) sliding(30s);

會(huì)自動(dòng)創(chuàng)建一個(gè)名為 avg_vol 的新表,然后每隔 30 秒,TDengine 會(huì)增量執(zhí)行 as 后面的 SQL 語(yǔ)句,并將查詢(xún)結(jié)果寫(xiě)入這個(gè)表中,用戶(hù)程序后續(xù)只要從 avg_vol 中查詢(xún)數(shù)據(jù)即可。例如:

taos> select * from avg_vol;
            ts           |        avg_voltage_    |
===================================================
 2020-07-29 13:37:30.000 |            222.0000000 |
 2020-07-29 13:38:00.000 |            221.3500000 |
 2020-07-29 13:38:30.000 |            220.1700000 |
 2020-07-29 13:39:00.000 |            223.0800000 |

需要注意,查詢(xún)時(shí)間窗口的最小值是10毫秒,沒(méi)有時(shí)間窗口范圍的上限。

此外,TDengine 還支持用戶(hù)指定連續(xù)查詢(xún)的起止時(shí)間。如果不輸入開(kāi)始時(shí)間,連續(xù)查詢(xún)將從第一條原始數(shù)據(jù)所在的時(shí)間窗口開(kāi)始;如果沒(méi)有輸入結(jié)束時(shí)間,連續(xù)查詢(xún)將永久運(yùn)行;如果用戶(hù)指定了結(jié)束時(shí)間,連續(xù)查詢(xún)?cè)谙到y(tǒng)時(shí)間達(dá)到指定的時(shí)間以后停止運(yùn)行。比如使用下面的SQL創(chuàng)建的連續(xù)查詢(xún)將運(yùn)行一小時(shí),之后會(huì)自動(dòng)停止。

create table avg_vol as select avg(voltage) from meters where ts > now and ts <= now + 1h interval(1m) sliding(30s);

需要說(shuō)明的是,上面例子中的 now 是指創(chuàng)建連續(xù)查詢(xún)的時(shí)間,而不是查詢(xún)執(zhí)行的時(shí)間,否則,查詢(xún)就無(wú)法自動(dòng)停止了。另外,為了盡量避免原始數(shù)據(jù)延遲寫(xiě)入導(dǎo)致的問(wèn)題,TDengine 中連續(xù)查詢(xún)的計(jì)算有一定的延遲。也就是說(shuō),一個(gè)時(shí)間窗口過(guò)去后,TDengine 并不會(huì)立即計(jì)算這個(gè)窗口的數(shù)據(jù),所以要稍等一會(huì)(一般不會(huì)超過(guò) 1 分鐘)才能查到計(jì)算結(jié)果。

管理連續(xù)查詢(xún)

用戶(hù)可在控制臺(tái)中通過(guò) show streams 命令來(lái)查看系統(tǒng)中全部運(yùn)行的連續(xù)查詢(xún),并可以通過(guò) kill stream 命令殺掉對(duì)應(yīng)的連續(xù)查詢(xún)。后續(xù)版本會(huì)提供更細(xì)粒度和便捷的連續(xù)查詢(xún)管理命令。

數(shù)據(jù)訂閱(Publisher/Subscriber)

基于數(shù)據(jù)天然的時(shí)間序列特性,TDengine 的數(shù)據(jù)寫(xiě)入(insert)與消息系統(tǒng)的數(shù)據(jù)發(fā)布(pub)邏輯上一致,均可視為系統(tǒng)中插入一條帶時(shí)間戳的新記錄。同時(shí),TDengine 在內(nèi)部嚴(yán)格按照數(shù)據(jù)時(shí)間序列單調(diào)遞增的方式保存數(shù)據(jù)。本質(zhì)上來(lái)說(shuō),TDengine 中里每一張表均可視為一個(gè)標(biāo)準(zhǔn)的消息隊(duì)列。

TDengine 內(nèi)嵌支持輕量級(jí)的消息訂閱與推送服務(wù)。使用系統(tǒng)提供的 API,用戶(hù)可使用普通查詢(xún)語(yǔ)句訂閱數(shù)據(jù)庫(kù)中的一張或多張表。訂閱的邏輯和操作狀態(tài)的維護(hù)均是由客戶(hù)端完成,客戶(hù)端定時(shí)輪詢(xún)服務(wù)器是否有新的記錄到達(dá),有新的記錄到達(dá)就會(huì)將結(jié)果反饋到客戶(hù)。

TDengine 的訂閱與推送服務(wù)的狀態(tài)是客戶(hù)端維持,TDengine 服務(wù)器并不維持。因此如果應(yīng)用重啟,從哪個(gè)時(shí)間點(diǎn)開(kāi)始獲取最新數(shù)據(jù),由應(yīng)用決定。

TDengine 的 API 中,與訂閱相關(guān)的主要有以下三個(gè):

taos_subscribe
taos_consume
taos_unsubscribe

這些API的文檔請(qǐng)見(jiàn) C/C++ Connector,下面仍以智能電表場(chǎng)景為例介紹一下它們的具體用法(超級(jí)表和子表結(jié)構(gòu)請(qǐng)參考上一節(jié)“連續(xù)查詢(xún)”),完整的示例代碼可以在 這里 找到。

如果我們希望當(dāng)某個(gè)電表的電流超過(guò)一定限制(比如 10A)后能得到通知并進(jìn)行一些處理, 有兩種方法:一是分別對(duì)每張子表進(jìn)行查詢(xún),每次查詢(xún)后記錄最后一條數(shù)據(jù)的時(shí)間戳,后續(xù)只查詢(xún)這個(gè)時(shí)間戳之后的數(shù)據(jù):

select * from D1001 where ts > {last_timestamp1} and current > 10;
select * from D1002 where ts > {last_timestamp2} and current > 10;
...

這確實(shí)可行,但隨著電表數(shù)量的增加,查詢(xún)數(shù)量也會(huì)增加,客戶(hù)端和服務(wù)端的性能都會(huì)受到影響,當(dāng)電表數(shù)增長(zhǎng)到一定的程度,系統(tǒng)就無(wú)法承受了。

另一種方法是對(duì)超級(jí)表進(jìn)行查詢(xún)。這樣,無(wú)論有多少電表,都只需一次查詢(xún):

select * from meters where ts > {last_timestamp} and current > 10;

但是,如何選擇 last_timestamp 就成了一個(gè)新的問(wèn)題。因?yàn)?,一方面?shù)據(jù)的產(chǎn)生時(shí)間(也就是數(shù)據(jù)時(shí)間戳)和數(shù)據(jù)入庫(kù)的時(shí)間一般并不相同,有時(shí)偏差還很大;另一方面,不同電表的數(shù)據(jù)到達(dá) TDengine 的時(shí)間也會(huì)有差異。所以,如果我們?cè)诓樵?xún)中使用最慢的那臺(tái)電表的數(shù)據(jù)的時(shí)間戳作為 last_timestamp,就可能重復(fù)讀入其它電表的數(shù)據(jù);如果使用最快的電表的時(shí)間戳,其它電表的數(shù)據(jù)就可能被漏掉。

TDengine 的訂閱功能為上面這個(gè)問(wèn)題提供了一個(gè)徹底的解決方案。

首先是使用 taos_subscribe 創(chuàng)建訂閱:

TAOS_SUB* tsub = NULL;
if (async) {
  // create an asynchronized subscription, the callback function will be called every 1s
  tsub = taos_subscribe(taos, restart, topic, sql, subscribe_callback, &blockFetch, 1000);
} else {
  // create an synchronized subscription, need to call 'taos_consume' manually
  tsub = taos_subscribe(taos, restart, topic, sql, NULL, NULL, 0);
}

TDengine 中的訂閱既可以是同步的,也可以是異步的,上面的代碼會(huì)根據(jù)從命令行獲取的參數(shù) async 的值來(lái)決定使用哪種方式。這里,同步的意思是用戶(hù)程序要直接調(diào)用 taos_consume 來(lái)拉取數(shù)據(jù),而異步則由 API 在內(nèi)部的另一個(gè)線程中調(diào)用 taos_consume,然后把拉取到的數(shù)據(jù)交給回調(diào)函數(shù) subscribe_callback去處理。(注意,subscribe_callback 中不宜做較為耗時(shí)的操作,否則有可能導(dǎo)致客戶(hù)端阻塞等不可控的問(wèn)題。)

參數(shù) taos 是一個(gè)已經(jīng)建立好的數(shù)據(jù)庫(kù)連接,在同步模式下無(wú)特殊要求。但在異步模式下,需要注意它不會(huì)被其它線程使用,否則可能導(dǎo)致不可預(yù)計(jì)的錯(cuò)誤,因?yàn)榛卣{(diào)函數(shù)在API的內(nèi)部線程中被調(diào)用,而 TDengine 的部分 API 不是線程安全的。

參數(shù) sql 是查詢(xún)語(yǔ)句,可以在其中使用where子句指定過(guò)濾條件。在我們的例子中,如果只想訂閱電流超過(guò) 10A 時(shí)的數(shù)據(jù),可以這樣寫(xiě):

select * from meters where current > 10;

注意,這里沒(méi)有指定起始時(shí)間,所以會(huì)讀到所有時(shí)間的數(shù)據(jù)。如果只想從一天前的數(shù)據(jù)開(kāi)始訂閱,而不需要更早的歷史數(shù)據(jù),可以再加上一個(gè)時(shí)間條件:

select * from meters where ts > now - 1d and current > 10;

訂閱的 topic 實(shí)際上是它的名字,因?yàn)橛嗛喒δ苁窃诳蛻?hù)端API中實(shí)現(xiàn)的,所以沒(méi)必要保證它全局唯一,但需要它在一臺(tái)客戶(hù)端機(jī)器上唯一。

如果名為 topic 的訂閱不存在,參數(shù) restart 沒(méi)有意義;但如果用戶(hù)程序創(chuàng)建這個(gè)訂閱后退出,當(dāng)它再次啟動(dòng)并重新使用這個(gè) topic 時(shí),restart 就會(huì)被用于決定是從頭開(kāi)始讀取數(shù)據(jù),還是接續(xù)上次的位置進(jìn)行讀取。本例中,如果 restarttrue(非零值),用戶(hù)程序肯定會(huì)讀到所有數(shù)據(jù)。但如果這個(gè)訂閱之前就存在了,并且已經(jīng)讀取了一部分?jǐn)?shù)據(jù),且 restartfalse0),用戶(hù)程序就不會(huì)讀到之前已經(jīng)讀取的數(shù)據(jù)了。

taos_subscribe的最后一個(gè)參數(shù)是以毫秒為單位的輪詢(xún)周期。在同步模式下,如果前后兩次調(diào)用 taos_consume 的時(shí)間間隔小于此時(shí)間,taos_consume 會(huì)阻塞,直到間隔超過(guò)此時(shí)間。異步模式下,這個(gè)時(shí)間是兩次調(diào)用回調(diào)函數(shù)的最小時(shí)間間隔。

taos_subscribe 的倒數(shù)第二個(gè)參數(shù)用于用戶(hù)程序向回調(diào)函數(shù)傳遞附加參數(shù),訂閱 API 不對(duì)其做任何處理,只原樣傳遞給回調(diào)函數(shù)。此參數(shù)在同步模式下無(wú)意義。

訂閱創(chuàng)建以后,就可以消費(fèi)其數(shù)據(jù)了,同步模式下,示例代碼是下面的 else 部分:

if (async) {
  getchar();
} else while(1) {
  TAOS_RES* res = taos_consume(tsub);
  if (res == NULL) {
    printf("failed to consume data.");
    break;
  } else {
    print_result(res, blockFetch);
    getchar();
  }
}

這里是一個(gè) while 循環(huán),用戶(hù)每按一次回車(chē)鍵就調(diào)用一次 taos_consume,而 taos_consume 的返回值是查詢(xún)到的結(jié)果集,與 taos_use_result 完全相同,例子中使用這個(gè)結(jié)果集的代碼是函數(shù) print_result

void print_result(TAOS_RES* res, int blockFetch) {
  TAOS_ROW row = NULL;
  int num_fields = taos_num_fields(res);
  TAOS_FIELD* fields = taos_fetch_fields(res);
  int nRows = 0;
  if (blockFetch) {
    nRows = taos_fetch_block(res, &row);
    for (int i = 0; i < nRows; i++) {
      char temp[256];
      taos_print_row(temp, row + i, fields, num_fields);
      puts(temp);
    }
  } else {
    while ((row = taos_fetch_row(res))) {
      char temp[256];
      taos_print_row(temp, row, fields, num_fields);
      puts(temp);
      nRows++;
    }
  }
  printf("%d rows consumed.\n", nRows);
}

其中的 taos_print_row 用于處理訂閱到數(shù)據(jù),在我們的例子中,它會(huì)打印出所有符合條件的記錄。而異步模式下,消費(fèi)訂閱到的數(shù)據(jù)則顯得更為簡(jiǎn)單:

void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) {
  print_result(res, *(int*)param);
}

當(dāng)要結(jié)束一次數(shù)據(jù)訂閱時(shí),需要調(diào)用 taos_unsubscribe

taos_unsubscribe(tsub, keep);

其第二個(gè)參數(shù),用于決定是否在客戶(hù)端保留訂閱的進(jìn)度信息。如果這個(gè)參數(shù)是false0),那無(wú)論下次調(diào)用 taos_subscribe 時(shí)的 restart 參數(shù)是什么,訂閱都只能重新開(kāi)始。另外,進(jìn)度信息的保存位置是 {DataDir}/subscribe/ 這個(gè)目錄下,每個(gè)訂閱有一個(gè)與其 topic 同名的文件,刪掉某個(gè)文件,同樣會(huì)導(dǎo)致下次創(chuàng)建其對(duì)應(yīng)的訂閱時(shí)只能重新開(kāi)始。

代碼介紹完畢,我們來(lái)看一下實(shí)際的運(yùn)行效果。假設(shè):

  • 示例代碼已經(jīng)下載到本地
  • TDengine 也已經(jīng)在同一臺(tái)機(jī)器上安裝好
  • 示例所需的數(shù)據(jù)庫(kù)、超級(jí)表、子表已經(jīng)全部創(chuàng)建好

則可以在示例代碼所在目錄執(zhí)行以下命令來(lái)編譯并啟動(dòng)示例程序:

make
./subscribe -sql='select * from meters where current > 10;'

示例程序啟動(dòng)后,打開(kāi)另一個(gè)終端窗口,啟動(dòng) TDengine 的 shell 向 D1001 插入一條電流為 12A 的數(shù)據(jù):

$ taos
> use test;
> insert into D1001 values(now, 12, 220, 1);

這時(shí),因?yàn)殡娏鞒^(guò)了 10A,您應(yīng)該可以看到示例程序?qū)⑺敵龅搅似聊簧?。您可以繼續(xù)插入一些數(shù)據(jù)觀察示例程序的輸出。

Java 使用數(shù)據(jù)訂閱功能

訂閱功能也提供了 Java 開(kāi)發(fā)接口,相關(guān)說(shuō)明請(qǐng)見(jiàn) Java Connector。需要注意的是,目前 Java 接口沒(méi)有提供異步訂閱模式,但用戶(hù)程序可以通過(guò)創(chuàng)建 TimerTask 等方式達(dá)到同樣的效果。

下面以一個(gè)示例程序介紹其具體使用方法。它所完成的功能與前面介紹的 C 語(yǔ)言示例基本相同,也是訂閱數(shù)據(jù)庫(kù)中所有電流超過(guò) 10A 的記錄。

準(zhǔn)備數(shù)據(jù)

# 創(chuàng)建 power 庫(kù)
taos> create database power;
# 切換庫(kù)
taos> use power;
# 創(chuàng)建超級(jí)表
taos> create table meters(ts timestamp, current float, voltage int, phase int) tags(location binary(64), groupId int);
# 創(chuàng)建表
taos> create table d1001 using meters tags ("Beijing.Chaoyang", 2);
taos> create table d1002 using meters tags ("Beijing.Haidian", 2);
# 插入測(cè)試數(shù)據(jù)
taos> insert into d1001 values("2020-08-15 12:00:00.000", 12, 220, 1),("2020-08-15 12:10:00.000", 12.3, 220, 2),("2020-08-15 12:20:00.000", 12.2, 220, 1);
taos> insert into d1002 values("2020-08-15 12:00:00.000", 9.9, 220, 1),("2020-08-15 12:10:00.000", 10.3, 220, 1),("2020-08-15 12:20:00.000", 11.2, 220, 1);
# 從超級(jí)表 meters 查詢(xún)電流大于 10A 的記錄
taos> select * from meters where current > 10;
           ts            |    current   |    voltage   |  phase |         location          |   groupid   |
===========================================================================================================
 2020-08-15 12:10:00.000 |    10.30000  |     220      |      1 |      Beijing.Haidian      |           2 |
 2020-08-15 12:20:00.000 |    11.20000  |     220      |      1 |      Beijing.Haidian      |           2 |
 2020-08-15 12:00:00.000 |    12.00000  |     220      |      1 |      Beijing.Chaoyang     |           2 |
 2020-08-15 12:10:00.000 |    12.30000  |     220      |      2 |      Beijing.Chaoyang     |           2 |
 2020-08-15 12:20:00.000 |    12.20000  |     220      |      1 |      Beijing.Chaoyang     |           2 |
Query OK, 5 row(s) in set (0.004896s)

示例程序

public class SubscribeDemo {
    private static final String topic = "topic-meter-current-bg-10";
    private static final String sql = "select * from meters where current > 10";

    public static void main(String[] args) {
        Connection connection = null;
        TSDBSubscribe subscribe = null;

        try {
            Class.forName("com.taosdata.jdbc.TSDBDriver");
            Properties properties = new Properties();
            properties.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
            properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
            String jdbcUrl = "jdbc:TAOS://127.0.0.1:6030/power?user=root&password=taosdata";
            connection = DriverManager.getConnection(jdbcUrl, properties);
            subscribe = ((TSDBConnection) connection).subscribe(topic, sql, true); // 創(chuàng)建訂閱
            int count = 0;
            while (count < 10) {
                TimeUnit.SECONDS.sleep(1); // 等待1秒,避免頻繁調(diào)用 consume,給服務(wù)端造成壓力
                TSDBResultSet resultSet = subscribe.consume(); // 消費(fèi)數(shù)據(jù)
                if (resultSet == null) {
                    continue;
                }
                ResultSetMetaData metaData = resultSet.getMetaData();
                while (resultSet.next()) {
                    int columnCount = metaData.getColumnCount();
                    for (int i = 1; i <= columnCount; i++) {
                        System.out.print(metaData.getColumnLabel(i) + ": " + resultSet.getString(i) + "\t");
                    }
                    System.out.println();
                    count++;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != subscribe)
                    subscribe.close(true); // 關(guān)閉訂閱
                if (connection != null)
                    connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

運(yùn)行示例程序,首先,它會(huì)消費(fèi)符合查詢(xún)條件的所有歷史數(shù)據(jù):

# java -jar subscribe.jar

ts: 1597464000000   current: 12.0   voltage: 220    phase: 1    location: Beijing.Chaoyang  groupid : 2
ts: 1597464600000   current: 12.3   voltage: 220    phase: 2    location: Beijing.Chaoyang  groupid : 2
ts: 1597465200000   current: 12.2   voltage: 220    phase: 1    location: Beijing.Chaoyang  groupid : 2
ts: 1597464600000   current: 10.3   voltage: 220    phase: 1    location: Beijing.Haidian   groupid : 2
ts: 1597465200000   current: 11.2   voltage: 220    phase: 1    location: Beijing.Haidian   groupid : 2

接著,使用 taos 客戶(hù)端向表中新增一條數(shù)據(jù):

# taos
taos> use power;
taos> insert into d1001 values("2020-08-15 12:40:00.000", 12.4, 220, 1);

因?yàn)檫@條數(shù)據(jù)的電流大于10A,示例程序會(huì)將其消費(fèi):

ts: 1597466400000   current: 12.4   voltage: 220    phase: 1    location: Beijing.Chaoyang  groupid: 2

緩存(Cache)

TDengine 采用時(shí)間驅(qū)動(dòng)緩存管理策略(First-In-First-Out,F(xiàn)IFO),又稱(chēng)為寫(xiě)驅(qū)動(dòng)的緩存管理機(jī)制。這種策略有別于讀驅(qū)動(dòng)的數(shù)據(jù)緩存模式(Least-Recent-Used,LRU),直接將最近寫(xiě)入的數(shù)據(jù)保存在系統(tǒng)的緩存中。當(dāng)緩存達(dá)到臨界值的時(shí)候,將最早的數(shù)據(jù)批量寫(xiě)入磁盤(pán)。一般意義上來(lái)說(shuō),對(duì)于物聯(lián)網(wǎng)數(shù)據(jù)的使用,用戶(hù)最為關(guān)心最近產(chǎn)生的數(shù)據(jù),即當(dāng)前狀態(tài)。TDengine 充分利用了這一特性,將最近到達(dá)的(當(dāng)前狀態(tài))數(shù)據(jù)保存在緩存中。

TDengine 通過(guò)查詢(xún)函數(shù)向用戶(hù)提供毫秒級(jí)的數(shù)據(jù)獲取能力。直接將最近到達(dá)的數(shù)據(jù)保存在緩存中,可以更加快速地響應(yīng)用戶(hù)針對(duì)最近一條或一批數(shù)據(jù)的查詢(xún)分析,整體上提供更快的數(shù)據(jù)庫(kù)查詢(xún)響應(yīng)能力。從這個(gè)意義上來(lái)說(shuō),可通過(guò)設(shè)置合適的配置參數(shù)將 TDengine 作為數(shù)據(jù)緩存來(lái)使用,而不需要再部署額外的緩存系統(tǒng),可有效地簡(jiǎn)化系統(tǒng)架構(gòu),降低運(yùn)維的成本。需要注意的是,TDengine 重啟以后系統(tǒng)的緩存將被清空,之前緩存的數(shù)據(jù)均會(huì)被批量寫(xiě)入磁盤(pán),緩存的數(shù)據(jù)將不會(huì)像專(zhuān)門(mén)的 key-value 緩存系統(tǒng)再將之前緩存的數(shù)據(jù)重新加載到緩存中。

TDengine 分配固定大小的內(nèi)存空間作為緩存空間,緩存空間可根據(jù)應(yīng)用的需求和硬件資源配置。通過(guò)適當(dāng)?shù)脑O(shè)置緩存空間,TDengine 可以提供極高性能的寫(xiě)入和查詢(xún)的支持。TDengine 中每個(gè)虛擬節(jié)點(diǎn)(virtual node)創(chuàng)建時(shí)分配獨(dú)立的緩存池。每個(gè)虛擬節(jié)點(diǎn)管理自己的緩存池,不同虛擬節(jié)點(diǎn)間不共享緩存池。每個(gè)虛擬節(jié)點(diǎn)內(nèi)部所屬的全部表共享該虛擬節(jié)點(diǎn)的緩存池。

TDengine 將內(nèi)存池按塊劃分進(jìn)行管理,數(shù)據(jù)在內(nèi)存塊里是以行(row)的形式存儲(chǔ)。一個(gè) vnode 的內(nèi)存池是在 vnode 創(chuàng)建時(shí)按塊分配好,而且每個(gè)內(nèi)存塊按照先進(jìn)先出的原則進(jìn)行管理。在創(chuàng)建內(nèi)存池時(shí),塊的大小由系統(tǒng)配置參數(shù) cache 決定;每個(gè) vnode 中內(nèi)存塊的數(shù)目則由配置參數(shù)blocks決定。因此對(duì)于一個(gè) vnode,總的內(nèi)存大小為:cache * blocks。一個(gè) cache block 需要保證每張表能存儲(chǔ)至少幾十條以上記錄,才會(huì)有效率。

你可以通過(guò)函數(shù) last_row() 快速獲取一張表或一張超級(jí)表的最后一條記錄,這樣很便于在大屏顯示各設(shè)備的實(shí)時(shí)狀態(tài)或采集值。例如:

select last_row(voltage) from meters where location='Beijing.Chaoyang';

該 SQL 語(yǔ)句將獲取所有位于北京朝陽(yáng)區(qū)的電表最后記錄的電壓值。