Cassandra開發入門文檔第二部分(timeuuid類型、復合主鍵、靜態字段詳解)


timeuuid類型

timeuuid具有唯一索引和日期時間的綜合特性,可以與日期和時間函數聯合使用,常用的關聯函數:

  • dateOf()
  • now()
  • minTimeuuid() and maxTimeuuid()
  • toDate(timeuuid)
  • toTimestamp(timeuuid)
  • toUnixTimestamp(timeuuid)

比如

SELECT * FROM myTable
WHERE t > maxTimeuuid('2013-01-01 00:05+0000')
AND t < minTimeuuid('2013-02-02 10:00+0000')

 

DataStax Enterprise 5.0及更高版本支持一些額外的timeuuid和timestamp函數來操作日期。這些函數可以在INSERT、UPDATE和SELECT語句中使用。

CREATE TABLE sample_times (a int, b timestamp, c timeuuid, d bigint, PRIMARY KEY (a,b,c,d));
INSERT INTO sample_times (a,b,c,d) VALUES (1, toUnixTimestamp(now()), 50554d6e-29bb-11e5-b345-feff819cdc9f, toTimestamp(now()));
SELECT a, b, toDate(c), toDate(d) FROM sample_times

開工!

-- 開始工作:
bin/cqlsh localhost
-- 查看所有的鍵空間:
DESCRIBE keyspaces
-- 使用創建的鍵空間:
USE myks;
-- 查看已有表:
describe tables;
-- 查看表結構:
describe table users;
-- 刪除已有表:
drop table users;

 

user_status_updates表有一個id字段,類型是timeuuid。

username是一個分區key。表的分區key的作用是將行分組到特定的邏輯相關。在程序中,每個用戶的時間線都是一個獨立的數據結構,因此按用戶划分表是一個合理的策略。

我們稱id列為集群(clustering)列。集群列的任務是確定分區中行的順序。這就是為什么我們觀察到在每個用戶的狀態更新中,行是按id的時間戳嚴格按升序返回的。

-- 首先,先建表
CREATE TABLE "user_status_updates" (
"username" text,
"id" timeuuid,
"body" text,
PRIMARY KEY ("username", "id")
);

CREATE TABLE "users" (
"username" text,
"email" text,
"encrypted_password" blob,
PRIMARY KEY ("username")
);

-- 對主表users進行數據插入
INSERT INTO "users"
("username", "email", "encrypted_password")
VALUES (
'alice',
'alice@gmail.com',
0x8914977ed729792e403da53024c6069a9158b8c4
);

INSERT INTO "users"
("username", "encrypted_password")
VALUES (
'bob',
0x10920941a69549d33aaee6116ed1f47e19b8e713
);

-- 對從表user_status_updates進行數據插入
INSERT INTO "user_status_updates"
("username", "id", "body")
VALUES (
'alice',
76e7a4d0-e796-11e3-90ce-5f98e903bf02,
'Learning Cassandra!'
);

INSERT INTO "user_status_updates"
("username", "id", "body")
VALUES (
'bob',
97719c50-e797-11e3-90ce-5f98e903bf02,
'Eating a tasty sandwich.'
);

-- 前面我們加了2條從表記錄,現在重點關注下id(即timeuuid)的查詢
SELECT * FROM "user_status_updates";

SELECT "username", "id", "body", DATEOF("id")
FROM "user_status_updates";

SELECT "username", "id", "body", UNIXTIMESTAMPOF("id")
FROM "user_status_updates";

SELECT * FROM "user_status_updates"
WHERE "username" = 'alice'
AND "id" = 76e7a4d0-e796-11e3-90ce-5f98e903bf02;

-- 我們加了2條自制的uuid,那么從時間函數能自動產生uuid嗎?會不會產生排序異常?
-- 再插入6條測試數據

INSERT INTO "user_status_updates" ("username", "id", "body")
VALUES ('alice', NOW(), 'Alice Update 1');
INSERT INTO "user_status_updates" ("username", "id", "body")
VALUES ('bob', NOW(), 'Bob Update 1');
INSERT INTO "user_status_updates" ("username", "id", "body")
VALUES ('alice', NOW(), 'Alice Update 2');
INSERT INTO "user_status_updates" ("username", "id", "body")
VALUES ('bob', NOW(), 'Bob Update 2');
INSERT INTO "user_status_updates" ("username", "id", "body")
VALUES ('alice', NOW(), 'Alice Update 3');
INSERT INTO "user_status_updates" ("username", "id", "body")
VALUES ('bob', NOW(), 'Bob Update 3');

-- 看看時間戳就知道了
SELECT "username", "id", "body",toTimestamp("id"), UNIXTIMESTAMPOF("id")
FROM "user_status_updates";

-- 結果:

上面的例子看到一個表的主鍵中有兩列:一個分區鍵(username)和一個集群列(id)。事實證明,這兩種都不僅僅限於一個列。可以定義一個或多個分區鍵列和零個或多個群集列。

多個群集列

群集列不限於前面指定的一個字段。讓我們看看多個集群列是如何工作的,並有助於數據排序。為了說明這一點,我們將重新創建狀態更新表,以便按用戶更新其狀態的日期和時間對其進行群集:

