【问题标题】:why Cassandra need soo much available disk space for little data?为什么 Cassandra 需要这么多可用磁盘空间来存储少量数据?
【发布时间】:2020-06-08 06:04:12
【问题描述】:

我是 Cassandra 的新手/初学者。我对 Cassandra 的工作原理进行了一些研究 (https://www.scnsoft.com/blog/cassandra-performance),但我遇到了一种情况。

我有 2 个总和为 384 MB 的 CSV 和一个 Win10 虚拟机,几乎有 10 GB 的可用存储空间。我的目标是使用 IntelliJ 的 spark/scala 将 384 MB 的 CSV(7.496.735 行)存储在 Cassandra 的单个表中(所有内容都在同一个单节点虚拟机中)。我想我会消耗大约 200-400 MB 的存储空间,但实际情况却大不相同。在由于缺少磁盘而失败之前,它消耗了所有 10 GB 的磁盘。我认为“这一定是复制因素”,但它不能像键空间那样创建:

CREATE KEYSPACE IF NOT EXISTS testkeyspace WITH REPLICATION = { '类':'SimpleStrategy','replication_factor':1 } AND DURABLE_WRITES = true ;

当计算存储的行数时(它会永远持续下去,它自己在控制台上进行了几次操作),它设法保存:1.767.450 行。

第二天我意识到它“释放”了 6.38 GB 的磁盘。

我的问题是:

为什么 Cassandra 需要如此多的可用磁盘空间来存储这么少的数据(最初是 10 GB,后来是 3.5 GB 来存储不到 0.5 GB 的原始数据)?

为什么它后来释放了磁盘空间(本应使用 6.38 GB)?

最后,如何从 spark/scala 成功地将 CSV 数据存储在 Cassandra 中?

编写代码为:

val spark_cassandra = cassandra_session()
cassandra_write(spark_cassandra, joined_df_unique, "joined_df_unique", "testkeyspace")

def cassandra_write( spark_cassandra : SparkSession, df : DataFrame , df_name : String, keyspace : String )  = {
    import com.datastax.spark.connector._
    import com.datastax.spark.connector.cql.CassandraConnector
    import org.apache.spark.sql.cassandra._

    val sparkContext = spark_cassandra.sparkContext
    val connector = CassandraConnector(sparkContext.getConf)

    df.createCassandraTable(keyspace,df_name) //, writeConf = writeConf)
    df.write.cassandraFormat(df_name,keyspace).mode(SaveMode.Append).save()

  }

def cassandra_session()  :  SparkSession = {

    val spark_cassandra = org.apache.spark.sql.SparkSession
      .builder()
      .master("local[*]")
      .config("spark.cassandra.connection.host", "localhost")
      .appName("Spark Cassandra Connector Example")
      .getOrCreate()

    spark_cassandra
  }

 // ("com.datastax.spark" %% "spark-cassandra-connector" % "2.4.3")

对不起,如果这太基本了,这是我第一次将 fon spark/scala 存储到 Cassandra。提前致谢。

【问题讨论】:

  • 好吧,看起来每 1000 行,您的查询也返回大约 13000 个墓碑。这告诉我超过 90% 的磁盘被已删除的数据占用。这是我要解决的第一个问题。

标签: scala apache-spark cassandra


【解决方案1】:

Cassandra 将数据作为不可变的 SSTable 存储在磁盘上(每个 SSTable 由几个文件组成)。 SSTables 的不变性解决了分布式系统固有的某些问题,这里我就不赘述了。

不变性的结果是,当您更新或删除一个值时,您只需写入新值(或者在删除的情况下,您编写一个墓碑,其实质上是“该值在某某某时被删除时间”)。 UPDATE 本质上是另一个 INSERT,而 DELETE 只是一个非常特殊的 INSERT。

  • 在时间 0,为键“A”插入值 1 => 将包含将 1 与“A”相关联的时间戳 0 记录的 SSTable 写入磁盘
  • 稍后n (n > 0),将键“A”更新为值为 2 => 一个包含时间戳 n的 SSTable > 将 2 与“A”相关联的内容写入磁盘(之前在时间 0 将 1 与“A”相关联的 SSTable 仍保留在磁盘上)
  • 在时间 n 之后,读取“A”的值将扫描 SSTable,查看与“A”关联的值 1 和 2,然后选择后面的值,即值 2
  • 稍后 m (m > n > 0),删除键 "A" => 一个包含时间戳的 SSTable m 将带有“A”的 tombstone 写入磁盘(之前的两个 SSTables 保留)

这有点简化,但结果是如果所有 INSERT 消耗了 x 个字节的磁盘,在运行 y UPDATE 或 DELETE 查询之后,您的总磁盘消耗可能不小于 (1 + y) * x

Cassandra 中有一个压缩过程,在我们的场景中,它最终会将三个具有“A”值(包括墓碑)的 SSTable 组合成一个只有最后一个值(即墓碑)为“A”的 SSTable,之后,最终从 SSTables 中删除任何“A”痕迹(请注意,在集群中,墓碑不会在集群周围一直传播,导致被删除的数据被复活为“僵尸”并不是闻所未闻的”)。根据使用的压缩策略和写入量,在回收任何空间之前可能会消耗大量额外的磁盘空间:甚至有可能从不回收空间的压缩策略(例如 TimeWindowCompaction ,在时间序列用例中很常见)。

值得注意的是,读取太多(默认,IIRC,为 100k)墓碑将无法返回任何数据;对于 DELETE 繁重的工作负载,这应该是另一个考虑因素。

如果您反复更新/删除相同的键,您的磁盘消耗将无限增长,除非压缩能够跟上您的写入。

【讨论】:

    猜你喜欢
    • 2019-06-10
    • 2011-05-12
    • 2018-08-20
    • 1970-01-01
    • 2015-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    相关资源
    最近更新 更多