timeuuid类型

timeuuid具有唯一索引和日期时间的综合特性,可以与日期和时间函数联合使用,常用的关联函数:

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

比如

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

DataStax Enterprise 5.0及更高版本支持一些额外的timeuuid和timestamp函数来操作日期。这些函数可以在INSERT、UPDATE和SELECT语句中使用。

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

开工!

  1. -- 开始工作:
  2. bin/cqlsh localhost
  3. -- 查看所有的键空间:
  4. DESCRIBE keyspaces
  5. -- 使用创建的键空间:
  6. USE myks;
  7. -- 查看已有表:
  8. describe tables;
  9. -- 查看表结构:
  10. describe table users;
  11. -- 删除已有表:
  12. drop table users;

user_status_updates表有一个id字段,类型是timeuuid。

username是一个分区key。表的分区key的作用是将行分组到特定的逻辑相关。在程序中,每个用户的时间线都是一个独立的数据结构,因此按用户划分表是一个合理的策略。

我们称id列为集群(clustering)列。集群列的任务是确定分区中行的顺序。这就是为什么我们观察到在每个用户的状态更新中,行是按id的时间戳严格按升序返回的。

  1. -- 首先,先建表
  2. CREATE TABLE "user_status_updates" (
  3. "username" text,
  4. "id" timeuuid,
  5. "body" text,
  6. PRIMARY KEY ("username", "id")
  7. );
  8.  
  9. CREATE TABLE "users" (
  10. "username" text,
  11. "email" text,
  12. "encrypted_password" blob,
  13. PRIMARY KEY ("username")
  14. );
  15.  
  16. -- 对主表users进行数据插入
  17. INSERT INTO "users"
  18. ("username", "email", "encrypted_password")
  19. VALUES (
  20. 'alice',
  21. 'alice@gmail.com',
  22. 0x8914977ed729792e403da53024c6069a9158b8c4
  23. );
  24.  
  25. INSERT INTO "users"
  26. ("username", "encrypted_password")
  27. VALUES (
  28. 'bob',
  29. 0x10920941a69549d33aaee6116ed1f47e19b8e713
  30. );
  31.  
  32. -- 对从表user_status_updates进行数据插入
  33. INSERT INTO "user_status_updates"
  34. ("username", "id", "body")
  35. VALUES (
  36. 'alice',
  37. 76e7a4d0-e796-11e3-90ce-5f98e903bf02,
  38. 'Learning Cassandra!'
  39. );
  40.  
  41. INSERT INTO "user_status_updates"
  42. ("username", "id", "body")
  43. VALUES (
  44. 'bob',
  45. 97719c50-e797-11e3-90ce-5f98e903bf02,
  46. 'Eating a tasty sandwich.'
  47. );
  48.  
  49. -- 前面我们加了2条从表记录,现在重点关注下id(即timeuuid)的查询
  50. SELECT * FROM "user_status_updates";
  51.  
  52. SELECT "username", "id", "body", DATEOF("id")
  53. FROM "user_status_updates";
  54.  
  55. SELECT "username", "id", "body", UNIXTIMESTAMPOF("id")
  56. FROM "user_status_updates";
  57.  
  58. SELECT * FROM "user_status_updates"
  59. WHERE "username" = 'alice'
  60. AND "id" = 76e7a4d0-e796-11e3-90ce-5f98e903bf02;
  61.  
  62. -- 我们加了2条自制的uuid,那么从时间函数能自动产生uuid吗?会不会产生排序异常?
  63. -- 再插入6条测试数据
  64.  
  65. INSERT INTO "user_status_updates" ("username", "id", "body")
  66. VALUES ('alice', NOW(), 'Alice Update 1');
  67. INSERT INTO "user_status_updates" ("username", "id", "body")
  68. VALUES ('bob', NOW(), 'Bob Update 1');
  69. INSERT INTO "user_status_updates" ("username", "id", "body")
  70. VALUES ('alice', NOW(), 'Alice Update 2');
  71. INSERT INTO "user_status_updates" ("username", "id", "body")
  72. VALUES ('bob', NOW(), 'Bob Update 2');
  73. INSERT INTO "user_status_updates" ("username", "id", "body")
  74. VALUES ('alice', NOW(), 'Alice Update 3');
  75. INSERT INTO "user_status_updates" ("username", "id", "body")
  76. VALUES ('bob', NOW(), 'Bob Update 3');
  77.  
  78. -- 看看时间戳就知道了
  79. SELECT "username", "id", "body",toTimestamp("id"), UNIXTIMESTAMPOF("id")
  80. FROM "user_status_updates";
  81.  
  82. -- 结果:

上面的例子看到一个表的主键中有两列:一个分区键(username)和一个集群列(id)。事实证明,这两种都不仅仅限于一个列。可以定义一个或多个分区键列和零个或多个群集列。

多个群集列

群集列不限于前面指定的一个字段。让我们看看多个集群列是如何工作的,并有助于数据排序。为了说明这一点,我们将重新创建状态更新表,以便按用户更新其状态的日期和时间对其进行群集:

  1. "status_date", "status_time"将是下面的演示多个集群列的例子。

复合主键

复合主键是由多个列组成的简单主键。虽然乍一看,这似乎是对Cassandra表的一个小小的补充,但实际上,具有复合主键的表是一个相当丰富的数据结构,它具有新的数据访问模式。
我们构建一个user_status_updates表,该表存储用户状态更新的时间线。

  1. -- 下面来看看复合主键
  2. -- 建表
  3.  
  4. CREATE TABLE "user_status_updates_by_datetime" (
  5. "username" text,
  6. "status_date" date,
  7. "status_time" time,
  8. "body" text,
  9. PRIMARY KEY ("username", "status_date", "status_time")
  10. );
  11.  
  12. INSERT INTO "user_status_updates_by_datetime" ("username",
  13. "status_date", "status_time", "body")
  14. VALUES ('alice', '2019-11-18', '08:30:55.123', 'Alice Update 1');
  15. INSERT INTO "user_status_updates_by_datetime" ("username",
  16. "status_date", "status_time", "body")
  17. VALUES ('alice', '2019-11-18', '14:40:25.123456789', 'Alice Update 2');
  18. INSERT INTO "user_status_updates_by_datetime" ("username",
  19. "status_date", "status_time", "body")
  20. VALUES ('alice', '2019-11-19', '08:25:25', 'Alice Update 3');
  21. INSERT INTO "user_status_updates_by_datetime" ("username",
  22. "status_date", "status_time", "body")
  23. VALUES ('alice', '2019-11-21', '08:35:55.123456', 'Alice Update 4');
  24. INSERT INTO "user_status_updates_by_datetime" ("username",
  25. "status_date", "status_time", "body")
  26. VALUES ('alice', '2019-11-21', '14:30:15.123', 'Alice Update 5');
  27. INSERT INTO "user_status_updates_by_datetime" ("username",
  28. "status_date", "status_time", "body")
  29. VALUES ('alice', '2019-11-23', '14:50:45.123456', 'Alice Update 6');

-- 试试看插入一些错误数据

  1. INSERT INTO "user_status_updates_by_datetime" ("username",
  2. "status_date", "status_time", "body")
  3. VALUES ('alice', '2019-14-23', '14:50:45.123456', 'Alice Update 7');
  4.  
  5. INSERT INTO "user_status_updates_by_datetime" ("username",
  6. "status_date", "status_time", "body")
  7. VALUES ('alice', '2019-11-23', '14:65:45.123456', 'Alice Update 8');

一些组合查询和传统数据库不同的地方

  1. SELECT * FROM "user_status_updates_by_datetime";
  2. -- 试试组合查询
  3. SELECT * FROM "user_status_updates_by_datetime"
  4. WHERE "username" = 'alice' AND "status_date" < '2019-11-20';
  5.  
  6. -- 试试组合日期和时间查询,要查大于某天及大于几点的数据
  7. SELECT * FROM "user_status_updates_by_datetime"
  8. WHERE "username" = 'alice' AND "status_date" > '2019-11-20' AND
  9. "status_time" > '12:00:00';
  10. -- 报了个错: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)"
  11. -- 原因Cassandra 支持的查询语句很严格,首先 partition key 必须精确查询,最后一个查询才能范围查询。
  12. -- 改成下面这样就合规了
  13. SELECT * FROM "user_status_updates_by_datetime"
  14. WHERE "username" = 'alice' AND "status_date" = '2019-11-21' AND
  15. "status_time" > '12:00:00';

Cassandra没有内置的不同表中数据之间关系的概念。SELECT语句中没有外键约束,也没有JOIN子句;
事实上,在同一个查询中没有从多个表中读取的方法,而关系数据库可以考虑不同表中数据之间的关系,无论它们是一对一、一对多还是多对多。
Cassandra没有用于描述或遍历表间关系的内置机制。也就是说,Cassandra的复合主键结构为一种特殊的主从关系提供了充足的保障。

