基于SCRAM-SHA256实现的Cassandra安全认证插件

Cassandra提供认证机制,保证访问安全,但是默认的PasswordAuthenticator是简单的用户名密码认证,客户端在连接后传输用户名密码,服务器确认有效即认证通过。但是在认证的过程中直接传输的是明文密码,有被抓包泄漏的风险。 Cassandra服务端和JAVA客户端本身都支持SASL扩展 https://tools.ietf.org/html/rfc4422 SCRAM-SHA256是SASL的一种实现方式,参考 https://wiki.tools.ietf.org/html/rfc5802 https://tools.ietf.org/html/rfc7677 使用SCRAM-SHA256认证,仅通过抓包是无法破解密码的。 基于SCRAM-SHA256标准,我实现了一个cassandra的插件,放在了github上 https://github.com/johnyannj/cassandra-secure-plugin 欢迎使用

2018年10月14日 · 1 分钟

关于level压缩策略的level0问题

Cassandra一开始是要写commitlog,当commitlog写到一定大小就会刷到一个sstable文件,再加上对于cassandra,删除也是一种写,这样下去sstable文件会越来越多。必须有一种机制来合并这些文件,并删除墓碑(标记为删除的记录),这种机制叫做compaction,先翻译为压缩。既然要合并文件,就要有合并策略。cassandra一开始只有size模式的压缩策略。后来增加了level压缩。 level压缩提高了读的性能,但是level压缩相比较size压缩更慢,因为它是要保证每个level都有一定数量的文件,新产生的文件都是level 0的状态,同时在执行的压缩任务是有限制的,当几个高level的文件在压缩的时候,可能导致level0的文件堆积。 level压缩需要保证低级别的level的文件较少,是为了提高查询的效率。 为了避免level0的文件因为大量写入而得不到压缩。cassandra采取了一种策略,就是level 0文件数目超过一定限制(默认32),就在level 0采用size压缩,通过合并快速减少 level 0文件数量,同时暂停高level的文件压缩。 这个设计在正常情况下是有好处的。但是当我们扩容一个节点的时候,新增节点的文件全部在level 0。 sstable level 12222 0 0 0 0 0 0 0 那么cassandra会的持续进行level 0的 size压缩。直到level 0的文件减少到32以下 sstable level 32 0 0 0 0 0 0 0 这样你会发现新扩节点会一开始产生一个超大文件,然后再拆分成个个小文件的现象。 问题是:如果你有6个500G的磁盘,而你的单节点数据是2T,那么你的节点会因为空间不足而挂掉。 解决这个问题有两种方法: 一种是磁盘做raid,搞成一个大磁盘。 一种是临时关闭level 0的size压缩,这又是cassandra的一个隐藏技能,在cassandra官方文档里你不会找到。就是启动的时候加禁用level 0 使用 size压缩的参数: ./cassandra -Dcassandra.disable_stcs_in_l0=true 注意这个参数从cassandra 2.0.10以后的版本才有。当解决了问题后建议把该参数还原。因为在 level 0采用size压缩,对于突发写入大量的数据的情况还是有好处的。 参考 https://issues-test.apache.org/jira/browse/CASSANDRA-6621

2018年5月6日 · 1 分钟

Cassandra压缩任务堆积如何处理?

当短时间内写入的数据太多,或者连续扩容多个节点,都有可能导致压缩任务堆积,压缩任务堆积会导致sstable太多,让该节点查询变慢,时延变大,一直累积下去,集群会变的很不稳定。解决方法如下: 加大压缩速度阈值 默认压缩阈值是16Mb/s,偏小,可以更改的大一点,这个参数是可以通过nodetool setcompactionthroughput xx进行修改,配置文件cassandra.yaml里的默认值(配置项是compaction_throughput_mb_per_sec),也建议修改到一个合适的值,否则某一天重启节点,又恢复到了默认值。 增加压缩线程 修改配置文件cassandra.yaml里的配置项concurrent_compactors,这个不能动态调整,需要重启生效,默认是2-8之间的一个值,取自数据盘和cpu个数的最小值,这个值一般不需要动,除非你是ssd的盘,可以适当的增加。 临时关闭gossip 如果只是其中一个节点压缩堆积,负载特别高,可以考虑先临时关闭这个节点的gossip,使用nodetool disablegossip命令。这样这个节点对于客户端而言就是DOWN的状态,客户端就不会发请求到该节点,但是集群内部之间通信正常,不会丢失数据。只要你不使用All一致性,是不会影响业务正常请求的。然后你可以把节点的压缩速度阈值调为0,也就是不限制速度,让它早点压缩完毕恢复正常。最后记得用enablegossip恢复。 临时忽略墓碑 这个方法不在cassandra的官方文档里,也没有出现在官方的changelist里。因为这是一个很危险的操作,墓碑不及时清除,会带来读操作性能问题。如果你确认短时间内不清除墓碑不会对你的业务场景产生影响,你可以尝试临时使用该方式,在压缩完成后恢复,不能长期使用。 cassandra在压缩的时候为了安全清除墓碑,会查找多个sstable文件,当压缩堆积,sstable很多的时候,压缩会变的相当缓慢。所以官方增加了这么一个彩蛋形式的配置项。这个配置项是一个环境变量,在启动的时候指定 ./cassandra -Dcassandra.never_purge_tombstones=true 注意这个配置项在2.1.15版本以后才有。

2018年3月7日 · 1 分钟

关于Cassandra的分段Repair

