读过本文才算真正了解Cassandra数据库( 二 )


在理解了Cassandra数据库的ROW-ORIENTED的稀疏矩阵存储之后,再来看看CQL语句的语法限制,那么这些限制就很容易理解 。例如:Select 语句,Where条件里,一定要送Partition Key(没有次索引的情况) 。如果不送,则语法上必须要添加ALLOW FILTERING 。
为什么是这样,刚才提到了,Partition Key决定了数据存在哪里,它像是一个指针,直接指向了这一行数据的物理位置 。ALLOW FILTERING表示什么,表示的是Cassandra数据库获得这条记录是通过筛选得来的,而不是通过直接定位得来的 。
类比一下传统数据库,Where 条件送Partition Key就好比通过HASH索引定位记录,ALLOW FILTERING就如同先做一次TABLE SCAN,读出大量记录再从记录里过滤出符合WHERE条件的 。再看看关于Clusterting Key的,CQL语法要求,范围查找、Order by一类的语法都需要使用Clusterting Key,这就十分好理解 。在定位的Partition Key确定了位置之后,同一Partition Key的数据,都是Clusterting Key有序存放的,那么通过在这个有序的Key列上,无论范围也好、排序也好,都不会需要数据库引擎真正去排序,这就好像在传统数据库里,ORDER BY的列,和某一个索引一致的情况下,执行计划里不会真的排序是一个道理 。
搞清楚了Cassandra的存储结构之后,我们来看Cassandra在某一个节点上怎么做增删改查 。无论Cassandra的多节点特点多么鲜明,在单一节点上面,数据的读写,永远才是数据库性能的根基 。节点再多,如果单节点上读写性能不行,那数据库终究是快不起来的 。所以这里我们来看一下,Cassandra是怎么样读写数据的 。
先翻译《Cassandra The Definitive Guide》一段话 。“在Cassandra中,写入数据非常快,因为它的memtables和SSTables设计,使它插入时,不需要执行磁盘读取或搜索,这些减慢数据库速度的操作 。Cassandra中的所有写入都是追加形态的 。”
我们看一下Cassandra的写入步骤,来解读它的写入优势 。

读过本文才算真正了解Cassandra数据库

文章插图
第一步,写Commit Logs 。这个步骤完全不是什么新发明 。我觉得它和传统数据库的REDO Log几乎是一样的 。无论是什么数据库,这个Log的写入,都是追加形态的 。但是,注意看这个图,Commit Logs直接写在硬盘上,我认为这个描述并不准确 。无论时传统数据库的REDO LOG还是Cassandra 的Commit Logs,它都是先到内存,再FLUSH到磁盘上的 。而FLUSH的策略是由一些参数决定的,比如commitlog_sync 。这和传统数据库非常相似,这里不展开来讨论,只需要认识到一点,FLUSH的动作频率越高,系统奔溃时丢失的数据越少,同时损失部分数据插入性能 。就像MySQL数据库的参数Innodb_flush_log_at_trx_commit=1时,Mysql是最安全,但是也是最慢的 。
读过本文才算真正了解Cassandra数据库

文章插图
第二步,Add to memtable,这是关键的一步,Cassandra的这一步是完完全全的内存动作 。而若是传统的数据库,则大约需要做这么几个动作:
  • 逐层搜索索引,若这个索引块不在DATA BUFFER里,触发磁盘IO 。
  • 通过索引定位数据块,若数据块不在DATA BUFFER里,触发磁盘IO 。
  • 修改索引块,修改数据块,如果修改并发量大时,可能产生锁等 。
当然,若是像Oracle数据库那样的堆表设计,纯粹的INSERT动作在b的IO触发可能性要少一点,但是在UPDATE(Cassandra中也是Insert)场景下,这些开销都是不可少的 。Cassandra为什么可以在Memtable上纯粹的做追加写入,这个Cassandra记录的Timestamp概念是分不开的,即无论你写入多少次,数据库只会以最新Timestamp的记录为准 。这样就不需要去对记录资源上锁 。这样的设计,不要说没有锁冲突了,就连去把需要上锁的记录找出来的开销都省了,快就快在这个地方 。
但是,这个快是有代价的,那就是数据的一致性 。比如一个简单的需求,在数据写入之前,需要看看这条数据是不是存在,如果存在了就不能插入(CQL的IF NOT EXIST语法),或者UPDATE需要看数据条件(WHERE IF Column = ‘*’)。一旦这种带条件CQL使用,那可以推断,上面的这些优势,也就不存在了 。
看第三步,如果这行数据在Row Caches里,使它失效 。注意这个地方,Row Caches里的记录是不改的 。那么Row Caches的使用场景,只有特别热点的数据读取的时候使用,它并不适合高并发热点数据修改的场景 。
常规交易,做完这三步就返回成功了,不需要等待Memtable的内容落盘 。换句话说,直接影响交易性能的步骤,结束了 。这和传统数据库也没有太大的差别 。那么接下来的步骤,就不直接影响数据库的写入能力 。


推荐阅读