在关系数据库中,我们可以使用外键约束使关系在模式中显式化,但是Cassandra没有提供这样的功能。事实上,如果我们想为用户和用户状态更新使用两个不同的表,我们无法在数据库模式中显式地编码它们的关系。但是,有一种方法可以将用户和状态更新合并到一个表中,同时保持它们之间的一对多关系。为了实现这一合并,我们将使用Cassandra表的一个特性,这是我们以前从未见过的静态列。

其实,归根结缔,用一个词来形容Cassandra的这种Non Join的做法的核心就是:冗余。

而静态列就是保持冗余和一致性的重要技术手段。

STATIC静态字段

  1. -- 创建表
  2. -- 我们将 email encrypted_password 两个字段设置为 STATIC 了,这意味着同一个 username 只会有一个 email encrypted_password
  3.  
  4. CREATE TABLE "users_with_status_updates" (
  5. "username" text,
  6. "id" timeuuid,
  7. "email" text STATIC,
  8. "encrypted_password" blob STATIC,
  9. "body" text,
  10. PRIMARY KEY ("username", "id")
  11. );
  12.  
  13. INSERT INTO "users_with_status_updates"
  14. ("username", "id", "email", "encrypted_password", "body")
  15. VALUES (
  16. 'alice',
  17. 76e7a4d0-e796-11e3-90ce-5f98e903bf02,
  18. 'alice@gmail.com',
  19. 0x8914977ed729792e403da53024c6069a9158b8c4,
  20. 'Learning Cassandra!'
  21. );
  22.  
  23. SELECT * FROM "users_with_status_updates";
  24. -- 验证插入
  25. INSERT INTO "users_with_status_updates"
  26. ("username", "id", "body")
  27. VALUES ('alice', NOW(), 'Another status update');
  28.  
  29. SELECT * FROM "users_with_status_updates";
  30.  
  31. SELECT "username", "email", "encrypted_password"
  32. FROM "users_with_status_updates"
  33. WHERE "username" = 'alice';
  34.  
  35. SELECT DISTINCT "username", "email", "encrypted_password"
  36. FROM "users_with_status_updates"
  37. WHERE "username" = 'alice';
  38.  
  39. -- 验证另一个主键bob
  40.  
  41. INSERT INTO "users_with_status_updates"
  42. ("username", "email", "encrypted_password")
  43. VALUES (
  44. 'bob',
  45. 'bob@gmail.com',
  46. 0x10920941a69549d33aaee6116ed1f47e19b8e713
  47. );
  48.  
  49. INSERT INTO "users_with_status_updates"
  50. ("username", "id", "body")
  51. VALUES ('bob', NOW(), 'Bob status update');

结果:

高级课题