为什么要Repair Repair对Cassandra集群是极为重要的,因为频繁的数据删除以及机器Down掉(尽管有Hinted Handoff机制)都会可能导致数据不一致(多个副本之间)。在Cassandra日常维护中,我们要例行对集群进行Repair操作,使用nodetool的Repair命令。 Repair原理 Cassandra在Repair的操作分两个步骤: 第一: 创建Merkle tree Merkle tree是一个二叉树,二叉树最底层是要比较的数据块的hash值,父节点是两个子节点的hash值(=hash(hash1+hash2))。二叉树的高度是15,也就是说最底层有2^15=32768个叶子节点,对应有32768个数据块,如下图所示。 计算Merkle Tree的过程需要依赖磁盘IO。为了不影响业务,你可以限制压缩阈值(nodetool setcompactionthroughput),因为这个过程被称作validation compaction,体现在压缩任务里。 第二:比较Merkle Tree,找出差异进行数据传输。 为每个副本创建Merkle Tree以后,副本之间只要通过比较最顶端的hash是否一致,然后一层层比较下来,就可以找到不一致的那个数据库,然后进行数据传输进行修复即可。 合理分段Repair 上面的修复过程有个问题,就是Merkle Tree是存储在内存里的,所以Cassandra对高度进行了限制,只能有15层,数据只能分为32768数据块。那么这就限制了Merkle Tree的精度,假设一个节点有10万个分区key,每个数据块大约有30个,假设其中有1条数据不一致,那至少要传输30个分区key的数据。这是很浪费集群带宽和修复时间的(修复需要在gc_grace_seconds周期内完成,防止删掉的数据又出现) Cassandra的nodetool repair提供了分段Repair的参数,-st -et分别表示token段的范围,假设我们每次repair的数据正好有32768个分区key,那么我们就可以进行精确的修复,减少不必要的传输。当我们把所有的token小段repair完毕,就相当与我们把所有数据进行了repair。 那么问题来了,怎么样对token段进行细分呢? 每个cassandra节点都有个表叫 system.size_estimates(好像是从2.1.4版本开始),在里面记录了每个表在每个token段上大约有多少分区key(partitions_count)以及每个分区key的大小(mean_partition_size) CREATE TABLE system.size_estimates ( keyspace_name text, table_name text, range_start text, range_end text, mean_partition_size bigint, partitions_count bigint, PRIMARY KEY (keyspace_name, table_name, range_start, range_end) ) 你可以遍历所有的token段进行repair,遇到分区key较多的token段(大于32768),继续细分成多个子token段进行repair。

2018年2月25日 · 1 分钟

Cassandra的数据分布和副本(一致性hash原理)

一致性hash设计出来的目的是: 根据数据的hash值把数据分布在n个节点上,当新增一个节点或者删除一个节点后根据算法重新计算,可以保证大部分数据都分布在原来节点上,只需要移动少部分数据即可。 再具体一点,当删除一个节点,只要把属于这个节点上的数据移动到其它节点上,当增加一个节点,只要从其它节点上把属于这个节点的数据自动过来。而保持不动的节点之间不需要数据移动。 下面是具体原理: Cassandra的对数据key的hash值范围是long的最小值到long的最大值。每个节点默认负责这个单位内的256个范围。我们这里用4个范围代替。首先我们用一个圆(hash环)来表示整个hash范围。然后每个节点随机取4个点,假设我们当前有三个节点(A,B,C),如下图所示: 整个hash环就被分割为了12段,顺时针去看,A和B之间的hash段就属于A,B和C之间的hash段就属于B,以此类推。 一条数据的分区key的hash值落在哪个段里,就存到对应的机器上。 现在看如果要增加一个节点。我们同样在圆上增加随机的4个点。 ​这样整个圆被分割成了16个段,同样顺时针看,我们发现大多数分段仍然属于原来的节点,只有四个小段属于了新节点D,也就是说只要移动这四个小段的数据到新节点D上,我们就完成了数据的再均衡,完成了节点扩容。 同样的如果用想退役D节点,只要把对应的分段的数据再挪回去,就完成了缩容。而大部分数据不需要移动。 这个算法就叫做一致性hash算法。

2018年2月4日 · 1 分钟

Cassandra节点间通信协议(Gossip协议)

