十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ScyllaDB CQL BATCH 语句完全指南:语法、原子性、批处理日志与强一致性

ScyllaDB CQL BATCH 语句完全指南:语法、原子性、批处理日志与强一致性 ScyllaDB CQL BATCH 语句完全指南语法、原子性、批处理日志与强一致性【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladbBATCH 是 ScyllaDB 的 CQL 中用于将多条INSERT、UPDATE、DELETE语句组合成单条请求执行的语句其完整规范定义在 docs/cql/dml/batch.rst。本指南围绕该文档展开深入讲解 BATCH 的语法、原子性语义、LOGGED/UNLOGGED/COUNTER 三种类型的区别、强一致性表上的特殊行为并结合仓库源码cql3/Cql.g、cql3/statements/batch_statement.cc、db/batchlog_manager.cc剖析其底层实现与实战配置。读完本文你将能够正确地在单分区与多分区场景下选用合适的 BATCH 类型理解批量写入的原子性边界与性能权衡并掌握批量大小阈值等相关配置项。BATCH 语法与示例语法定义BATCH 语句的完整文法定义如下batch_statement: BEGIN [ UNLOGGED | COUNTER ] BATCH : [ USING update_parameter ( AND update_parameter )* ] : modification_statement ( ; modification_statement )* : APPLY BATCH modification_statement: insert_statement | update_statement | delete_statement其 ANTLR 文法实现在 cql3/Cql.g 中BEGIN后可选地跟随UNLOGGED或COUNTER关键字默认类型为LOGGED对应cql3::statements::raw::batch_statement::type::LOGGED随后是可选USING子句、一条或多条修改语句由batchStatementObjective限制为insertStatement、updateStatement、deleteStatement三者之一见 cql3/Cql.g最后以APPLY BATCH结束。示例官方文档给出了一个跨多个分区、混合三种修改语句的经典示例BEGIN BATCH INSERT INTO users (userid, password, name) VALUES (user2, chngem3b, second user); UPDATE users SET password ps22dhds WHERE userid user3; INSERT INTO users (userid, password) VALUES (user4, chngem3c); DELETE name FROM users WHERE userid user1; APPLY BATCH;注意示例中并未显式指定 BATCH 类型因此这是一个 LOGGED 批处理。BATCH 的核心用途BATCH 将多条修改语句组合为一条语句主要解决三个问题减少网络往返原本需要客户端与服务器以及有时服务器协调节点与副本之间多次交互的多个更新现在一次请求即可完成。单分区原子性同一分区键partition key下的所有更新在 BATCH 中被原子地执行。默认的最终一致性保证默认情况下批处理中的所有操作以logged记录日志方式执行确保所有变更最终全部完成或一个都不完成即 all-or-nothing详见下文 UNLOGGED 批处理 一节。文档同时强调了几点重要约束BATCH 中只能包含UPDATE、INSERT和DELETE语句不能嵌套其他 BATCH。BATCH不是SQL 事务的完整等价物。如果每条操作未显式指定时间戳则所有操作将使用同一个时间戳自动生成或 BATCH 级别提供。由于 ScyllaDB 在时间戳相同时的冲突解决机制操作的实际生效顺序可能与 BATCH 中列出的顺序不同。若要强制特定顺序必须为每条操作分别指定时间戳。这一点与 docs/cql/dml.rst 中更新排序Update ordering一节描述的时间戳冲突解决算法一致cell 级别以时间戳最大者获胜时间戳相同时依据 cell 是否为 tombstone、是否过期、TTL 以及值本身等属性做一致的冲突裁决。单分区的 LOGGED 批处理会被自动优化为 UNLOGGED 批处理以消除不必要的批处理日志开销下文源码分析会展示这一优化在 cql3/statements/batch_statement.cc 中的体现。USING 子句与更新参数BATCH 支持USING子句携带更新参数其中TIMESTAMP的语义与UPDATE语句中的TIMESTAMP参数完全相同TIMEOUT同样受支持TTL不被允许见下文校验部分。update_parameter的完整定义位于 docs/cql/dml.rstupdate_parameter: ( TIMESTAMP int_value | TTL int_value | TIMEOUT duration )各参数的语义TIMESTAMP为操作设置时间戳。若未指定协调节点会在语句开始执行时使用当前时间自 Unix 纪元 1970-01-01 00:00:00 UTC 起以微秒为单位作为时间戳。同一协调节点上生成的查询时间戳保证唯一即使跨该节点上的不同 shard但不同节点分配的时间戳不保证全局唯一在高写入速率下时间戳碰撞并不罕见碰撞时由冲突解决算法决定哪个插入的 cell 胜出。TTL为插入值指定可选的生存时间秒到期后自动删除。TTL 作用于插入的值本身而非列后续对列的更新会重置 TTL。默认永不过期TTL 为 0 等价于无 TTL若表配置了default_time_to_liveTTL 为 0 会移除插入或更新值的 TTL。TIMEOUT为特定请求指定超时时长。源码中的参数校验USING子句的参数在 cql3/statements/batch_statement.cc 的batch_statement::validate()中做了严格约束这些约束直接决定了哪些组合合法BATCH 级别不允许设置全局 TTL抛出Global TTL on the BATCH statement is not supported.。条件 BATCH含IF子句不允许自定义时间戳抛出Cannot provide custom timestamp for conditional BATCH。COUNTER 批处理不允许自定义时间戳抛出Cannot provide custom timestamp for counter BATCH。含计数器语句的批处理不允许自定义时间戳抛出Cannot provide custom timestamp for a BATCH containing counters。时间戳要么在 BATCH 级别设置要么在单条语句级别设置二者不能混用抛出Timestamp must be set either on BATCH or individual statements。COUNTER 批处理中不允许包含非计数器语句抛出Cannot include non-counter statement in a counter batch。LOGGED 批处理中不允许包含计数器语句抛出Cannot include a counter statement in a logged batch。计数器与非计数器变更不能出现在同一批处理中抛出Counter and non-counter mutations cannot exist in the same batch。带条件的 BATCH 不能跨表抛出BATCH with conditions cannot span multiple tables执行阶段还会校验不能跨分区见 cql3/statements/batch_statement.cc。批处理超时批处理的超时由其类型决定COUNTER批处理使用counter_write_timeout其余类型使用write_timeout见 cql3/statements/batch_statement.cc 的timeout_for_type()。若USING中显式设置了TIMEOUT则优先采用该值cql3/statements/batch_statement.cc。LOGGED默认与 UNLOGGED 批处理LOGGED 批处理与批处理日志默认情况下ScyllaDB 使用批处理日志batch log来保证批处理中的操作要么全部最终完成要么一个也不完成。需要强调的是这种隔离性仅限同一分区内——跨分区的 LOGGED 批处理虽然通过批处理日志保证最终全部完成或全部回滚但操作之间并不具备事务隔离。批处理日志的实现在 db/batchlog_manager.cc 中批处理写入时先在system.batchlog表中记录一条包含所有 mutation 的日志项get_batchlog_mutation_for随后将实际 mutation 发送到各副本副本投递完成后删除日志项get_batchlog_delete_mutation。日志项按written_at时间散列到 256 个批处理日志分片batchlog_shard_bits 8见 db/batchlog_manager.cc并支持 v1/v2 两种 schema 版本及在线迁移maybe_migrate_v1_to_v2。后台的batchlog_replay_loop()会周期性扫描尚未完成的日志项并重新投递do_batch_log_replay从而保证最终一致性当副本返回失败时日志项会被标记为failed_replay阶段以便后续重试。多分区批处理的性能代价与 UNLOGGED 选项当批处理跨越多个分区时批处理日志会带来明显的性能开销。如果不想承担这一代价可以使用UNLOGGED选项跳过批处理日志。代价是使用UNLOGGED后失败的批处理可能只被部分应用partly applied。BEGIN UNLOGGED BATCH UPDATE users SET password ps22dhds WHERE userid user3; INSERT INTO users (userid, password) VALUES (user4, chngem3c); APPLY BATCH;源码中的三种原子性路径cql3/statements/batch_statement.cc 的execute_without_conditions()清晰地展示了三种情形类型非LOGGED即UNLOGGEDmutate_atomic false直接非原子写入并累计batches_pure_unlogged统计。类型为LOGGED且产生的 mutation 数量多于 1mutate_atomic true走批处理日志路径累计batches_pure_logged统计。类型为LOGGED但 mutation 数量只有 1单分区优化自动降级为非原子写入mutate_atomic false累计batches_unlogged_from_logged统计——这正是文档所述单分区的 LOGGED 批处理会被转换为 UNLOGGED 批处理优化在实现层面的体现。所有路径最终都经由qp.proxy().mutate_with_triggers(...)提交给存储代理执行。批处理大小阈值配置ScyllaDB 会对批处理大小做守卫guardrail相关阈值配置在 conf/scylla.yaml 中参数定义于 db/config.hh# Log WARN on any batch size exceeding this value. 128 kiB per batch by default. # Caution should be taken on increasing the size of this threshold as it can lead to node instability. batch_size_warn_threshold_in_kb: 128 # Fail any multiple-partition batch exceeding this value. 1 MiB (8x warn threshold) by default. batch_size_fail_threshold_in_kb: 1024batch_size_warn_threshold_in_kb默认 128 KiB超过该值记录 WARN 日志。batch_size_fail_threshold_in_kb默认 1024 KiB即 1 MiB为警告阈值的 8 倍超过该值的多分区批处理直接失败。在 cql3/statements/batch_statement.cc 的verify_batch_size()中只有当批处理产生的 mutation 数量大于 1 时才做检查累计各 mutation 分区的外部内存占用若超过batch_size_fail_threshold_in_kb则抛出Batch too large异常若超过警告阈值则记录包含具体大小与超出量的 WARN 日志。对应测试可见 test/cqlpy/test_batch.py构造超过 1025 KiB 的批处理并断言抛出InvalidRequest错误信息为Batch too large。COUNTER 批处理计数器counter更新与其他更新不同不是幂等的。因此批处理计数器更新时必须使用COUNTER选项BEGIN COUNTER BATCH UPDATE click_stats SET clicks clicks 1 WHERE page_id 1; UPDATE click_stats SET clicks clicks 1 WHERE page_id 2; APPLY BATCH;COUNTER 批处理受以下限制来自源码校验与文档COUNTER 批处理中不能混合非计数器语句反之亦然cql3/statements/batch_statement.cc。LOGGED 批处理中不能包含计数器语句cql3/statements/batch_statement.cc。COUNTER 批处理不支持自定义时间戳cql3/statements/batch_statement.cc。同一批处理中不能混合原始raw与常规计数器 shard 写cql3/statements/batch_statement.cc。强一致性表不支持 COUNTER 批处理见下文。强一致性表中的 BATCH当 BATCH 针对强一致性表strongly consistent table通过 Raft 共识协议提供线性化保证的表时行为与普通表不同批处理中所有语句必须针对同一分区。执行阶段在 cql3/statements/strong_consistency/batch_statement.cc 中逐条构建分区键并校验每条语句必须恰好定位单个分区否则抛出Each statement in a strongly consistent batch must target a single partition且所有语句的分区键必须相同否则抛出All statements in a strongly consistent batch must target the same partition校验阶段还要求所有语句针对同一张表cql3/statements/strong_consistency/batch_statement.cc。mutation 被合并为单个 mutation并通过 Raft 原子提交在 cql3/statements/strong_consistency/batch_statement.cc 中协调器把批处理内各语句产生的 mutation 依次apply合并为单个 mutation再调用强一致性协调器的mutate()经由 Raft 提交最终获得原子的 all-or-nothing 语义。LOGGED 与 UNLOGGED 在此场景下没有区别原子性始终由底层 Raft 共识协议保证而非批处理日志。两种写法都允许效果相同。COUNTER 批处理不支持强一致性表校验阶段直接抛出Counter batches are not supported with strongly consistent tablescql3/statements/strong_consistency/batch_statement.cc。此外从 cql3/statements/batch_statement.cc 可以看到语句在 prepare 阶段即根据 keyspace 是否启用强一致性分流若目标 keyspace 是强一致性的则构造strong_consistency::batch_statement否则构造普通的cql3::statements::batch_statement。条件批处理LWT BATCH尽管原文档未展开但源码揭示了 BATCH 与轻量级事务LWT结合的一个重要细节若批处理中任意语句带有IF条件子句则整个批处理被视为 CASCompare-And-Set批处理_has_conditions标记见 cql3/statements/batch_statement.hh执行时走 Paxos 路径cql3/statements/batch_statement.cc。CAS 批处理有如下约束与行为不能跨表校验阶段抛出BATCH with conditions cannot span multiple tables执行阶段也不能跨分区抛出BATCH with conditions cannot span multiple partitions。不能自定义时间戳Cannot provide custom timestamp for conditional BATCH。结果集固定包含[applied]列及所有主键列见build_cas_result_set_metadata()cql3/statements/batch_statement.cc与 Cassandra 不同ScyllaDB 无论 CAS 成功与否都返回完整列结果集方便客户端统一 prepare。统计指标cas_batches与statements_in_cas_batches会相应累加。实战建议与最佳实践综合文档与源码使用 BATCH 时应遵循以下原则优先单分区批处理BATCH 最擅长的场景是把针对同一分区键的多次写入合并成一次请求。这既减少网络往返又能获得真正的原子性且 LOGGED 批处理还会自动降级为 UNLOGGED 以消除批处理日志开销。跨分区时权衡原子性与性能跨多分区的 LOGGED 批处理依赖批处理日志存在明显的性能代价UNLOGGED可跳过日志但允许部分应用。只有当你确实需要最终全部完成或全部不完成的保证时才使用 LOGGED 跨分区批处理。不要在批处理中混合计数器与非计数器语句计数器更新必须使用BEGIN COUNTER BATCH。控制批处理大小默认 128 KiB 以上产生 WARN、1024 KiB 以上直接失败多分区批处理。若确有大批量写入需求应分解为更小的批处理并谨慎调整 conf/scylla.yaml 中的阈值——注释明确警告增大阈值可能导致节点不稳定。强一致性表上的 BATCH 必须单分区且 COUNTER 批处理不被支持此时 LOGGED/UNLOGGED 的选择无关紧要原子性由 Raft 保证。不要将 BATCH 当作 SQL 事务使用它没有完整的隔离语义跨分区操作尤其如此需要可序列化保证时应结合唯一写入时间戳与轻量级事务LWT使用。延伸阅读docs/cql/dml/batch.rst本文依据的官方 BATCH 参考文档docs/cql/dml.rst更新参数TIMESTAMP/TTL/TIMEOUT与更新排序、时间戳冲突解决机制的完整说明docs/cql/index.rstCQL 参考总目录cql3/Cql.gBATCH 的 ANTLR 文法cql3/statements/batch_statement.ccBATCH 的校验、mutation 生成与执行逻辑cql3/statements/strong_consistency/batch_statement.cc强一致性表 BATCH 的 Raft 提交实现db/batchlog_manager.cc批处理日志的写入、重放与清理conf/scylla.yaml批处理大小阈值配置test/cqlpy/test_batch.py批处理大小阈值与错误处理的测试用例【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表