以上是联合主键的一些用法,现在进入高级些的课题

  • 如何检索单个分区中的所有行
  • 如何检索集群列值范围内的行
  • 如何对单个分区中的行进行分页
  • 如何更改结果的顺序
  • 如何按聚类列的降序存储行
  • 如何分页
  1. SELECT * FROM "user_status_updates"
  2. WHERE "username" = 'alice';
  3.  
  4. -- 以前,我们使用WHERE关键字为一个完整的主键指定一个确切的值。在前面的查询中,我们只指定主键的分区键部分,这允许我们只检索那些我们要求分区的行;
  5. -- 出于唯一性的目的,username列肯定不能保证唯一性;id是一个UUID,我们可以跳过用户名分区键,只通过id clustering列查找行吗?让我们试一试:
  6.  
  7. SELECT * FROM "user_status_updates"
  8. WHERE id = 3f9b5f00-e8f7-11e3-9211-5f98e903bf02;
  9.  
  10. INSERT INTO "status_update_replies"
  11. ("status_update_username", "status_update_id", "id", "body")
  12. VALUES(
  13. 'alice',
  14. 76e7a4d0-e796-11e3-90ce-5f98e903bf02,
  15. NOW(),
  16. 'Good luck!'
  17. );
  18.  
  19. SELECT * FROM "status_update_replies"
  20. WHERE "status_update_username" = 'alice';
  21.  
  22. -- 检索特定时间范围的状态更新
  23.  
  24. -- 在探究了CQLWHERE关键字的限制之后,让我们返回到user_status_updates表。假设我们想为MyStatus构建一个存档特性,显示用户在请求的一个月内的所有更新。
  25. -- CQL术语中,我们希望选择一系列集群列;例如,让我们返回20145月创建的alice的所有状态更新:
  26.  
  27. SELECT "id", DATEOF("id"), "body"
  28. FROM "user_status_updates"
  29. WHERE "username" = 'alice'
  30. AND "id" >= MINTIMEUUID('2014-05-01')
  31. AND "id" <= MAXTIMEUUID('2014-05-31');
  32.  
  33. -- 在分区中分页,顺序
  34.  
  35. SELECT "id", DATEOF("id"), "body"
  36. FROM "user_status_updates"
  37. WHERE "username" = 'alice'
  38. LIMIT 3;
  39. -- 在分区中筛选后分页,顺序
  40.  
  41. SELECT "id", DATEOF("id"), "body"
  42. FROM "user_status_updates"
  43. WHERE "username" = 'alice'
  44. AND id > 3f9df710-e8f7-11e3-9211-5f98e903bf02
  45. LIMIT 3;
  46.  
  47. SELECT COUNT(1) FROM "user_status_updates"
  48. WHERE "username" = 'alice';
  49.  
  50. -- 在分区中筛选后分页,倒序
  51.  
  52. SELECT "id", DATEOF("id"), "body"
  53. FROM "user_status_updates"
  54. WHERE "username" = 'alice'
  55. ORDER BY "id" DESC;
  56.  
  57. -- 在分区中不使用簇列进行排序,将会报错
  58.  
  59. cqlsh:myks> SELECT "id", DATEOF("id"), "body"
  60. ... FROM "user_status_updates"
  61. ... WHERE "username" = 'alice'
  62. ... ORDER BY "body" DESC;
  63. 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"
  64.  
  65. -- 显式设定特定的倒序排序
  66. CREATE TABLE "reversed_user_status_updates" (
  67. "username" text,
  68. "id" timeuuid,
  69. "body" text,
  70. PRIMARY KEY ("username", "id")
  71. ) WITH CLUSTERING ORDER BY ("id" DESC);
  72.  
  73. INSERT INTO "reversed_user_status_updates"
  74. ("username", "id", "body")
  75. VALUES ('alice', NOW(), 'Reversed status 1');
  76. INSERT INTO "reversed_user_status_updates"
  77. ("username", "id", "body")
  78. VALUES ('alice', NOW(), 'Reversed status 2');
  79. INSERT INTO "reversed_user_status_updates"
  80. ("username", "id", "body")
  81. VALUES ('alice', NOW(), 'Reversed status 3');
  82. -- 显式倒序排序测试
  83. SELECT * FROM "reversed_user_status_updates"
  84. WHERE "username" = 'alice';
  85.  
  86. -- 正确
  87.  
  88. SELECT * FROM "user_status_updates_by_datetime";
  89.  
  90. SELECT * FROM "user_status_updates_by_datetime"
  91. WHERE "username" = 'alice'
  92. ORDER BY "status_date" ASC, "status_time" ASC;
  93.  
  94. SELECT * FROM "user_status_updates_by_datetime"
  95. WHERE "username" = 'alice'
  96. ORDER BY "status_date" DESC, "status_time" DESC;
  97.  
  98. --错误
  99.  
  100. SELECT * FROM "user_status_updates_by_datetime"
  101. WHERE "username" = 'alice'
  102. ORDER BY "status_date" ASC, "status_time" DESC;
  103.  
  104. SELECT * FROM "user_status_updates_by_datetime"
  105. WHERE "username" = 'alice'
  106. ORDER BY "status_date" DESC, "status_time" ASC;
  107.  
  108. -- 显式设定特定的倒序-正序排序组合
  109.  
  110. CREATE TABLE "reversed_user_status_updates_by_datetime" (
  111. "username" text,
  112. "status_date" date,
  113. "status_time" time,
  114. "body" text,
  115. PRIMARY KEY ("username", "status_date", "status_time")
  116. ) WITH CLUSTERING ORDER BY ("status_date" ASC, "status_time" DESC);
  117.  
  118. JSON支持
  119. -- 插入JSON,看起来完全不像传统数据库SQL
  120.  
  121. INSERT INTO "user_status_updates_by_datetime"
  122. JSON '{"username": "alice", "status_date": "2016-11-24", "status_time":
  123. "13:35:20.123456", "body": "Alice Update 7"}';
  124.  
  125. SELECT * FROM "user_status_updates_by_datetime";
  126. -- json查询
  127. SELECT JSON * FROM "user_status_updates_by_datetime"
  128. WHERE "username" = 'alice'
  129. AND status_date > '2016-11-20';