Gossip协议是一个点对点协议,Cassandra用于两个节点间相互交换他们的状态信息,以及他们所知道的其它节点的信息。Gossip线程每秒执行一次随机和集群内其它三个节点进行信息交换。交换的信息包括他们自己的信息,以及他们知道的其他节点的信息,所以所有节点很快就可以得到整个集群节点的信息。每个交换的gossip信息都有一个版本号,所以交换过程中较新的信息就会覆盖老的信息。 为了避免通信异常,请确保每个节点的种子节点列表都一样,这对于一个节点的第一次启动很重要。通常一个几点会通过gossip记住所有后面启动的节点。种子节点的设计除了引导新加入集群的新节点启动gossip进程以外,没有其它目的。种子节点不是单点故障,除了引导新节点启动外,没有任何其它特殊目的。 注意:在多数据中心的集群里,种子列表应该包含每个数据中心的至少一个节点(建议每个数据中心多于一个节点,用于容错),因为节点启动的时候还需要和另外数据中心的节点进行通信。不推荐把每个节点都作为种子节点,因为这增加了维护成本还降低了gossip性能。gossip方面的优化不是很重要,但是还是建议使用一个较小的种子列表(每个数据中心三个节点差不多了) 集群内节点故障检测和恢复都是通过gossip实现的. 故障检测是本节点通过gossip状态和历史信息确定集群中另外一个节点是挂掉了还是恢复了的一种方法。Cassandra从而避免把客户端的请求路由到不可达的节点上。(Cassandra还可以根据动态的策略,避免把消息发送到负载高的节点上)。 Gossip线程可以直接或者间接的跟踪其它节点的状态。(就是说这些节点状态的信息,可能是对应节点直接发给它的,也有可能是通过别的节点转发过来的二手,三手信息)。 Cassandra并不是通过一个固定的阈值来判断一个节点挂掉了。而且有一套动态的检测机制来计算每个节点的阈值,考虑了网络、负载、历史状态等因素。在gossip信息交换的过程中,每个节点都记录了从其它节点获取到的消息的时间窗口信息。配置文件里的phi_convict_threshold配置项可以调节失败检测的灵敏度。这个值越小,一个节点被标记为DOWN状态的几率就越大,值越大反而减少一个节点因为瞬间故障被标记为Down状态的概率。大多数情况下这个值保持默认就可以。但是在亚马逊云E2上可以把它提高到10~12,(亚马逊云经常有网络拥塞)。对于不稳定的网络环境(比如E2),提高到10~12可以减少故障误判。不推荐这个值大于12或者小于5。 节点故障可能是多种原因造成的,比如硬件故障,网络中断。节点中断往往是临时性的,但可能持续很长时间。节点中断通常并不意味着它永久脱离集群,所以cassandra并不会自动把故障的节点从集群hash环中删除掉,其它节点会定时的尝试联系这个节点以判断它是否恢复。如果想是永久性的操作,管理员必须使用nodetool工具或者从opscenter上明确的添加或者删除某个节点。 当一个节点从中断中恢复,他可能错过了很多写操作。一旦一个节被检测为故障中断,数据的其它副本所在的节点会帮它记录错过的写操作(记作hints)一段时间,前提是这个功能已配置启用。不过一个节点挂掉的时间太久,超过了max_hint_window_in_ms配置的值(默认3小时),hints就不再记录。那些挂掉的节点也有可能存储了一些未送达的hints。所以在恢复一个挂掉很久的节点后,请执行repair操作。而且,你应该定期执行noodtool repair命令以保证节点间数据一致性。 在cassandra中,数据的分布和副本要一起看,数据通过一个个的表组织起来,由一个主键确定数据存储在哪个节点上。副本就是一行数据的多个拷贝,当数据第一次写入的时候,其实我可以称为一个副本。 影响数据副本分布的因素包括: 虚拟节点:用于数据和物理节点映射关系。 分区:对集群中的数据进行分区 副本策略:确定每行数据的副本数 告密者: 对集群中节点拓扑关系的一种定义,副本策略根据它来放置副本。 Cassandra采用一致性hash算法来计算数据如何在节点上分布。使用一致性hash的目的是就是当增加或者移动一个节点的时候,只要移动很小的数据就可以完成数据的再均衡。 每条数据的第一个主键叫分区主键,分区主键的hash值落在哪个节点内,这条数据就分布在哪个节点。其它副本就落在hash环上的下一个节点上。

2018年2月4日 · 1 分钟

Cassandra如何选择压缩策略

SSTable的压缩是Cassandra的重要设计之一,墓碑的删除,数据的合并都依赖压缩才能完成,目前Cassandra有四种压缩策略,其中的一种已经被废弃。 压缩除了解决墓碑等根本问题外,选择不同的压缩策略也影响你的读写性能和集群稳定性。 至于如何选择压缩策略,官方有一系列问句帮你决定: 你存储的是和时间序列有关的数据吗? 如果是的,那么最佳的压缩策略就是TWCS,如果不是请继续后面的问题。 你的表是读多写少,还是写多读少? 如果读是写的两倍以上,特别是随机读的场景,建议用LCS策略,如果读和写差不多,用LCS引起的性能缺失和带来的好处相比,可能并不划算了。注意LCS策略很容易被大量的写击垮的。 你的表里的数据更新频繁吗? LCS的一个好处就是让有关联的数据集中在一组SSTable文件里。如果你的数据更新不频繁甚至是不更新的,用STCS也可以达到这样的目的,而不会有LCS带来的性能牺牲。 你是否需要可预测的读写? 这一段比较难以理解,博主用白话描述:你是否需要服务级别的读,就是对读的tps和时延是都有要求。如果有的话,即便是你的读写比很小,还是建议用LCS,因为LCS可以控制SSTable的数量和大小,从而保证稳定的读时延,当然写性能就会受到影响,不过这一点你可以通过扩节点的方式解决~ 你的表是否有大量的batch提交? 对于批量读和写,STCS的性能要优于LCS。批量提交不会引起太多的碎片文件,所以LCS的好处体现不出来,而且大量的批处理可能会击垮LCS策略的表。 你的磁盘空间是否有限? 在磁盘利用效率方面LCS要比STCS好,它只需要相比存储的数据10%的额外冗余空间,而STCS和DTCS需要50%以上的空间。注意DTCS已经废弃了。 你的系统IO是否到达瓶颈? LCS比DTCS和STCS产生更多的密集IO操作,切换到LCS带来的额外IO开销可能会抵消它所带来的优势。 通过上述问题,你是否知道自己应该选择什么样的压缩策略了吗?不要盲目做决定,建议你在正式上线前创建三个节点,设置你选择的策略,使用cassandra-stress对你的系统做个压力测试。

2017年11月26日 · 1 分钟

Cassandra有关系统参数九点优化

