【问题标题】:Does an UPDATE become an implied INSERTUPDATE 是否成为隐含的​​ INSERT
【发布时间】:2013-06-25 06:34:14
【问题描述】:

对于 Cassandra,如果所选行不存在,UPDATEs 是否会成为隐含的​​ INSERT?也就是说,如果我说

 UPDATE users SET name = "Raedwald" WHERE id = 545127

idusers 表的PRIMARY KEY,并且该表没有key 为545127 的行,会不会等价于

 INSERT INTO users (id, name) VALUES (545127, "Raedwald")

我知道情况正好相反:已经存在的idINSERT 变成了具有id 的行的UPDATE。由于这个原因,较早的 Cassandra 文档谈到插入实际上是“upserts”。

我对 CQL3 的案例感兴趣,Cassandra 版本 1.2+。

【问题讨论】:

    标签: cassandra cql


    【解决方案1】:

    是的,对于 Cassandra,UPDATEINSERT 的同义词,正如 the CQL documentation 中所解释的那样,它对 UPDATE 进行了以下说明:

    请注意,与 SQL 不同,UPDATE 不检查该行的先前存在:如果之前不存在,则创建该行,否则更新该行。此外,没有办法知道发生了哪个创建或更新。其实INSERTUPDATE的语义是相同的。

    为了使语义不同,Cassandra 需要进行读取以了解该行是否已存在。 Cassandra 是写优化的,所以你总是可以假设它在任何写操作上都不会先读再写。唯一的例外是计数器(除非replicate_on_write = false),在这种情况下,增量复制涉及读取。

    【讨论】:

    • 嗨,我有一个问题,更新键与插入相同,但如果我想更新另一列的数据(例如用户名或...)。和插入一样吗?合并批量数据的最佳解决方案是什么?
    【解决方案2】:

    很遗憾,接受的答案并非 100% 准确。 inserts 与 updates 不同:

    cqlsh> create table ks.t (pk int, ck int, v int, primary key (pk, ck));
    cqlsh> update ks.t set v = null where pk = 0 and ck = 0;
    cqlsh> select * from ks.t where pk = 0 and ck = 0;
    
     pk | ck | v
    ----+----+---
    
    (0 rows)
    cqlsh> insert into ks.t (pk,ck,v) values (0,0,null);
    cqlsh> select * from ks.t where pk = 0 and ck = 0;
    
     pk | ck | v
    ----+----+------
      0 |  0 | null
    
    (1 rows)
    
    

    Scylla 做同样的事情。

    在 Scylla 和 Cassandra 中,行是 细胞序列。每列都有一个对应的单元格(或在非冻结集合或 UDT 的情况下的一组单元格)。但是还有一个额外的、不可见的单元格 - 行标记(至少在 Scylla 中;我怀疑 Cassandra 有类似的东西)。

    行标记对所有其他单元格都已死的行有所不同:当且仅当至少有一个活的单元格时,才会在查询中显示一行。因此,如果行标记处于活动状态,则该行将显示,即使所有其他列之前使用例如设置为 null。 updates.

    inserts 创建一个实时行标记,而updates 不触摸行标记,因此很明显它们是不同的。上面的例子说明了这一点。 有人可能会争辩说,行标记是 Cassandra/Scylla 的“内部”,但正如您所见,它们的效果是可见的。无论您喜欢与否,行标记都会影响您的生活,因此记住它们可能会很有用。

    很遗憾,没有文档提到行标记(好吧,我发现了这个:https://docs.scylladb.com/architecture/sstable/sstable2/sstable-data-file/#cql-row-marker,但它是在解释 SSTable 内部的上下文中,这可能是专门为 Scylla 开发人员而不是用户提供的)。

    奖励:单元格删除

    delete v from ks.t where pk = 0 and ck = 0
    

    null更新相同:

    update ks.t set v = null where pk = 0 and ck = 0
    

    确实,单元格删除也不会触及行标记。它只将指定单元格设置为null

    这与行删除不同:

    delete from ks.t where pk = 0 and ck = 0
    

    因为行删除插入了一个行墓碑,它会杀死行中的所有单元格(包括行标记)。您可以说行删除与插入相反。更新和单元格删除介于两者之间。

    【讨论】:

    • 这很有趣。我试过你的例子,它就像你说的那样有效。当然,当您将值 v 设置为 null 以外的值时,update 确实会创建一个行标记,因此对于 null 值而言,它的行为似乎有所不同。那一定是你所说的“一个活细胞”。再次,这是一个很好的发现!
    • “当然,当您将值 v 设置为 null 以外的值时,update 确实会创建一个行标记”不!尝试将值更新回null。编辑,更多细节:update'ing v 到非null,然后将其更新回null,该行将消失。但是,在使用insert 设置v,然后将其更新为null 之后,该行将仍然存在。因为insert 创建了一个行标记。
    • 在将v 更新为非null 后看到该行的原因是v 单元格是活动的。行标记不是。在insert 和非null v 之后,有两个活单元格:v 单元格和行标记。
    • 这是一个很好的解释。这将是一篇博客文章的好主题!
    • 谢谢。我们会看到这篇博文:)
    【解决方案3】:

    但是,可以做的是:

    UPDATE table_name SET field = false WHERE key = 55 IF EXISTS;
    

    这将确保您的更新是真正的更新,而不是 upsert。

    【讨论】:

    • 是的。澄清一下:if exists防止 insert
    • 需要启用 LightWeightTransactions 以支持此类查询。
    猜你喜欢
    • 1970-01-01
    • 2013-11-16
    • 2016-05-21
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多