微服务之-Cassandra-之三

83 阅读3分钟

书接上文 - 微服务实战之 Cassandra 之二

Cassandra 没有行级锁,在并发写入数据的时候,会有可能冲突,所以Cassandra 遵循“Last Write Wins”策略。

使用的时间戳可以手动设置,但默认情况下由请求者 see Datastax Java Driver docs 在客户端设置。写入到达的顺序无关紧要。如果写 A 的时间戳比写 B 早,那么它总是会被写 B 覆盖。唯一不明确的情况是时间戳完全匹配。在这种情况下,较大的值获胜。

最终一致的部分是:

  • 假设 A 的时间戳比 B早
  • 如果 A 到达副本 1,B 到达副本 2,则正确的状态是 B
  • 副本 1 将响应 A,直到它从副本 2 收到有关 B 的信息
  • 当 B 被复制时,副本 1 也会响应 B。 大多数用例都涉及不在 Cassandra 中存储状态,因此不会出现此类问题。

设置你的一致性级别为 LOCAL_QUARM

www.ecyrd.com/cassandraca…

以一个例子来说明 Cassandra 的数据存储方式

CREATE TABLE pool_usage (
  domain_name text,
  report_time timestamp,
  pool_name text,
  pool_usage text,
  PRIMARY KEY (domain_name, report_time)
) WITH CLUSTERING ORDER BY (report_time DESC);

Cassandra 作为一个数据存储系统,我们存储数据在其中,期望能够在随后读出来,这是一个存储系统必需要保证的。可是为什么在实践中,常常会出现数据写进去,却读不出来呢?

让我们先看看一个实际的例子


test@cqlsh> help

insert into data.person (anum, balance) VALUES ( 880, 200) USING TIMESTAMP 1502356880123456;

CREATE KEYSPACE walter_apjc WITH replication = {'class': 'NetworkTopologyStrategy', 'HF1': '3', 'SZ1': '3'} AND durable_writes = true;

参考资料

CQL 2.0 use timestamp in server-side for insert / delete / update operation

CQL 3.0 use timestamp in client-side for insert / delete / update operation

在CQL V3中引入的, issues.apache.org/jira/browse…, 原话是:

···

I continue to believe that in many case it makes somewhat more sense to have the default timestamp provided by the client (this is a necessary condition for true idempotent retries in particular). I'm absolutely fine making that optional and leaving server-side generated timestamps by default, but since client can already provide timestamp in query string anyway, I don't see a big deal in making it easier for client driver to control that without messing with the query string.

···

官方文档也有一段 描述, docs.datastax.com/en/devapp/d…

Without client timestamps, the client is at the whim of timestamps assigned by coordinating nodes. Coordinating nodes assign timestamps based on their internal system clock. It is difficult to keep the different nodes system clock synchronized in a distributed system. Each node is subject to clock drifts ranging from tens of milliseconds to seconds, even when the nodes use NTP or other clock synchronization software.

Cassandra Java Driver

image.png

public static Cluster getCluster(CassandraProperties props, CassandraConnectionConfig connectionConfig) {

    String[] contactPoints = props.getCassandraHostAddress().split(",");

    PercentileTracker percentileTracker  = null;

    if(connectionConfig == null) {

        connectionConfig = CassandraConnectionConfig.defaultInstance();

    }

    ProtocolOptions.Compression compression = ProtocolOptions.Compression.NONE;

    if (connectionConfig.isCompressionEnabled()) {

        compression = ProtocolOptions.Compression.LZ4;

    }

    try {

        Cluster.Builder builder = Cluster.builder()

                .withCredentials(props.getCassandraUsername(), props.getCassandraPassword())

                .withLoadBalancingPolicy(getLoadBalancingPolicy(props))

                .withRetryPolicy(new LoggingRetryPolicy(getRetryPolicy(props)))

                .withoutJMXReporting()

                .withPort(props.getCassandraPort())

                .withPoolingOptions(getPoolingOptions(props, connectionConfig))

                .withSocketOptions(getSocketOptions(props))

                .withQueryOptions(getQueryOptions(props))

                .withSpeculativeExecutionPolicy(getSpeculativeExceutionPolicy(percentileTracker, connectionConfig))

                .withTimestampGenerator(ServerSideTimestampGenerator.INSTANCE)

                .withCompression(compression);

        addContactPoints(contactPoints, builder);

        ProtocolVersion protocolVersion = getProtocolVersionIfNeeded(props);

        if (protocolVersion != null){

            builder.withProtocolVersion(protocolVersion);

        }

        SSLOptions sslOptions = getSSLOptions(connectionConfig);

        if (sslOptions != null) {

            builder.withSSL(sslOptions);

        }

        Cluster cluster =  builder.build();

        return cluster;

    } catch (DriverException e) {

        throw new CassandraException("Error creating cluster connection", e);

    }

}

如何在 Java Driver 中用 Client timestamp

docs.datastax.com/en/develope…

Quick overview

Defines the order in which mutations are applied on the server. Ways to set it (by order of precedence, higher priority first):

  • USING TIMESTAMP in the query string.

  • programmatically with Statement.setQueryTimestamp().

  • timestamp generator: advanced.timestamp-generator in the configuration. Defaults to session-wide monotonic, also available: per-thread monotonic, server-side, or write your own.

  • if the generator didn’t set it, assigned server-side.

  1. Using timestamp

session.execute("INSERT INTO my_table(c1, c2) values (1, 1) " +

    "USING TIMESTAMP 1432815430948040");

Reference

issues.apache.org/jira/browse…

docs.datastax.com/en/devapp/d…

docs.datastax.com/en/develope…

www.allthingsdistributed.com/2007/10/ama…

www.datastax.com/blog/2013/0…