Cassandra不同于普通的应用程序,它是分布式数据库,它要大口吃内存,吃磁盘,吃CPU,所以机器要进行特殊的配置,以适应其需要。 第一:使用最新的64位的jdk8的最新发布版本。 第二:时钟同步,开启NTP服务,cassandra是分布式存储,就靠时间戳解决数据冲突,所以始终必须同步 第三:TCP参数设置 在低带宽环境下,防火墙会检测闲置的连接并关闭,为了保护节点之间,或者多个DC节点之间的连接,建议如下配置系统参数 sudo sysctl -w net.ipv4.tcp_keepalive_time=60 net.ipv4.tcp_keepalive_probes=3 net.ipv4.tcp_keepalive_intvl=10 设置这个就是可以快速的发现底层的TCP连接是否已经关闭,它间隔60秒开始探测3次,每次探测间隔10秒,也就是说最多在60+3*10=90秒内就可以检测到连接被中断。 为了支撑上千个数据库连接,还建议修改以下参数 sudo sysctl -w net.core.rmem_max=16777216 net.core.wmem_max=16777216 net.core.rmem_default=16777216 net.core.wmem_default=16777216 net.core.optmem_max=40960 net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem=4096 65536 16777216 为了让参数永久生效,记得把它们写入系统配置文件/etc/sysctl.conf里 第四:禁用CPU动态跳频功能。 最近的linux系统增加了一个新特性,就是可以动态调整CPU频率,就是在机器低负载的时候,可以降低CPU频率,以达到降低功耗的目的。 这种动态调频功能会影响cassandra数据库的吞吐量。建议禁用,让CPU一直维持恒定的频率输出,尽管这很耗电,但是保证你的数据库的吞吐量。 禁用方式: for CPUFREQ in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor do [ -f $CPUFREQ ] || continue echo -n performance > $CPUFREQ done 第五:禁用zone_reclaim_mode 官方建议禁用,这个是关于多核CPU使用NUMA架构,分别访问内存,内存回收方面的一个参数 这个参数的解释,可以参考: http://linuxinsight.com/proc_sys_vm_zone_reclaim_mode.html 这里面有一句话,当你的机器用作文件服务器,或者你的大部分内存需要用于系统文件缓存的时候,你需要禁用这个功能。 我们的Cassandra就相当于文件服务器,它对IO是依赖的,它需要系统内存用于大量缓存DB文件。所以要禁用这个功能。 echo 0 > /proc/sys/vm/zone_reclaim_mode Cassandra官方描述了如果不禁用这个参数带来的后果: 1、随机CPU尖峰带来时延增加,吞吐量增加。 2、程序假死,什么也不做。 3、一些突然发生又消失的莫名异常。 4、重启机器,可能在一段时间内不再出现异常。 第六:资源限制放开。 cassandra会使用很多内存,很多连接,很多文件,所以一律放开。 <cassandra_user> - memlock unlimited <cassandra_user> - nofile 100000 <cassandra_user> - nproc 32768 <cassandra_user> - as unlimited ...

2017年11月21日 · 1 分钟

Cassandra的jvm内存设置多大合适

cassandra是java写的程序,java写的程序难以避免会遇到GC的问题,第一个问题就是jvm的内存设置多大合适,这往往是让人很纠结的事情。 以下来自官方建议: 首选是GC类型如何选择 推荐G1 GC有以下原因: 1、内存可以从14GB到64GB,G1在大内存上比CMS表现好,因为G1首选会扫描那些堆内区域包含垃圾对象比较多,并同时进行堆压缩,而CMS在执行GC的时候需要停止应用。 2、系统的负载是个变量,也就是说系统每个时间都在执行不同的线程操作。 3、CMS在java9以后就会被废弃 4、G1是很容易配置的 5、G1是一种自我优化的GC 6、你只要设置MAX_HEAP_SIZE就行了。 当然G1 GC这种分析会导致一定时延。 CMS通过在以下情形下被推荐使用: 1、你有足够的时间和专业知识来手动优化调试垃圾手机。 注意当数据库内存里保持了更多的系统元数据的时候,增加更大的内存给jvm,会因为GC而导致性能下降。 2、堆大小不超过14GB. 3、系统负载是固定的,也就是说集群一致在执行相同的工作。 4、环境需要更低的时延。 备注:不建议在java7下使用G1,因为G1在java7下有个bug关于类卸载的问题,在java7里PermGen会被一直填满,直到发生一个full GC。 其次是内存大小如何设置 你可能试图把java的内存设置为接近系统的RAM大小,显然这是不可能的,因为这会干扰操作系统的页面缓存的操作。操作系统把频繁访问的数据保存到内存里,也就是操作系统的页面缓存,这个是很有用的。适当调整操作系统的页面缓存通常比使用cassandra的row cache还好。 cassandra会基于以下公式,自动计算最堆内存(MAX_HEAP_SIZE): max(min(1/2RAM, 1024M), min(1/4RAM, 32G)) 对于生产环境,我们提供以下指导原则: 1、堆内存一般在操作系统内存的四分之一和二分之一之间 2、不要把所有的内存都给jvm,因为内存还要用于堆外缓存以及操作系统缓存。建议在优化GC的时候始终开启GC日志。 3、针对每个配置项的变化进行调整和测试 4、启用GC的并行处理,特别是使用cassandra企业版的索引检索。 5、GCInspector类的日志会对超过200ms的GC,打印日志。如果这样的GC频繁发生,或者需要很长的时间才能完成GC,那么表示当前压力已经超过GC能力范围,除了优化GC选项以外,其它的措施还包括扩容节点,降低缓存大小等。 6、如果是使用的G1 GC。Datastax官方建议最大内存MAX_HEAP_SIZE越大越好,可以到64GB. MAX_HEAP_SIZE的大小设置根据使用的GC类型而定: 1、对于超过8C256G内存的机器, 使用G1 GC建议MAX_HEAP_SIZE在14G到64GB之间 2、对于超过8C256G内存的机器, 使用CMS GC建议MAX_HEAP_SIZE不要超过14GB 3、其它较老的机器,配置典型值8G 对于CMS,你可能还要优化HEAP_NEWSIZE(新生代的堆大小) Cassandra的计算原则是 min(100M* cpu核心数, 1/4 MAX_HEAP_SIZE) 总之:HEAP_NEWSIZE越大停顿时间越长,HEAP_NEWSIZE越小可能更频繁,这个过程更加昂贵。 博主建议 不要再纠结,用jdk8,G1 GC,内存在1/4ram和1/2ran之间,越大越好,不要超过64G。