"status_date", "status_time"將是下面的演示多個集群列的例子。

復合主鍵

復合主鍵是由多個列組成的簡單主鍵。雖然乍一看,這似乎是對Cassandra表的一個小小的補充,但實際上,具有復合主鍵的表是一個相當豐富的數據結構,它具有新的數據訪問模式。
我們構建一個user_status_updates表,該表存儲用戶狀態更新的時間線。

-- 下面來看看復合主鍵
-- 建表

CREATE TABLE "user_status_updates_by_datetime" (
"username" text,
"status_date" date,
"status_time" time,
"body" text,
PRIMARY KEY ("username", "status_date", "status_time")
);

INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-11-18', '08:30:55.123', 'Alice Update 1');
INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-11-18', '14:40:25.123456789', 'Alice Update 2');
INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-11-19', '08:25:25', 'Alice Update 3');
INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-11-21', '08:35:55.123456', 'Alice Update 4');
INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-11-21', '14:30:15.123', 'Alice Update 5');
INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-11-23', '14:50:45.123456', 'Alice Update 6');

 

-- 試試看插入一些錯誤數據

INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-14-23', '14:50:45.123456', 'Alice Update 7');

INSERT INTO "user_status_updates_by_datetime" ("username", 
"status_date", "status_time", "body")
VALUES ('alice', '2019-11-23', '14:65:45.123456', 'Alice Update 8');

 

一些組合查詢和傳統數據庫不同的地方

SELECT * FROM "user_status_updates_by_datetime";
-- 試試組合查詢
SELECT * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice' AND "status_date" < '2019-11-20';

-- 試試組合日期和時間查詢,要查大於某天及大於幾點的數據
SELECT * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice' AND "status_date" > '2019-11-20' AND 
"status_time" > '12:00:00';
-- 報了個錯:InvalidRequest: Error from server: code=2200 [Invalid query] message="Clustering column "status_time" cannot be restricted (preceding column "status_date" is restricted by a non-EQ relation)"
-- 原因Cassandra 支持的查詢語句很嚴格,首先 partition key 必須精確查詢,最后一個查詢才能范圍查詢。
-- 改成下面這樣就合規了
SELECT * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice' AND "status_date" = '2019-11-21' AND 
"status_time" > '12:00:00';

 

Cassandra沒有內置的不同表中數據之間關系的概念。SELECT語句中沒有外鍵約束,也沒有JOIN子句;
事實上,在同一個查詢中沒有從多個表中讀取的方法,而關系數據庫可以考慮不同表中數據之間的關系,無論它們是一對一、一對多還是多對多。
Cassandra沒有用於描述或遍歷表間關系的內置機制。也就是說,Cassandra的復合主鍵結構為一種特殊的主從關系提供了充足的保障。

在關系數據庫中,我們可以使用外鍵約束使關系在模式中顯式化,但是Cassandra沒有提供這樣的功能。事實上,如果我們想為用戶和用戶狀態更新使用兩個不同的表,我們無法在數據庫模式中顯式地編碼它們的關系。但是,有一種方法可以將用戶和狀態更新合並到一個表中,同時保持它們之間的一對多關系。為了實現這一合並,我們將使用Cassandra表的一個特性,這是我們以前從未見過的靜態列。

其實,歸根結締,用一個詞來形容Cassandra的這種Non Join的做法的核心就是:冗余。

而靜態列就是保持冗余和一致性的重要技術手段。

STATIC靜態字段

-- 創建表
-- 我們將 email 和 encrypted_password 兩個字段設置為 STATIC 了,這意味着同一個 username 只會有一個 email 和 encrypted_password

CREATE TABLE "users_with_status_updates" (
"username" text,
"id" timeuuid,
"email" text STATIC,
"encrypted_password" blob STATIC,
"body" text,
PRIMARY KEY ("username", "id")
);

INSERT INTO "users_with_status_updates"
("username", "id", "email", "encrypted_password", "body")
VALUES (
'alice',
76e7a4d0-e796-11e3-90ce-5f98e903bf02,
'alice@gmail.com',
0x8914977ed729792e403da53024c6069a9158b8c4,
'Learning Cassandra!'
);

SELECT * FROM "users_with_status_updates";
-- 驗證插入
INSERT INTO "users_with_status_updates"
("username", "id", "body")
VALUES ('alice', NOW(), 'Another status update');

SELECT * FROM "users_with_status_updates";

SELECT "username", "email", "encrypted_password"
FROM "users_with_status_updates"
WHERE "username" = 'alice';

SELECT DISTINCT "username", "email", "encrypted_password"
FROM "users_with_status_updates"
WHERE "username" = 'alice';

-- 驗證另一個主鍵bob

INSERT INTO "users_with_status_updates"
("username", "email", "encrypted_password")
VALUES (
'bob',
'bob@gmail.com',
0x10920941a69549d33aaee6116ed1f47e19b8e713
);

INSERT INTO "users_with_status_updates"
("username", "id", "body")
VALUES ('bob', NOW(), 'Bob status update');

