Secondary indexes用来过滤非primary key列的表查询。例如,一个表存储cyclist names 和ages。使用cyclist的last name作为主键,可能会有一个age字段的secondary index,使得能够允许根据年龄来查询。查询匹配非主键的列是反范式的,因为这样的查询需要从表中读取连续的数据片段。
如果一个表根据last names来存储数据,表将会分成多个parittions存储在不同的节点上。基于last names的某个特定范围的查询,比如所有的last name 为Matthews的cyclists,会从表中获取连续的rows,但是基于age的查询,比如哪些cyclists 28岁,会查询所有的节点。非primary keys的列在数据存储时是乱序的。这种根据非主键的查询会导致全partitions的扫描。扫描所有的partitions会导致非常高昂的读延迟,因此这是不好的。
可以为表的某一个列构建Secondary indexes。这些indexes是通过node上的后台进程构建出来的,这些indexes存储在每个节点本地的一个隐藏表中。如果在一个query中使用了一个secondary index,但没有指定partition key,这样的query同样会有高延迟,因为所有的节点都得查询(二级索引是在每个节点上单独生成的,存放了这个节点上,该列的index,因此如果查询时不指定partition key,还是要所有节点扫一遍)。若要想在查询的时候指定partition key 或者secondary index等参数,需要将Cassadra的查询选项置为 ALLOW FILTERING。这个选项不适用生产环境???。如果一个查询包括一个partiton key的条件和secondary index列条件,那这个查询的效率会很高,因为该查询会直接定位到a single node partition(上面去找)。
然而上面提到的方法并不能保证使用索引会效率很高,因此需要知道when and when not to use an index。以cyclists为例,可以使用age列上的index,但是更好的解决方案是创建一个materialized view或者新建一张以age为主键的表。
和关系型数据库一样,维护index需要时间和资源,因此要避免不必要的indexes。当一个列被更新的时候,它的index也需要更新。如果不断的更新某一行,那么memtable中可能会有该行过时的列值,Cassandra会将对应的过时的index entry删除;然后在compaction的时候,该index entry会被彻底清除。如果读操作在compaction之前看到了一个过时的index entry,reader线程会将它置为无效。
网友评论