2017年11月19日 · 1 分钟

Mysql是如何做到安全登陆

首先Mysql的密码权限存储在mysql.user表中。我们不关注鉴权的部分,我们只关心身份认证,识别身份,后面的权限控制是很简单的事情。 在mysql.user表中有个authentication_string字段,存储的是密码的两次sha1值。 你可以用下面的语句,验证和mysql.user表中存储的是一致的。 select sha1(UNHEX(sha1(‘password’))) 以上就是服务端关于密码的存储,接下来是认证过程。 Mysql采用的是一种challenge/response(挑战-应答)的认证模式。 第一步:客户端连接服务器 第二步:服务器发送随机字符串challenge给客户端 第三步:客户端发送username+response给服务器 其中response=HEX(SHA1(password) ^ SHA1(challenge + SHA1(SHA1(password)))) 第四步:服务器验证response。 服务器存储了SHA1(SHA1(password))) 所以可以计算得到SHA1(challenge + SHA1(SHA1(password)))) 那么SHA1(password)=response^ SHA1(challenge + SHA1(SHA1(password)))) 最后再对SHA1(password)求一次sha1和存储的数据进行比对,一致表示认证成功。 我们分析它的安全性: 1、抓包可以得到response,但是每次认证服务器都会生成challenge,所以通过抓包无法构造登陆信息。 2、数据库内容被偷窥,数据库记录的是sha1(sha1(password)),不可以得到sha1(password)和明文密码,所以无法构造response,同样无法登陆。 当然如果被抓包同时数据库泄密,就可以得到sha1(password),就可以仿冒登陆了。 这种认证方式其实是有一个框架标准的,叫做SASL(Simple Authentication and Security Layer ),专门用于C/S模式下的用户名密码认证。原理就是服务器发送一个挑战字challenge给客户端,客户端返回的response证明自己拥有密码,从而完成认证的过程,整个过程不需要密码明文在网络上传输。 基于SASL协议有很多实现,mysql的就是模仿的CRAM-MD5协议,再比如SCRAM-SHA1协议,是mongdb、PostgreSQL 使用的认证方式。在JDK中专门有一套SASL的API,用于实现不同的SASL认证方式。

2017年11月11日 · 1 分钟

[翻译]关于Cassandra中的删除和墓碑(七)