Cassandra开发入门文档第二部分(timeuuid类型、复合主键、静态字段详解)的更多相关文章

  1. Cassandra开发入门文档第三部分(非规范化关系结构、批处理)

    非规范化关系结构 第二部分我们讲了复合主键,这可以灵活的解决主从关系,也即是一对多关系,那么多对多关系呢?多对多关系的数据模型应该回答两个问题: 我跟着谁? 谁跟着我? -- 建表,我们发现这里有个不 ...

  2. Cassandra开发入门文档第五部分(使用场景)

    正确建模 开发人员在构建Cassandra数据库时犯的另一个主要错误是分区键的选择不佳.cassandra是分布式的.这意味着您需要有一种方法来跨节点分布数据.Cassandra通过散列每个表的主键( ...

  3. Cassandra开发入门文档第一部分

    Cassandra的特点 横向可扩展性: Cassandra部署具有几乎无限的存储和处理数据的能力.当需要额外的容量时,可以简单地将更多的机器添加到集群中.当新机器加入集群时,Cassandra需要对 ...

  4. Cassandra开发入门文档第四部分(集合类型、元组类型、时间序列、计数列)

    Cassandra 提供了三种集合类型,分别是Set,List,MapSet: 非重复集,存储了一组类型相同的不重复元素,当被查询时会返回排好序的结果,但是内部构成是无序的值,应该是在查询时对结果进行 ...

  5. 使用DOM进行xml文档的crud(增删改查)操作<操作详解>

    很多朋友对DOM有感冒,这里我花了一些时间写了一个小小的教程,这个能看懂,会操作了,我相信基于DOM的其它API(如JDOM,DOM4J等)一般不会有什么问题. 后附java代码,也可以下载(可点击这 ...

  6. Hibernate入门之主键生成策略详解

    前言 上一节我们讲解了Hibernate命名策略,从本节我们开始陆续讲解属性.关系等映射,本节我们来讲讲主键的生成策略. 主键生成策略 JPA规范支持4种不同的主键生成策略(AUTO.IDENTITY ...

  7. 【简明翻译】Hibernate 5.4 Getting Started Guide 官方入门文档

    前言 最近的精力主要集中在Hibernate上,在意识到Hibernate 5 的中文资料并不多的时候,我不得不把目光转向Hibernate的官方doc,学习之余简要翻一下入门文档. 原文地址:htt ...

  8. Apache BeanUtils 1.9.2 官方入门文档

    为什么需要Apache BeanUtils? Apache BeanUtils 是 Apache开源软件组织下面的一个项目,被广泛使用于Spring.Struts.Hibernate等框架,有数千个j ...

  9. Duilib入门文档提供下载

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] Duilib入门文档 基本框架 编写界面xml 响应事件 贴图描述 类html文本描述 动态换肤 Dll插件 资源打包 Duil ...

随机推荐

  1. 将java的jar包作为windows的服务来启动

    1.在idea中用maven将程序打成jar,放到运行的目录中. 2.去github上面下载winsw: https://github.com/kohsuke/winsw/releases 3. 将W ...

  2. Oracle rman备份还原

    备份脚本: oracle备份fullbak.sh 脚本 . /u01/prod/db/12.1.0/PROD_erpdbp.env LOGDATE="`date '+%Y%m%d'`&quo ...

  3. 【使用DIV+CSS重写网站首页案例】CSS选择器

    使用表格<table></table>对网站首页进行布局有缺陷,不能拖动版块,不灵活. DIV Div是一个html的标签,单独使用没有意义,必须结合CSS使用: 是一个块级元 ...

  4. php7中的随机数,序列化及unicode增强

    <?php //random_bytes //random_int //unserialize可自定义过滤 //unicode增强\u{code-point} header("Cont ...

  5. httprunner学习18-多进程运行模式

    前言 使用Locust进行性能测试时,当一台单机不足以模拟所需的用户数量的时候,可以使用主从模式,启动一个master节点,多个slave节点. 主从模式 loucsts 是httprunner 里面 ...

  6. 关于PID控制的一点资料搜集

    CMU做的控制教程 <动态系统的反馈控制> MATLAB&Simulink的PID控制(官方)

  7. ImportError: No module named wx

    先检查下Python中是否有此模块 $ python >>> import wx ImportError: No module named wx  wx模块的确没有安装. 要安装wx ...

  8. Linux系统下jar包的启动方式

    Linux 运行jar包命令如下: 方式一: Java -jar shareniu.jar 特点:当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出 那如何让窗口不锁定? ...

  9. jQuery--data()方法

    data() 函数用于在当前jQuery对象所匹配的所有元素上存取数据. 通过data()函数存取的数据都是临时数据,一旦页面刷新,之前存放的数据都将不复存在. 该函数属于jQuery对象(实例).如 ...

  10. 用Python 打开程序的两中方法

    1.ShellExecute函数 import win32api win32api.ShellExecute(0, 'open', 'notepad.exe', '', '', 0) # 后台执行 w ...