遇到什么问题?
1.查询 Cassandra 总是报查询超时异常、等待获取连接超时 2.虽然查询超时,但是 Cassandra 服务器所在机器 Cpu 使用率缺非常低
官网介绍
cassandra.apache.org/_/cassandra...
配置优化
cassandra 启动配置重点优化以下 4 个参数
-
native_transport_max_threads:
- 作用:这个配置项表示 Cassandra 数据库的本地传输(Native Transport)线程池的最大线程数。
- 值: "4096" 表示最大线程数为 4096。这个值决定了 Cassandra 数据库能够处理同时进行的客户端连接请求的数量。增加这个值可以提高 Cassandra 的并发连接处理能力,但也需要更多的系统资源。
-
concurrent_reads:
- 作用:这个配置项表示 Cassandra 数据库允许同时执行的读取操作(reads)的数量。
- 值: "64" 表示同时允许执行的读取操作数量为 64。通过调整这个值,可以控制 Cassandra 在同一时间处理读取请求的数量。这可以影响读取操作的吞吐量和响应时间。
-
concurrent_compactors:
- 作用:这个配置项表示 Cassandra 数据库用于压缩 SSTable 文件的并发压缩线程数。
- 值: "8" 表示有 8 个并发的 SSTable 压缩线程。SSTable 是 Cassandra 中的一种数据文件格式,通过压缩可以释放磁盘空间并提高性能。通过调整这个值,可以控制压缩操作的并发程度。
-
垃圾回收器可以尝试改为 G1
连接客户端配置优化
java
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;
import org.springframework.data.cassandra.config.CqlSessionFactoryBean;
import org.springframework.data.cassandra.config.SessionBuilderConfigurer;
import java.time.Duration;
@Slf4j
@Configuration
public class CassandraConfig extends AbstractCassandraConfiguration {
//空间名称
@Value("${spring.data.cassandra.keyspace-name}")
String keyspaceName;
//节点IP(连接的集群节点IP)
@Value("${spring.data.cassandra.contact-points}")
String contactPoints;
@Value("${spring.data.cassandra.username}")
String username;
@Value("${spring.data.cassandra.password}")
String password;
@Value("${spring.data.cassandra.session-name}")
String sessionName;
@Value("${spring.data.cassandra.pool-size}")
Integer poolSize;
@Override
public String getKeyspaceName() {
return keyspaceName;
}
@Override
public String getContactPoints() {
return contactPoints;
}
@Override
public String getSessionName() {
return sessionName;
}
@Override
public String getLocalDataCenter() {
return "datacenter1";
}
@Bean
@Override
public CqlSessionFactoryBean cassandraSession() {
CqlSessionFactoryBean cqlSessionFactoryBean = super.cassandraSession();
cqlSessionFactoryBean.setPassword(password);
cqlSessionFactoryBean.setUsername(username);
return cqlSessionFactoryBean;
}
/**
* PT2S 异常优化 & 配置文件修改 request.timeout 不生效
* DefaultDriverOption 有哪些配置主要看这个类
*
* @return
*/
@Override
protected SessionBuilderConfigurer getSessionBuilderConfigurer() {
log.info("getSessionBuilderConfigurer-start poolSize : {}", poolSize);
return new SessionBuilderConfigurer() {
@Override
public CqlSessionBuilder configure(CqlSessionBuilder cqlSessionBuilder) {
CqlSessionBuilder cqlSessionBuilder1 = cqlSessionBuilder
.withConfigLoader(DriverConfigLoader.programmaticBuilder()
.withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofMillis(6000))
.withDuration(DefaultDriverOption.HEARTBEAT_TIMEOUT, Duration.ofMillis(3000))
.withInt(DefaultDriverOption.CONNECTION_POOL_LOCAL_SIZE, poolSize)
.withInt(DefaultDriverOption.CONNECTION_POOL_REMOTE_SIZE, poolSize)
.build());
return cqlSessionBuilder1;
}
};
}
}
分区优化
注意:分区优化需要结合压测 & 监控
压测方法:zqhxuyuan.github.io/2015/10/15/...
压测过程
分区相关知识
- Cassandra 官方建议单个分区数据大小不要超过 50 M,实战可以控制在 10M 左右性能更好
- Cassandra 的每个节点都是协调器,每个节点之间都会互相通信,所以当一个节点接收请求时它可以知道数据在哪个节点,每个节点负责一定范围的令牌, Cassandra 通过查看表中的分区键来执行这些读取和写入操作,并使用令牌(tokens)(一个 -2^{63}−263 到 +2^{63}-1+263−1 范围内的 long 类型值)来进行数据分布和索引,拿到分区键就可以快速通过令牌分区函数定位到分区,又因为这个 tokens 数非常多,所以可以做到哪怕 2000w 分区也可以快速查询
查询有 2000w 数据的表,表中一共 2000w 分区,但是没有大分区的情况
查询有 2400w 数据的表,表中有 2000w 小分区和 2 个 200w 数据的大分区,查询 2 个大分区
查询有 4000w 数据的表,表中有 2000w 小分区和 2 个 1000w 数据的大分区,查询 2 个大分区
查询有 4000w 数据的表,表中有 2000w 小分区和 2 个 1000w 数据的大分区,不查询 2 个大分区,只查询小分区
压测总结
- 只要单个分区数据大小合理,哪怕 2000w 分区也能快速查询
- 查询越大的分区,查询延迟越大
- 一张表中有大分区,但是不查询大分区,查询速度也很快 综上设计分区时尽量避免后续产生大分区
如何设计分区
优化思路
- 使分区尽量散列确保数据的均匀分布
常见两种分区方案
- 唯一键是字符串
java
import org.apache.commons.codec.digest.MurmurHash3;
public Integer getPartitionId(String uuid) {
int hash32x86 = MurmurHash3.hash32x86(uuid.getBytes());
int finalHash = Math.abs(hash32x86 % 1000000);
return finalHash;
}
注意:1000000 取决于你的数据大小,这个案例实际含义该表最大会有 1000000 个分区,每个分区总数据大小不要超过 50M,如果可能超过 50M 则应在设计表时扩大分区数直到预估不存在超过 50M 的分区
- 唯一键是自增数字类型
java
public Long getPartitionId(Long userId) {
long partitionId = userId / 1000;
return partitionId;
}
注意:1000 取决于你的数据大小,这个案例实际含义就是每 1000 个用户的该表数据在一个分区,一个用户可能存在多条数据,每 1000 个用户的分区总数据大小不要超过 50 M
云盘读写达上限,导致读延迟
Cassandra 延迟数据表现
Cassandra Cpu 使用率
云盘吞吐量,突增达到上限
咨询阿里云是否超过上限,得到回答:秒级监控读达到 475M/s ,超过上限 这也解释了一大困惑:为什么 cassandra 延迟很高,但是 cpu 使用率却不高,因为性能瓶颈在云盘,并不在 cassandra 服务器,而在云盘