单SSTable压实 单SSTable压实是在cassandra1.2引入的,是由Jonathan Ellis在CASSANDRA-3442最新提出来的: 在Size压实模式下,你可能会产生很大的SSTable,很少被压实,但是有很多过期的数据在里面,我们在这种情况下可能浪费了大量的磁盘空间。 就像我们上面提到的,压实的目的就是墓碑的剔除,在一定的场景下,压实操作并没有很好的剔除墓碑。不仅仅是这里提到的Size模式的压实(STCS),所有的压实模式都有这种情况。一些SSTable文件很久才会被压一次或者很长时间都会有重叠的SSTables(译者注:表示不能合并ROW)。这也是为什么,时至今天,每个压实模式都会有一堆的配置项用于调节墓碑的剔除。 tombstone_threshod: 这个配置项的作用就是Jonathan Ellis 在2011年提出的: 如果一个SSTable在它的元数据信息里保存它含有的ttl记录的统计数据,我们就可以当过期数据超过20%的时候,进行一次单SSatble压实操作。 所以当墓碑占有率超过这个值(默认等于0.2,也就是20%)的时候,这个选项就会触发一次单SStable压实。需要注意的是,那些真正可以被剔除的墓碑经常小于估值,因为计算这个墓碑占有率的时候,并未考虑gc_grace_secods参数。 tombstone_compaction_interval: 这个选项是在CASSANDRA-4781中被引入,目的是为了解决一个死循环问题,当一个SSTable文件的墓碑占有率达到了触发一次单SStable压实的操作,但是由于和别的SStable有重叠,导致无法清除墓碑。因为我们要删除一个文件的所有碎片,以防止僵尸数据产生。这种情况下一些SStable的压实操作可能无休止的进行下去。既然一个SStable里还有的墓碑率是个估值,那么我这个选项就用于限制两次单SStable压实操作的最小间隔时间,默认是1天。 unchecked_tombstone_compaction:是Paulo Motta在CASSANDRA-6563引入的。在这个问题单里他描述了单个SSTable压实的历史和他引入这个参数的原因,非常有趣。我是无法更好的去解释它。(译者注:意思是你可以直接去看问题单。) 注意此选项配置为true以后,就会出发一个SStable一天一次的压实(tombstone_compaction_interval默认),只要墓碑占有率(估计值)高于0.2(20%是墓碑,这个是tombstone_threshold默认值),最坏的情况就是即便是没有任何墓碑是可以被剔除的也会执行一次压实。 所以最好是多投入一些资源(多加一些机器?)希望让墓碑的剔除更好的进行。 推荐做法:当一个DC已经在删除墓碑上有麻烦了,立即使用下这个参数,应该是值得的。我在使用这个选项上,已经有一些很成功的经验以及一些不是很糟糕的经验。相反的也有一些很少场景,这个选项实际上并没有什么作用。我甚至有一次设置这个选项为true除非手动压缩。把一些磁盘已经100% 马上挂掉的机器恢复了正常。 要修改这些配置的时候,先查看表的描述。然后重新指定整个压实策略,以避免一些意外。假设我想修改tlp_lab库里tombstones表的压实参数,我会这样做: MacBook-Pro:~ alain$ echo "DESCRIBE TABLE tlp_lab.tombstones;" | cqlsh CREATE TABLE tlp_lab.tombstones ( fruit text, date text, crates set, PRIMARY KEY (fruit, date) ) WITH CLUSTERING ORDER BY (date ASC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'} AND comment = '' AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'} AND compression = {'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1.0 AND dclocal_read_repair_chance = 0.1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND read_repair_chance = 0.0 AND speculative_retry = '99PERCENTILE'; 然后我会复制这些压实选项,并修改它: ...

2017年6月9日 · 2 分钟

[翻译]关于Cassandra中的删除和墓碑(六)

墓碑清除 只有在local_delete_time + gc_grace_seconds以后墓碑才会在压实的时候完全清除掉。记住,这是假定所有节点都在gc_grace_seconds时间内repair过了,以保证墓碑在所有节点上都正确分布,这是为了防止删除的数据再出现,上面已经说过了。 gc_grace_seconds参数是墓碑在磁盘上存在的最短时间。我们需要保证所有的副本都收到了这个删除操作,并且写入了墓碑,从而避免僵尸数据的问题。为了达到这个目的,我们唯一的方法就是全量repair。在gc_grace_seconds以后,墓碑最终会请清除,如果其中一个节点没有写入这个墓碑,我们就会进入上面描述的数据又出现的境地。TTL没有影响,因为没有节点可以保留数据而错过ttl,它是一个原子操作,数据和ttl是一条记录。任何有数据的节点都知道什么时候必须删除数据。 另外,为了删除数据和墓碑,还有一些安全守则需要Cassandra节点必须遵守。我们需要一行数据或者一个分区key的所有的片段数据以及墓碑都要在同一个压实中。假设一个压实操作包含1-4个文件,如果一些数据在文件5上面,墓碑被清除后,我们仍需要留下一个标记数据(译者注:墓碑),表示文件5里的数据被清除了,否则文件5里的数据又会回来了(成为僵尸数据)。 这些条件有时候让删除墓碑成为一件很复杂的事情,它经常给Cassandra的使用者带来麻烦。墓碑不被清除意味着占用更多的磁盘,更慢的读,以及更多的repair工作,高概率的GC压力,更多的资源利用等等。当你的sstable的墓碑占到一个很高的的比率(90%的数据都是墓碑),读取一个值或者一段相关的数据会变的相当困难,存储的成本也越来越高。这些问题最终会导致磁盘空间耗尽。 很多使用情况下会导致数据删除(TTL或者delete操作),作为Cassandra的使用者我们必须克制,控制这些事情。 再次回到我们这个例子,我重启了这个节点在很多天以后(>10天,gc_grace_seconds的默认值)。Cassandra重新打开压实过的mb-14-big文件,它立马又进行了压实操作。 MacBook-Pro:tombstones alain$ grep ‘mb-14-big’ /Users/alain/.ccm/Cassa-3.7/node1/logs/system.log DEBUG [SSTableBatchOpen:1] 2016-06-28 15:56:17,947 SSTableReader.java:482 - Opening /Users/alain/.ccm/Cassa-3.7/node1/data/tlp_lab/tombstones-c379952033d311e6aa4261d6a7221ccb/mb-14-big (0.103KiB) DEBUG [CompactionExecutor:2] 2016-06-28 15:56:18,525 CompactionTask.java:150 - Compacting (166f61c0-3d38-11e6-bfe3-e9e451310a18) [/Users/alain/.ccm/Cassa-3.7/node1/data/tlp_lab/tombstones-c379952033d311e6aa4261d6a7221ccb/mb-14-big-Data.db:level=0, ] 此时,gc_grace_seconds已经过去了,墓碑有条件被清除了,所以所有的墓碑都被清除了,最后表里没有任何数据了,数据目录最终也是空的: MacBook-Pro:tombstones alain$ ll /Users/alain/.ccm/Cassa-3.7/node1/data/tlp_lab/tombstones-c379952033d311e6aa4261d6a7221ccb/ total 0 drwxr-xr-x 3 alain staff 102 Jun 28 15:56 . drwxr-xr-x 3 alain staff 102 Jun 16 20:25 .. MacBook-Pro:tombstones alain$ 如果墓碑在所有副本都都正确存在,我们就会有完全一致的删除操作,删除的数据就不会再出现。而且我们还可以释放一些磁盘空间,让其他数据的读变的更容易,尽管为了证明这个事情,我的例子有点傻,但最后这个表是完全变空了。 监控墓碑比率和到期时间 因为Cassandra的设计,当我们删除数据或者使用ttl的时候很正常的就产生了墓碑。当然这个是我们必须要控制的。 使用sstablemetadata我们可以指定一个sstable的墓碑占有率,以及一个大概的墓碑清除时间分布情况。 alain$ SSTablemetadata /Users/alain/.ccm/Cassa-3.7/node1/data/tlp_lab/tombstones-c379952033d311e6aa4261d6a7221ccb/mb-14-big-Data.db – Estimated droppable tombstones: 2.0 – Estimated tombstone drop times: 1466154851: 2 1466156036: 1 1466156332: 1 – ...

2017年3月24日 · 1 分钟

[翻译]关于Cassandra中的删除和墓碑(五)

减轻墓碑带来的麻烦 好了,现在我们已经明白为什么我们要用墓碑,我们对墓碑也有一个大致的了解了。现在让我们看看墓碑会引起哪些潜在的麻烦,我们可以采取哪些措施来减轻这些麻烦。 首先一个很显而易见的事情就是墓碑没有让数据被删掉,反而增加了存储。我们需要删除这些墓碑以腾出磁盘空间,并且限制读出无用数据的大小,以降低时延和提高资源利用率。这个事情就发生在接下来你看到的压实的过程。 压实(Compactions) 当我们读取某一行数据的时候,为了读取到这一行数据的所有片段,我们翻阅的SSTables阅读,读时延就越大。因此我们有必要把这些片段通过压实的过程把他们合并,以获得更低的读时延。这个过程包括把合适的目标也清除掉,如我所愿的持续释放可用的空间。 压实的过程是通过合并来自多个sstable的row片段,去删除满足一定条件的墓碑。有些条件是在表的schema中指定的,而且是可以优化可调节的,比如gc_grace_seconds参数,有些条件是cassandra内部的,代码里写死的,这是为了保证数据持久化和一致性。要保证没有参与当前压实的sstable(重叠sstables)里没有新的数据片段,这是防止墓碑被清掉以后,数据又出现成为僵尸数据的必要条件。 再看上面的例子,经过删除和flush以后,表数据目录大致如下: alain$ ll /Users/alain/.ccm/Cassa-3.7/node1/data/tlp_lab/tombstones-c379952033d311e6aa4261d6a7221ccb/ total 360 drwxr-xr-x 43 alain staff 1462 Jun 17 11:39 . drwxr-xr-x 3 alain staff 102 Jun 16 20:25 .. drwxr-xr-x 2 alain staff 68 Jun 16 17:05 backups -rw-r–r– 1 alain staff 43 Jun 17 11:13 mb-10-big-CompressionInfo.db -rw-r–r– 1 alain staff 43 Jun 17 11:13 mb-10-big-Data.db -rw-r–r– 1 alain staff 10 Jun 17 11:13 mb-10-big-Digest.crc32 -rw-r–r– 1 alain staff 16 Jun 17 11:13 mb-10-big-Filter.db -rw-r–r– 1 alain staff 9 Jun 17 11:13 mb-10-big-Index.db -rw-r–r– 1 alain staff 4701 Jun 17 11:13 mb-10-big-Statistics.db -rw-r–r– 1 alain staff 59 Jun 17 11:13 mb-10-big-Summary.db -rw-r–r– 1 alain staff 92 Jun 17 11:13 mb-10-big-TOC.txt -rw-r–r– 1 alain staff 43 Jun 17 11:33 mb-11-big-CompressionInfo.db -rw-r–r– 1 alain staff 53 Jun 17 11:33 mb-11-big-Data.db -rw-r–r– 1 alain staff 9 Jun 17 11:33 mb-11-big-Digest.crc32 -rw-r–r– 1 alain staff 16 Jun 17 11:33 mb-11-big-Filter.db -rw-r–r– 1 alain staff 9 Jun 17 11:33 mb-11-big-Index.db -rw-r–r– 1 alain staff 4611 Jun 17 11:33 mb-11-big-Statistics.db -rw-r–r– 1 alain staff 59 Jun 17 11:33 mb-11-big-Summary.db -rw-r–r– 1 alain staff 92 Jun 17 11:33 mb-11-big-TOC.txt -rw-r–r– 1 alain staff 43 Jun 17 11:33 mb-12-big-CompressionInfo.db -rw-r–r– 1 alain staff 42 Jun 17 11:33 mb-12-big-Data.db -rw-r–r– 1 alain staff 10 Jun 17 11:33 mb-12-big-Digest.crc32 -rw-r–r– 1 alain staff 16 Jun 17 11:33 mb-12-big-Filter.db -rw-r–r– 1 alain staff 9 Jun 17 11:33 mb-12-big-Index.db -rw-r–r– 1 alain staff 4611 Jun 17 11:33 mb-12-big-Statistics.db -rw-r–r– 1 alain staff 59 Jun 17 11:33 mb-12-big-Summary.db -rw-r–r– 1 alain staff 92 Jun 17 11:33 mb-12-big-TOC.txt -rw-r–r– 1 alain staff 43 Jun 17 11:39 mb-13-big-CompressionInfo.db -rw-r–r– 1 alain staff 32 Jun 17 11:39 mb-13-big-Data.db -rw-r–r– 1 alain staff 9 Jun 17 11:39 mb-13-big-Digest.crc32 -rw-r–r– 1 alain staff 16 Jun 17 11:39 mb-13-big-Filter.db -rw-r–r– 1 alain staff 11 Jun 17 11:39 mb-13-big-Index.db -rw-r–r– 1 alain staff 4591 Jun 17 11:39 mb-13-big-Statistics.db -rw-r–r– 1 alain staff 65 Jun 17 11:39 mb-13-big-Summary.db -rw-r–r– 1 alain staff 92 Jun 17 11:39 mb-13-big-TOC.txt -rw-r–r– 1 alain staff 43 Jun 17 11:12 mb-9-big-CompressionInfo.db -rw-r–r– 1 alain staff 127 Jun 17 11:12 mb-9-big-Data.db -rw-r–r– 1 alain staff 10 Jun 17 11:12 mb-9-big-Digest.crc32 -rw-r–r– 1 alain staff 16 Jun 17 11:12 mb-9-big-Filter.db -rw-r–r– 1 alain staff 20 Jun 17 11:12 mb-9-big-Index.db -rw-r–r– 1 alain staff 4740 Jun 17 11:12 mb-9-big-Statistics.db -rw-r–r– 1 alain staff 61 Jun 17 11:12 mb-9-big-Summary.db -rw-r–r– 1 alain staff 92 Jun 17 11:12 mb-9-big-TOC.txt ...

2017年3月24日 · 4 分钟

[翻译]关于Cassandra中的删除和墓碑(四)

让我们现在看看各种类型的删除: cell删除 在cassandra存储引擎里,一指定行里面的一列就叫做cell。 删除某一行的某一个cell如下: DELETE crates FROM tlp_lab.tombstones WHERE fruit=‘apple’ AND date =‘20160617’; 这一行的crates列就会显示为“null”: alain$ echo “SELECT * FROM tlp_lab.tombstones LIMIT 100;” | cqlsh fruit | date | crates ———+———-+—————– apple | 20160616 | {1, 2, 3, 4, 5} apple | 20160617 | null pickles | 20160616 | {6, 7, 8} (3 rows) 执行flush以后,我们会得到一个新的sstable在磁盘上: mb-6-big alain$ ll /Users/alain/.ccm/Cassa-3.7/node1/data/tlp_lab/tombstones-c379952033d311e6aa4261d6a7221ccb/ total 144 drwxr-xr-x 19 alain staff 646 Jun 16 21:12 . drwxr-xr-x 3 alain staff 102 Jun 16 20:25 .. drwxr-xr-x 2 alain staff 68 Jun 16 17:05 backups -rw-r–r– 1 alain staff 43 Jun 16 20:53 mb-5-big-CompressionInfo.db -rw-r–r– 1 alain staff 127 Jun 16 20:53 mb-5-big-Data.db -rw-r–r– 1 alain staff 10 Jun 16 20:53 mb-5-big-Digest.crc32 -rw-r–r– 1 alain staff 16 Jun 16 20:53 mb-5-big-Filter.db -rw-r–r– 1 alain staff 20 Jun 16 20:53 mb-5-big-Index.db -rw-r–r– 1 alain staff 4740 Jun 16 20:53 mb-5-big-Statistics.db -rw-r–r– 1 alain staff 61 Jun 16 20:53 mb-5-big-Summary.db -rw-r–r– 1 alain staff 92 Jun 16 20:53 mb-5-big-TOC.txt -rw-r–r– 1 alain staff 43 Jun 16 21:12 mb-6-big-CompressionInfo.db -rw-r–r– 1 alain staff 43 Jun 16 21:12 mb-6-big-Data.db -rw-r–r– 1 alain staff 10 Jun 16 21:12 mb-6-big-Digest.crc32 -rw-r–r– 1 alain staff 16 Jun 16 21:12 mb-6-big-Filter.db -rw-r–r– 1 alain staff 9 Jun 16 21:12 mb-6-big-Index.db -rw-r–r– 1 alain staff 4701 Jun 16 21:12 mb-6-big-Statistics.db -rw-r–r– 1 alain staff 59 Jun 16 21:12 mb-6-big-Summary.db -rw-r–r– 1 alain staff 92 Jun 16 21:12 mb-6-big-TOC.txt ...

2017年3月23日 · 4 分钟

[翻译]关于Cassandra中的删除和墓碑(三)

保存墓碑(原文Tombstones to the rescue 墓碑营救?) 在Cassandra语境中,墓碑是一种特殊的数据和普通数据一样存储,一个删除操作,就是写入一个墓碑。当Cassandra读取数据的时候,它会合并这些内存里或者磁盘上写入的数据行。然后使用一种最新写入胜出(LWW)算法选择出正确的数据,不管它是个标准写入的数据,还是一个墓碑。 举例: 我们看下接下来的例子,背景是Cassandra 3.7集群,有3个节点(通过ccm创建的,译者注:ccm是个脚本程序可以快速的删除创建一个小Cassandra集群,github地址是:https://github.com/pcmanus/ccm) CREATE KEYSPACE tlp_lab WITH replication = {‘class’: ‘NetworkTopologyStrategy’, ‘datacenter1’ : 3}; CREATE TABLE tlp_lab.tombstones (fruit text, date text, crates set<int>, PRIMARY KEY (fruit, date)); 插入一些数据,每天创建一些水果(译者注:表里的字段的含义),如下: INSERT INTO tlp_lab.tombstones (fruit, date, crates) VALUES (‘apple’, ‘20160616’, {1,2,3,4,5}); INSERT INTO tlp_lab.tombstones (fruit, date, crates) VALUES (‘apple’, ‘20160617’, {1,2,3}); INSERT INTO tlp_lab.tombstones (fruit, date, crates) VALUES (‘pickles’, ‘20160616’, {6,7,8}) USING TTL 2592000; 下面就是我们存储的数据: alain$ echo “SELECT * FROM tlp_lab.tombstones LIMIT 100;” | cqlsh ...

2017年3月21日 · 3 分钟