分布式微服务系统架构第126集:集群,数据库扩展,多节点分布,分库,分表,分片,分表,运维

加群联系作者vx:xiaoda0423

仓库地址:https://webvueblog.github.io/JavaPlusDoc/

https://1024bat.cn/

go 复制代码
cassandra 主备集群
elasticsearch
nginx
redis

grafana+prometheus
skywalking

一、开头部分:nohup ... &

go 复制代码
nohup ... >/dev/null 2>&1 &
部分 说明
nohup 防止程序因退出终端而终止,保证服务后台运行
> /dev/null 2>&1 把标准输出(stdout)和错误输出(stderr)都重定向到"黑洞"(不显示任何日志)
& 表示 后台运行,不会卡住当前终端

📌 生产环境中常用,可以不挂断地运行服务。

参数 说明
-javaagent=... 启动时加载 SkyWalking 的探针(agent),用于采集调用链
agent.service_name=xx-service 当前服务在链路追踪系统中的名称
collector.backend_service=xxx:xxx SkyWalking 后端 OAP 服务地址(收集数据)
参数 说明
nacos.discovery.server-addr 配置 Nacos 注册中心地址(服务注册发现)
spring.profiles.active=prod 使用 application-prod.yml 作为配置文件(生产环境)
server.port=xxx 启动服务监听的端口号为 10003
  • ✅ 如果你想看日志

    go 复制代码
    > /var/log/xxx-service.log 2>&1 &
  • ✅ 如果服务挂了,可以用:

    go 复制代码
    ps -ef | grep xxx-service
    kill -9 <PID>

给脚本加执行权限

go 复制代码
chmod +x start.sh stop.sh restart.sh

生产环境建议

建议项 内容
配置外置 application-prod.yml 放到 /config 或指定 -Dspring.config.location
日志系统 使用 logback-spring.xml 输出日志到文件
守护进程 生产中可以考虑用 systemdsupervisor 守护 Java 服务防止崩溃

日常部署注意事项

内容 建议
JAR 包版本管理 放在 releases/ 子目录,方便版本管理和回退
使用软链接启动 current -> 真实 jar ,解耦脚本与版本
保持日志不被覆盖 日志写入 logs/ 目录,防止丢失
自动化 可升级为 Jenkins、GitLab CI、Ansible 等自动部署

✅ 工具横向对比推荐

系统 推荐压测工具 并发控制 备注
Cassandra cassandra-stress 内置工具,广泛使用
Scylla scylla-bench 官方支持,兼容 Cassandra
Elasticsearch esrally 官方工具,支持自定义场景
Kafka kafka-producer-perf-test 吞吐、延迟指标清晰
MySQL sysbench 支持读写混合、高并发模拟
Redis redis-benchmark 极高性能测试,支持多命令

(Redis压测参考)

Record Count Operation Avg TPS / QPS Avg Latency CPU (%) Mem (MB) Disk Write Disk Used
1M Write 150,000 0.6 ms 80% 500 MB 100 MB/s 0.4 GB

汇总:

Record Count Operation QPS (r/s) Avg Latency (估算) CPU MEM Disk Space
1,000,000 SET 154930.12 ≈ 6.45ms/1M = 0.006
1,000,000 GET 159235.67 ≈ 6.28ms/1M = 0.006

日志文件内容(每秒一行):

go 复制代码
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0      0 1120000  12300 205500    0    0     5     6  110  210  5  3 90  2  0
 1  0      0 1108000  12000 208000    0    0     4     8  108  190  7  4 87  2  0
 ...

可从中提取:

  • us+sy:CPU使用率(user/system)

  • free:空闲内存

  • wa:I/O等待率

  • bi/bo:磁盘读写速率(block in/out)

✅ 注意事项

项目 建议
✅ 自动调用 主脚本中已经用:./tools/monitor.sh start ... &stop ...
❗防止误删 如果中间卡死或中断,请手动 kill $(cat /tmp/monitor_pid)
✅ 多系统兼容 文件名中加上 ${SYSTEM}_${COUNT} 区分不同压测记录

✅ Cassandra 中的 Keyspace 是什么?

Keyspace 类似于 MySQL 的数据库(CREATE DATABASE),是 Cassandra 的最顶层数据结构 ,里面包含多个表(Table)。每个 Keyspace 有自己的 复制策略(replication strategy)副本数(replication factor)

  • 'class': 'SimpleStrategy':复制策略选择的是 SimpleStrategy,适合单数据中心

  • 'replication_factor': 2每条数据保存 2 份,分布在不同节点,提高容灾能力。

✅ 副本数(replication_factor)设置建议

  • 2 表示:写入数据会保存两份,放在两个节点上。

    • 适合生产环境的主数据,防止单节点宕机导致数据丢失。
  • 1 表示:只保存一份,适合日志这类容错要求不高、容量大的场景。

多数据中心的集群

go 复制代码
WITH replication = {
  'class': 'NetworkTopologyStrategy',
  'datacenter1': 3,
  'datacenter2': 2
};

你想要 将 Cassandra 数据部署为副本 2(replication_factor=2)的高可用架构,需要满足以下两个条件:


✅ 1. 集群中至少部署两个节点

这是基本前提:因为副本数是 2,Cassandra 需要能在两个不同节点存放相同的数据。

✅ 最小部署拓扑示意:

go 复制代码
Node1 (192.168.1.101)   ⇄   Node2 (192.168.1.102)
         __________________/
             数据副本互为备份

✅ 2. 创建 Keyspace 使用 SimpleStrategy(单数据中心)或 NetworkTopologyStrategy(多数据中心)

推荐方式(生产环境):

使用 NetworkTopologyStrategy,即使你只有一个数据中心,也建议使用它。

🛠 部署步骤简化(以两节点集群为例)

假设两台服务器:

  • Node1: 192.168.1.101

  • Node2: 192.168.1.102

修改两台机器的配置

修改 /etc/cassandra/cassandra.yaml 中关键字段:

每台机器都要配置:

go 复制代码
cluster_name: 'MyCluster'         # 保持一致
num_tokens: 256                   # 默认值即可
listen_address: 本机IP            # 如 192.168.1.101 / 192.168.1.102
rpc_address: 0.0.0.0              # 或写死本机IP
seed_provider:
  - class_name: org.apache.cassandra.locator.SimpleSeedProvider
    parameters:
      - seeds: "192.168.1.101,192.168.1.102"
endpoint_snitch: GossipingPropertyFileSnitch
修改 /etc/cassandra/cassandra-rackdc.properties

确保都属于同一个数据中心:

go 复制代码
dc=datacenter1
rack=rack1

启动 Cassandra 服务并加入集群

分别在两台机器上执行:

go 复制代码
sudo systemctl start cassandra

或者:

go 复制代码
cassandra -f

确认集群状态(任意节点上)

go 复制代码
nodetool status

你会看到类似输出:

go 复制代码
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
--  Address         Load       Tokens       Owns (effective)  Host ID                               Rack
UN  192.168.1.101   150.99 KB  256          ?                 abcdefg-...                           rack1
UN  192.168.1.102   160.23 KB  256          ?                 hijklmn-...                           rack1

Cassandra 集群并写数据

连接 Cassandra + 写入 + 查询

go 复制代码
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.*;

import java.net.InetSocketAddress;

public class CassandraExample {
    public static void main(String[] args) {
        // 连接 Cassandra 集群,传入主机 IP 和端口
        try (CqlSession session = CqlSession.builder()
                .addContactPoint(new InetSocketAddress("192.168.1.101", 9042))
                .addContactPoint(new InetSocketAddress("192.168.1.102", 9042))
                .withLocalDatacenter("datacenter1")  // 与 cassandra-rackdc.properties 的 dc 匹配
                .build()) {

            // 使用或创建 Keyspace
            session.execute("CREATE KEYSPACE IF NOT EXISTS demo_db " +
                    "WITH replication = {'class':'NetworkTopologyStrategy', 'datacenter1':2}");

            // 使用该 keyspace
            session.execute("USE demo_db");

            // 创建一个表
            session.execute("CREATE TABLE IF NOT EXISTS users (" +
                    "id UUID PRIMARY KEY," +
                    "name text," +
                    "email text)");

            // 插入数据
            session.execute("INSERT INTO users (id, name, email) VALUES (uuid(), 'Tom', '[email protected]')");

            // 查询数据
            ResultSet resultSet = session.execute("SELECT * FROM users");

            for (Row row : resultSet) {
                System.out.println("User: " + row.getString("name") + " - " + row.getString("email"));
            }
        }
    }
}

使用 Spring Data for Apache Cassandra(推荐企业项目)

💡 依赖(Spring Boot 项目使用):

go 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>

✅ application.yml 示例配置

go 复制代码
spring:
  data:
    cassandra:
      contact-points: 192.168.1.101,192.168.1.102
      port: 9042
      keyspace-name: demo_db
      local-datacenter: datacenter1
      schema-action: create_if_not_exists

✅ Entity + Repository 示例

go 复制代码
@Table("users")
public class User {
    @PrimaryKey
    private UUID id;

    private String name;
    private String email;

    // getters/setters ...
}
go 复制代码
public interface UserRepository extends CassandraRepository<User, UUID> {
}

✅ 使用 Service 或 Controller 中调用

go 复制代码
@Autowired
private UserRepository userRepository;

public void demo() {
    User user = new User();
    user.setId(UUID.randomUUID());
    user.setName("Alice");
    user.setEmail("[email protected]");

    userRepository.save(user);

    userRepository.findAll().forEach(u -> System.out.println(u.getName()));
}
方案 适合场景 优点
原生 DataStax Java Driver 轻量项目 / 快速集成 灵活、性能好、控制粒度大
Spring Data Cassandra Spring Boot 项目 快速开发、抽象更高、更易维护

项目结构预览

go 复制代码
spring-cassandra-demo/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/com/example/demo/
│   │   │   ├── DemoApplication.java
│   │   │   ├── entity/User.java
│   │   │   ├── repository/UserRepository.java
│   │   │   └── controller/UserController.java
│   │   └── resources/
│   │       ├── application.yml
│   │       └── logback-spring.xml

✅ 1. pom.xml 添加依赖

go 复制代码
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>spring-cassandra-demo</artifactId>
  <version>1.0.0</version>
  <dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Cassandra Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-cassandra</artifactId>
    </dependency>

    <!-- UUID 工具包(可选) -->
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>
  </dependencies>

  <properties>
    <java.version>17</java.version>
    <spring.boot.version>3.1.0</spring.boot.version>
  </properties>

  <build>
    <plugins>
      <!-- Spring Boot Plugin -->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

✅ 2. application.yml 配置 Cassandra 连接

go 复制代码
spring:
  data:
    cassandra:
      contact-points: 192.168.1.101,192.168.1.102
      port: 9042
      keyspace-name: demo_db
      local-datacenter: datacenter1
      schema-action: create_if_not_exists

✅ 3. DemoApplication.java 启动类

go 复制代码
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

✅ 4. User.java 实体类

go 复制代码
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;

import java.util.UUID;

@Table("users")
public class User {

    @PrimaryKey
    private UUID id;
    private String name;
    private String email;

    // Getters & Setters
    public UUID getId() { return id; }
    public void setId(UUID id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

✅ 5. UserRepository.java 仓储接口

go 复制代码
import com.example.demo.entity.User;
import org.springframework.data.cassandra.repository.CassandraRepository;
import java.util.UUID;

public interface UserRepository extends CassandraRepository<User, UUID> {
}

✅ 6. UserController.java 示例接口

go 复制代码
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @PostMapping
    public User create(@RequestBody User user) {
        user.setId(UUID.randomUUID());
        return userRepository.save(user);
    }

    @GetMapping
    public List<User> list() {
        return userRepository.findAll();
    }
}

🚀 启动 & 测试

1. 启动项目:

go 复制代码
mvn spring-boot:run

2. 测试接口:

go 复制代码
POST http://localhost:8080/users
Body:
{
  "name": "Alice",
  "email": "[email protected]"
}

GET http://localhost:8080/users
相关推荐
陆少枫4 分钟前
MySQL基础关键_013_常用 DBA 命令
数据库·mysql
赵渝强老师19 分钟前
【赵渝强老师】在PostgreSQL中使用file_fdw访问外部文件系统
数据库·postgresql
智_永无止境31 分钟前
Redis 8.0携新功能,重新开源
数据库·redis·开源
阿乾之铭1 小时前
Spring Boot 参数验证
java·数据库·mysql
佩奇的技术笔记1 小时前
Java学习手册:微服务设计原则
java·微服务
martian6651 小时前
信创生态核心技术栈:数据库与中间件
开发语言·中间件·系统架构·系统安全·创业创新
UpUpUp……2 小时前
Linux--JsonCpp
linux·运维·服务器·c++·笔记·json
唐人街都是苦瓜脸2 小时前
MySQL创建了一个索引表,如何来验证这个索引表是否使用了呢?
数据库·mysql
前进的程序员2 小时前
SQLite 数据库常见问题及解决方法
数据库·sqlite
zhcong_2 小时前
MySQL数据库操作
数据库·mysql