結果:

高級課題

以上是聯合主鍵的一些用法,現在進入高級些的課題

  • 如何檢索單個分區中的所有行
  • 如何檢索集群列值范圍內的行
  • 如何對單個分區中的行進行分頁
  • 如何更改結果的順序
  • 如何按聚類列的降序存儲行
  • 如何分頁

 

SELECT * FROM "user_status_updates"
WHERE "username" = 'alice';

-- 以前,我們使用WHERE關鍵字為一個完整的主鍵指定一個確切的值。在前面的查詢中,我們只指定主鍵的分區鍵部分,這允許我們只檢索那些我們要求分區的行;
-- 出於唯一性的目的,username列肯定不能保證唯一性;id是一個UUID,我們可以跳過用戶名分區鍵,只通過id clustering列查找行嗎?讓我們試一試:

SELECT * FROM "user_status_updates"
WHERE id = 3f9b5f00-e8f7-11e3-9211-5f98e903bf02;

INSERT INTO "status_update_replies"
("status_update_username", "status_update_id", "id", "body")
VALUES(
'alice',
76e7a4d0-e796-11e3-90ce-5f98e903bf02,
NOW(),
'Good luck!'
);

SELECT * FROM "status_update_replies"
WHERE "status_update_username" = 'alice';

-- 檢索特定時間范圍的狀態更新

-- 在探究了CQL中WHERE關鍵字的限制之后,讓我們返回到user_status_updates表。假設我們想為MyStatus構建一個存檔特性,顯示用戶在請求的一個月內的所有更新。
-- 在CQL術語中,我們希望選擇一系列集群列;例如,讓我們返回2014年5月創建的alice的所有狀態更新:

SELECT "id", DATEOF("id"), "body"
FROM "user_status_updates"
WHERE "username" = 'alice'
AND "id" >= MINTIMEUUID('2014-05-01')
AND "id" <= MAXTIMEUUID('2014-05-31');


-- 在分區中分頁,順序

SELECT "id", DATEOF("id"), "body"
FROM "user_status_updates"
WHERE "username" = 'alice'
LIMIT 3;
-- 在分區中篩選后分頁,順序

SELECT "id", DATEOF("id"), "body"
FROM "user_status_updates"
WHERE "username" = 'alice'
AND id > 3f9df710-e8f7-11e3-9211-5f98e903bf02
LIMIT 3;

SELECT COUNT(1) FROM "user_status_updates"
WHERE "username" = 'alice';

-- 在分區中篩選后分頁,倒序

SELECT "id", DATEOF("id"), "body"
FROM "user_status_updates"
WHERE "username" = 'alice'
ORDER BY "id" DESC;

-- 在分區中不使用簇列進行排序,將會報錯

cqlsh:myks> SELECT "id", DATEOF("id"), "body"
... FROM "user_status_updates"
... WHERE "username" = 'alice'
... ORDER BY "body" DESC;
InvalidRequest: Error from server: code=2200 [Invalid query] message="Order by is currently only supported on the clustered columns of the PRIMARY KEY, got body"

-- 顯式設定特定的倒序排序
CREATE TABLE "reversed_user_status_updates" (
"username" text,
"id" timeuuid,
"body" text,
PRIMARY KEY ("username", "id")
) WITH CLUSTERING ORDER BY ("id" DESC);

INSERT INTO "reversed_user_status_updates"
("username", "id", "body")
VALUES ('alice', NOW(), 'Reversed status 1');
INSERT INTO "reversed_user_status_updates"
("username", "id", "body")
VALUES ('alice', NOW(), 'Reversed status 2');
INSERT INTO "reversed_user_status_updates"
("username", "id", "body")
VALUES ('alice', NOW(), 'Reversed status 3');
-- 顯式倒序排序測試
SELECT * FROM "reversed_user_status_updates"
WHERE "username" = 'alice';

-- 正確

SELECT * FROM "user_status_updates_by_datetime";

SELECT * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice'
ORDER BY "status_date" ASC, "status_time" ASC;

SELECT * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice'
ORDER BY "status_date" DESC, "status_time" DESC;

--錯誤

SELECT * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice'
ORDER BY "status_date" ASC, "status_time" DESC;

SELECT * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice'
ORDER BY "status_date" DESC, "status_time" ASC;

-- 顯式設定特定的倒序-正序排序組合

CREATE TABLE "reversed_user_status_updates_by_datetime" (
"username" text,
"status_date" date,
"status_time" time,
"body" text,
PRIMARY KEY ("username", "status_date", "status_time")
) WITH CLUSTERING ORDER BY ("status_date" ASC, "status_time" DESC);

JSON支持
-- 插入JSON,看起來完全不像傳統數據庫SQL了

INSERT INTO "user_status_updates_by_datetime"
JSON '{"username": "alice", "status_date": "2016-11-24", "status_time":
"13:35:20.123456", "body": "Alice Update 7"}';


SELECT * FROM "user_status_updates_by_datetime";
-- json查詢
SELECT JSON * FROM "user_status_updates_by_datetime"
WHERE "username" = 'alice'
AND status_date > '2016-11-20';

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM