Guohao Lu's Blog

个人技术博客

当MySQL查询优化器选择了错误的索引时,我们有以下几种解决方案:

1. 使用强制索引(FORCE INDEX)

如果您确定某个索引是最优选择,可以使用FORCE INDEX强制MySQL使用指定的索引:

1
2
SELECT FROM users FORCE INDEX (idx_username)
WHERE username = 'test' AND status = 1;

2. 使用索引提示(USE INDEX)

相比FORCE INDEX更温和的方式是使用USE INDEX,它会建议MySQL使用指定的索引:

1
2
SELECT * FROM users USE INDEX (idx_username) 
WHERE username = 'test' AND status = 1;

3. 更新统计信息

有时索引选择错误是因为统计信息过时,可以通过以下命令更新:

1
ANALYZE TABLE users;

4. 重写查询

可以尝试重写查询语句,使其更符合索引的设计:

  • 调整WHERE子句的顺序
  • 改写JOIN条件
  • 使用子查询替代JOIN等

5. 修改索引

如果频繁发生索引选择错误,可能需要:

  • 创建更合适的复合索引
  • 删除重复或无用的索引
  • 优化现有索引结构

注意事项

  1. 使用强制索引要谨慎,因为:

    • 可能导致性能更差
    • 随着数据变化,强制的索引可能不再是最优选择
  2. 定期维护统计信息很重要

    • 建议在业务低峰期执行ANALYZE TABLE
    • 可以配置自动更新统计信息
  3. 监控查询性能

    • 使用EXPLAIN分析执行计划
    • 观察查询响应时间
    • 记录慢查询日志

总结

虽然MySQL的查询优化器通常能做出正确的选择,但在特定情况下可能选错索引。了解上述解决方案,可以帮助我们在遇到此类问题时快速处理。建议优先考虑更新统计信息和优化查询语句,只在必要时才使用强制索引。

  1. 什么是Rebalance(1)
  • 当消费者组成员发生变化时触发的分区重新分配机制
  • 目的是实现负载均衡,让分区尽可能均匀地分配给所有消费者
  1. 触发条件
  • 消费者组成员数量发生变化(新增或减少消费者)
  • 订阅的主题数量发生变化
  • 主题的分区数发生变化
  • 消费者宕机或网络故障
  1. 再均衡策略
  • Range策略(默认)

    • 按照分区号范围进行分配
    • 可能会导致分配不均
    • 分配公式:分区号/消费者数量
  • RoundRobin策略

    • 轮询分配方式
    • 分区分配更均匀
    • 适合分区数较多的场景
  • Sticky策略

    • 分配尽可能与上次保持相同
    • 减少分区迁移带来的开销
    • 在出现故障时才进行必要的分区移动
  1. 再均衡过程
  • 消费者组选举Group Coordinator
  • Group Coordinator选举Leader Consumer
  • Leader制定分配方案
  • Group Coordinator将方案下发给所有消费者
  1. 注意事项
  • 再均衡期间消费者无法消费消息
  • 频繁的再均衡会影响系统性能
  • 合理设置session.timeout.ms和heartbeat.interval.ms
  • 建议使用Sticky策略减少不必要的分区移动

Kafka的消费者(KafkaConsumer)不是线程安全的,具体表现在:

  1. 官方说明(1)
  • KafkaConsumer不是线程安全的
  • 所有网络I/O操作都发生在进行调用的线程中
  • 可以安全地关闭消费者或者从另一个线程唤醒轮询
  1. 正确使用方式
  • 单线程消费:一个消费者实例对应一个线程
  • 多线程处理:消费单线程,处理多线程
  • 多消费者实例:每个线程一个独立的消费者实例
  1. 错误使用示例

    1
    2
    3
    4
    5
    // 错误示例:多线程共享一个消费者实例
    KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.submit(() -> consumer.poll(Duration.ofMillis(100))); // 线程1
    executor.submit(() -> consumer.poll(Duration.ofMillis(100))); // 线程2
  2. 正确使用示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 正确示例1:每个线程一个消费者实例
    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.submit(() -> {
    KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
    while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    // 处理消息
    }
    });

    // 正确示例2:消费单线程,处理多线程
    KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
    ExecutorService executor = Executors.newFixedThreadPool(2);
    while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
    executor.submit(() -> processRecord(record)); // 异步处理
    }
    }
  3. 最佳实践建议

  • 使用消费者组机制实现并行消费
  • 处理逻辑放入线程池异步执行
  • 注意消费位移的正确提交
  • 合理设置消费者数量和分区数

Redis的SDS通过预分配空间来减少内存分配的次数,具体策略如下:

  1. 预分配规则
  • 当字符串长度小于1MB时,扩容会分配双倍空间
    • 例如:修改后长度为13字节,则会分配13+13=26字节
  • 当字符串长度大于等于1MB时,扩容会多分配1MB空间
    • 例如:修改后长度为30MB,则会分配30MB+1MB=31MB
  1. 优势说明
  • 减少内存重新分配次数
    • 传统C字符串每次增长都需要重新分配内存
    • SDS预分配策略让连续增长可以重用之前预留的空间
  • 降低内存操作开销
    • 减少了malloc、realloc、free等系统调用
    • 避免了频繁的数据复制和移动
  1. 举例说明
    假设字符串从空开始,依次追加5次,每次1个字节:
  • 传统C字符串:需要5次内存分配
  • Redis SDS:
    • 第1次:分配1+1=2字节
    • 第2次:使用预分配空间,无需分配
    • 第3次:使用预分配空间,无需分配
    • 第4次:分配4+4=8字节
    • 第5次:使用预分配空间,无需分配
      最终只需要2次内存分配

Redis虽然是单线程模型,但性能仍然优于多线程的Memcached,主要有以下几个原因:

  1. 内存模型不同
  • Redis直接自己构建了VM机制,减少内存碎片和申请/释放内存的开销
  • Memcached使用预分配的内存池,需要进行内存申请和释放,产生内存碎片
  1. 网络模型不同(1)
  • Redis使用自己实现的事件驱动库AE,采用多路复用技术(epoll)
  • Memcached是基于libevent构建的多线程模型,线程之间需要锁竞争
  1. 数据结构不同
  • Redis有丰富的数据结构(String、Hash、List、Set、ZSet等),数据操作更高效
  • Memcached只支持简单的key-value结构,复杂操作需要客户端实现
  1. 持久化机制
  • Redis支持RDB和AOF两种持久化方式,可以定期将数据同步到磁盘
  • Memcached数据只在内存中,重启后数据会丢失
  1. 单线程的优势
  • 避免了多线程的上下文切换开销
  • 避免了多线程的锁竞争问题
  • 简化了数据结构和算法的实现
  • 保证了数据访问的原子性,不需要额外的同步机制
  1. I/O多路复用
  • Redis使用epoll/kqueue等I/O多路复用技术
  • 单线程可以处理大量的并发连接
  • 非阻塞I/O,提高了I/O效率

总的来说,Redis通过巧妙的设计(高效的数据结构、I/O多路复用、内存管理等),充分发挥了单线程的优势,同时规避了多线程带来的问题,因此可以实现比Memcached更好的性能。

Redis中的数据类型和数据结构是两个不同的概念:

一、数据类型(对外使用的数据类型)

  1. 基本数据类型(1)
  • String(字符串):最基本的数据类型,可以存储字符串、整数或浮点数,最大512MB
  • List(列表):按插入顺序排序的字符串列表,支持双向操作,可用于消息队列
  • Hash(哈希):键值对的无序散列表,适合存储对象
  • Set(集合):无序的字符串集合,自动去重,支持并交差集运算
  • Zset(有序集合):有序的字符串集合,每个元素关联一个分数,可按分数排序
  1. 特殊数据类型
  • Bitmap(位图):二进制位操作,适合统计日活、在线状态等布尔型数据
  • HyperLogLog:基数统计,统计UV等,占用空间小,有一定误差
  • GEO(地理位置):存储地理坐标,支持计算距离、范围查询等

二、底层数据结构(内部实现)

  1. 简单动态字符串(SDS)
  • 用于实现String类型
  • 相比C字符串,增加了长度字段,避免了多次遍历
  • 预分配空间,减少内存分配次数
  1. 双向链表(linkedlist)
  • 用于实现List类型
  • 带有前驱和后继指针
  • 支持双向遍历
  1. 压缩列表(ziplist)
  • 用于优化存储空间
  • 可用于实现List、Hash、Zset等
  • 适用于元素数量少、元素值小的场景
  1. 哈希表(hashtable)
  • 用于实现Hash、Set等
  • 使用MurmurHash2算法计算哈希值
  • 采用链地址法解决冲突
  1. 跳表(skiplist)
  • 主要用于实现Zset
  • 平均O(logN)的查找复杂度
  • 相比红黑树,实现更简单,内存占用更小
  1. 整数集合(intset)
  • 用于优化Set的整数存储
  • 随数据升级编码方式
  • 节省内存空间
  1. QuickList
  • Redis 3.2后用于实现List
  • 结合了ziplist和linkedlist的优点
  • 平衡了存储效率和访问效率

三、数据类型与数据结构的关系

  1. String类型
  • 整数:直接存储
  • 短字符串(≤44字节):嵌入式存储
  • 长字符串:SDS存储
  1. List类型
  • 3.2版本前:ziplist或linkedlist
  • 3.2版本后:quicklist
  1. Hash类型
  • 小规模数据:ziplist
  • 大规模数据:hashtable
  1. Set类型
  • 整数集合:intset
  • 其他情况:hashtable
  1. Zset类型
  • 小规模数据:ziplist
  • 大规模数据:skiplist+hashtable

这种分层设计体现了Redis的优化思想:在不同场景下选择最优的数据结构,在性能和内存使用之间取得平衡。

本文详细介绍Kafka的acks机制及其实现原理。

  1. acks=0 (fire and forget)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    发送流程:
    Producer -----> Leader
    不等待确认

    特点:
    - 生产者发完即忘
    - 不等待任何确认
    - 最大吞吐量

    风险:
    ┌─────────┐ ┌─────────┐
    │Producer │ │ Leader │ ✗ (宕机)
    └─────────┘ └─────────┘
    │ │
    └──消息───────┘
    (消息丢失)

    适用场景:
    - 日志收集
    - 监控数据
    - 允许少量丢失
  2. acks=1 (leader only)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    发送流程:
    Producer -----> Leader -----> Follower
    等待Leader确认 异步复制

    确认过程:
    ┌─────────┐ ┌─────────┐
    │Producer │ │ Leader │ ✓ (确认)
    └─────────┘ └─────────┘
    │ │
    └──消息───────┘
    │ │
    └──ACK────────┘

    风险场景:
    Leader确认后立即宕机,数据未同步到Follower:
    ┌─────────┐ ┌─────────┐
    │Producer │ │ Leader │ ✓ -> ✗ (宕机)
    └─────────┘ └─────────┘

    未同步 │

    ┌─────────┐
    │Follower │
    └─────────┘
  3. acks=-1/all (all in sync replicas)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    发送流程:
    Producer -----> Leader -----> ISR所有副本
    等待所有ISR确认

    确认过程:
    ┌─────────┐ ┌─────────┐
    │Producer │ │ Leader │ ✓
    └─────────┘ └─────────┘
    │ │
    │ 同步 │
    │ ↓
    │ ┌─────────┐
    │ │Follower │ ✓
    │ └─────────┘
    │ │
    └────ACK──────┘

    配置建议:
    min.insync.replicas=2
    replication.factor=3
  4. 性能对比

    1
    2
    3
    4
    5
    6
    7
    8
    9
    延迟对比:
    acks=0 < 1ms
    acks=1 ~10ms
    acks=-1 ~100ms

    吞吐量对比(消息/秒):
    acks=0 100,000+
    acks=1 50,000+
    acks=-1 10,000+
  5. 最佳实践

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 重要业务数据配置
    Properties props = new Properties();
    props.put(ProducerConfig.ACKS_CONFIG, "all");
    props.put(ProducerConfig.RETRIES_CONFIG, 3);
    props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);

    // 非重要数据配置
    Properties props = new Properties();
    props.put(ProducerConfig.ACKS_CONFIG, "1");
    props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy");

本文介绍Kafka事件驱动架构的基础知识。

Kafka事件驱动架构的基础是什么?

  1. 核心概念(1)
  • **事件(Event)**:记录系统中”发生了什么”的事实

    • 包含:事件key、事件value、时间戳、元数据
    • 例如:”用户Alice在2024-03-21 14:30支付了200元”
  • **生产者(Producer)**:发布事件的应用

    • 完全解耦:生产者无需等待消费者
    • 支持多生产者写入同一主题
  • **消费者(Consumer)**:订阅和处理事件的应用

    • 可以重复读取事件
    • 支持多消费者订阅同一主题
  1. 基础架构
  • **主题(Topic)**:

    • 类似文件系统的文件夹
    • 持久化存储事件
    • 支持多生产者/多消费者
  • **分区(Partition)**:

    • 主题的分布式存储单元
    • 保证同一分区内事件顺序
    • 支持并行处理
  1. 关键特性
  • 持久性:事件被持久化存储
  • 顺序性:分区内事件严格有序
  • 可伸缩:通过分区实现横向扩展
  • 容错性:通过副本机制保证可用性
  1. 应用场景
  • 事件溯源

    • 记录状态变更的完整历史
    • 支持系统状态重建
  • 流处理

    • 实时数据转换和聚合
    • 构建实时数据管道
  • 系统集成

    • 解耦系统组件
    • 实现异步通信
  1. 设计原则
  • 事件即事实:记录已发生的事情
  • 事件不可变:一旦写入不能修改
  • 时间很重要:事件必须包含时间信息
  • 顺序很重要:保证因果关系

Kafka事件驱动的深入理解

  1. 事件的本质(1)
  • 事件是”某事发生”的记录,包含三个核心要素:
    • 事件键(Event key):标识事件主体
    • 事件值(Event value):描述发生了什么
    • 时间戳(Timestamp):什么时候发生
  • 示例:
    1
    2
    3
    4
    5
    {
    "key": "Alice",
    "value": "Trip requested at work location",
    "timestamp": "Jun. 25, 2020 at 2:06 p.m."
    }
  1. 事件流特性(1)
  • 持续性:实时捕获来自各种源的事件
  • 持久性:事件被持久化存储以供处理
  • 实时性:支持实时处理和后期检索
  • 路由性:事件可以路由到不同的目标技术
  1. 典型应用场景(1)
  • 消息系统:处理实时支付和金融交易
  • 活动跟踪:监控车辆、货物实时位置
  • 指标收集:捕获和分析IoT设备数据
  • 流处理:处理客户交互和订单
  • 系统解耦:连接不同部门的数据流
  • 大数据集成:与Hadoop等技术集成
  1. 设计考虑(1)
  • 高吞吐量:支持高容量事件流
  • 数据积压:优雅处理大量数据积压
  • 低延迟:支持传统消息传递场景
  • 容错性:机器故障时的保障机制
  1. 实现机制
  • Topics:事件的基本组织单位
    • 只能追加写入
    • 事件不可变
    • 支持多生产者和多订阅者
  • Partitions:实现并行处理
    • 相同key的事件写入相同分区
    • 保证分区内事件顺序
    • 支持横向扩展

Kafka事件流详解

  1. 事件流的本质(1)
  • 事件流是对现实世界状态变化的实时捕获
  • 每个事件代表一个不可变的事实记录
  • 事件按时间顺序追加存储
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "event_id": "ord_12345",
    "event_type": "ORDER_CREATED",
    "timestamp": "2024-03-21T14:30:00Z",
    "key": "user_123",
    "payload": {
    "order_id": "12345",
    "user_id": "user_123",
    "items": [...],
    "total_amount": 199.99
    },
    "metadata": {
    "source": "mobile_app",
    "version": "1.0"
    }
    }
  1. 技术架构设计
  • 生产层

    • 事件捕获服务
    • 事件规范化处理
    • 事件校验和过滤
  • 存储层

    • Topic设计:按业务域划分
    • 分区策略:基于key的一致性哈希
    • 数据保留策略:基于时间或大小
  • 消费层

    • 实时处理:Kafka Streams
    • 批量处理:Spark/Flink
    • 数据分发:Kafka Connect
  1. 实际业务案例 - 电商订单系统
    1
    2
    3
    4
    订单域事件流:
    Order_Created -> Payment_Initiated -> Payment_Completed ->
    Inventory_Reserved -> Order_Fulfilled -> Delivery_Started ->
    Delivery_Completed
  • Topic设计

    1
    2
    3
    4
    5
    orders.events          - 订单主事件流
    orders.payments - 支付事件流
    orders.inventory - 库存事件流
    orders.delivery - 配送事件流
    orders.notifications - 通知事件流
  • 分区设计

    1
    2
    3
    4
    // 确保同一订单的所有事件进入同一分区
    String orderKey = event.getOrderId();
    ProducerRecord<String, String> record =
    new ProducerRecord<>("orders.events", orderKey, eventJson);
  1. 事件流处理模式
  • 状态跟踪

    1
    2
    3
    4
    5
    // 使用Kafka Streams跟踪订单状态
    StreamsBuilder builder = new StreamsBuilder();
    KTable<String, OrderState> orderStates = builder
    .table("orders.events",
    Materialized.as("order-states-store"));
  • 事件关联

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 关联支付和库存事件
    KStream<String, PaymentEvent> payments =
    builder.stream("orders.payments");
    KStream<String, InventoryEvent> inventory =
    builder.stream("orders.inventory");

    KStream<String, EnrichedOrder> enrichedOrders =
    payments.join(inventory,
    (payment, inventory) -> new EnrichedOrder(...),
    JoinWindows.of(Duration.ofMinutes(5)));
  1. 实践经验
  • 数据一致性

    • 使用事务生产者确保原子性写入
    • 实现幂等性消费者处理重复事件
  • 性能优化

    • 合理设置分区数(并行度)
    • 批量处理提升吞吐量
    • 压缩策略减少存储开销
  • 监控指标

    • 生产延迟(Producer Latency)
    • 消费延迟(Consumer Lag)
    • 端到端延迟(End-to-end Latency)

Kafka集群搭建实施方案

一、环境准备

  1. 硬件配置建议(1)

Kafka集群硬件配置建议

  1. CPU配置(1)
  • 普通场景:8-12核心即可
  • 原因:
    • Kafka不是CPU密集型应用
    • 主要负责消息传输和磁盘I/O
    • 仅在压缩/解压缩时较耗CPU
  1. 内存配置
  • 建议配置:16GB RAM
  • 分配建议:
    • 系统预留:4GB
    • Kafka堆内存:4-8GB
    • 页面缓存:剩余内存
  • 原因:
    • Kafka利用操作系统的页面缓存
    • 不需要很大的JVM堆内存
    • 过大堆内存反而会影响GC性能
  1. 磁盘配置
  • 类型选择:普通HDD即可
  • 容量建议:根据数据量和保留策略决定
  • 原因:
    • Kafka采用顺序写入
    • HDD顺序写性能接近SSD
    • 成本效益比更高
  1. 网络配置
  • 普通场景:千兆网卡足够
  • 高吞吐场景:万兆网卡
  • 原因:
    • 网络通常是瓶颈
    • 根据实际吞吐量需求选择
  1. 最佳实践
  • 磁盘配置:
    • 使用RAID10提高可靠性
    • 单独挂载数据目录
    • 使用XFS文件系统
  • 网络配置:
    • 调整TCP参数
    • 开启网卡多队列
  • 系统配置:
    • 调整文件描述符限制
    • 优化内存页面分配
  1. 软件要求

    1
    2
    3
    4
    5
    6
    7
    # 操作系统
    CentOS 7.x 或 Ubuntu 20.04 LTS

    # 基础环境
    Java 11+
    ZooKeeper 3.7.1 (如果使用KRaft模式则不需要)
    Kafka 3.5.0
  2. 系统优化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 文件描述符限制
    cat >> /etc/security/limits.conf << EOF
    * soft nofile 65536
    * hard nofile 65536
    EOF

    # 系统参数优化
    cat >> /etc/sysctl.conf << EOF
    vm.swappiness=1
    net.core.somaxconn=32768
    net.ipv4.tcp_max_syn_backlog=16384
    net.core.netdev_max_backlog=16384
    net.ipv4.tcp_rmem=4096 87380 16777216
    net.ipv4.tcp_wmem=4096 65536 16777216
    EOF
    sysctl -p

二、集群部署

  1. 节点规划

    1
    2
    3
    node1: 192.168.1.101 (broker-1)
    node2: 192.168.1.102 (broker-2)
    node3: 192.168.1.103 (broker-3)
  2. 安装配置

    1
    2
    3
    4
    5
    6
    7
    8
    # 下载并解压
    wget https://downloads.apache.org/kafka/3.5.0/kafka_2.13-3.5.0.tgz
    tar -xzf kafka_2.13-3.5.0.tgz
    mv kafka_2.13-3.5.0 /opt/kafka

    # 创建数据目录
    mkdir -p /data/kafka/logs
    chown -R kafka:kafka /data/kafka
  3. Broker配置(每个节点)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 基础配置
    broker.id=1 # 每个节点唯一
    listeners=PLAINTEXT://192.168.1.101:9092
    advertised.listeners=PLAINTEXT://192.168.1.101:9092
    num.network.threads=8
    num.io.threads=16

    # 日志配置
    log.dirs=/data/kafka/logs
    num.partitions=8
    default.replication.factor=3
    min.insync.replicas=2

    # 性能优化
    socket.send.buffer.bytes=102400
    socket.receive.buffer.bytes=102400
    socket.request.max.bytes=104857600
    num.replica.fetchers=4

    # 日志保留策略
    log.retention.hours=168
    log.segment.bytes=1073741824

三、启动服务

  1. 启动命令

    1
    2
    3
    4
    5
    # 启动ZooKeeper(如果使用)
    bin/zookeeper-server-start.sh -daemon config/zookeeper.properties

    # 启动Kafka
    bin/kafka-server-start.sh -daemon config/server.properties
  2. 验证集群状态

    1
    2
    3
    4
    5
    6
    7
    8
    # 查看主题列表
    bin/kafka-topics.sh --bootstrap-server 192.168.1.101:9092 --list

    # 创建测试主题
    bin/kafka-topics.sh --bootstrap-server 192.168.1.101:9092 \
    --create --topic test \
    --partitions 3 \
    --replication-factor 3

四、监控与运维

  1. JMX监控配置

    1
    2
    3
    4
    5
    # 设置JMX端口
    export JMX_PORT=9999
    export KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote \
    -Dcom.sun.management.jmxremote.authenticate=false \
    -Dcom.sun.management.jmxremote.ssl=false"
  2. 关键指标监控

  • Broker存活状态
  • 分区Leader分布
  • 消息吞吐量
  • 延迟监控
  • GC状态
  1. 日常运维命令
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 查看消费组
    bin/kafka-consumer-groups.sh --bootstrap-server 192.168.1.101:9092 --list

    # 查看主题详情
    bin/kafka-topics.sh --bootstrap-server 192.168.1.101:9092 \
    --describe --topic test

    # 平衡leader
    bin/kafka-leader-election.sh --bootstrap-server 192.168.1.101:9092 \
    --election-type PREFERRED --all-topic-partitions

五、性能调优

  1. 生产者优化

    1
    2
    3
    4
    5
    # 批量设置
    batch.size=131072
    linger.ms=10
    compression.type=lz4
    acks=1
  2. 消费者优化

    1
    2
    3
    fetch.min.bytes=1024
    fetch.max.wait.ms=500
    max.partition.fetch.bytes=1048576
  3. 操作系统优化

  • 使用XFS文件系统
  • 禁用atime更新
  • 配置noatime挂载选项

微服务与DDD的关系分析

  1. 核心概念对比

    1
    2
    3
    4
    5
    6
    7
    DDD(领域驱动设计)              微服务架构
    ┌────────────────┐ ┌────────────────┐
    │ - 限界上下文 │ 映射关系 │ - 服务边界 │
    │ - 领域模型 │ ═══════► │ - 服务接口 │
    │ - 聚合根 │ │ - 数据模型 │
    │ - 领域事件 │ │ - 消息通信 │
    └────────────────┘ └────────────────┘
  2. 设计思路对比

    1
    2
    3
    4
    5
    DDD思路:
    业务驱动 ──► 领域划分 ──► 模型设计 ──► 技术实现

    微服务思路:
    服务拆分 ──► 接口定义 ──► 服务实现 ──► 服务治理
  3. 实践案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // DDD风格的领域模型
    @Aggregate
    public class Order {
    @AggregateId
    private OrderId orderId;
    private UserId userId;
    private Money totalAmount;
    private List<OrderItem> items;
    private OrderStatus status;

    public void place() {
    // 业务规则验证
    validateOrder();

    // 状态变更
    this.status = OrderStatus.PLACED;

    // 发布领域事件
    DomainEvents.publish(
    new OrderPlacedEvent(this)
    );
    }
    }

    // 微服务风格的服务实现
    @Service
    public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private OrderMapper orderMapper;

    public OrderDTO createOrder(CreateOrderCommand cmd) {
    Order order = orderMapper.toEntity(cmd);
    orderRepository.save(order);
    return orderMapper.toDTO(order);
    }
    }
  4. 主要区别

    1
    2
    3
    4
    5
    6
    7
    8
    ┌────────────┬───────────────┬───────────────┐
    │ 维度 │ DDD │ 微服务 │
    ├────────────┼───────────────┼───────────────┤
    │设计重点 │ 领域逻辑 │ 服务边界 │
    │技术关注点 │ 业务规则 │ 技术实现 │
    │边界划分 │ 业务边界 │ 部署边界 │
    │通信方式 │ 领域事件 │ API接口 │
    └────────────┴───────────────┴───────────────┘
  5. 结合使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // 1. 领域层(DDD)
    public class OrderDomain {
    // 领域模型
    @Value
    public class OrderId {
    private final String value;
    }

    // 领域服务
    public interface OrderDomainService {
    Order createOrder(OrderCommand cmd);
    }
    }

    // 2. 应用层(微服务)
    @RestController
    @RequestMapping("/api/orders")
    public class OrderController {
    @Autowired
    private OrderApplicationService orderService;

    @PostMapping
    public ResponseEntity<OrderDTO> createOrder(
    @RequestBody CreateOrderRequest request
    ) {
    OrderCommand cmd = OrderCommand.from(request);
    OrderDTO result = orderService.createOrder(cmd);
    return ResponseEntity.ok(result);
    }
    }
  6. 最佳实践

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    1. 服务划分
    DDD: 按限界上下文划分
    微服务: 按服务能力划分
    结合: 限界上下文→微服务边界

    2. 数据管理
    DDD: 聚合根管理数据一致性
    微服务: 数据库隔离
    结合: 每个微服务一个聚合根

    3. 通信方式
    DDD: 领域事件
    微服务: REST/RPC
    结合: 同步+异步通信
  7. 协作模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ┌─────────────────────────────────────┐
    │ 微服务架构 │
    │ ┌────────────┐ ┌────────────┐ │
    │ │ 订单服务 │ │ 支付服务 │ │
    │ └────────────┘ └────────────┘ │
    │ DDD实现 DDD实现 │
    │ │ │ │
    │ └───────┬───────┘ │
    │ ▼ │
    │ 领域事件通信 │
    └─────────────────────────────────────┘

  1. Page Size大小
  • Linux默认页大小为4KB (4096字节)
  • 可以通过命令查看:getconf PAGE_SIZE
  • 某些系统支持大页(Huge Page),如2MB或1GB
  1. Page Cache总大小
  • 不是固定值,是动态调节的
  • 默认最大可使用所有可用物理内存
  • Linux通过vm.swappiness参数调节内存与swap的权衡
  • 通过/proc/sys/vm/drop_caches手动释放
  1. 查看方式

    1
    2
    3
    4
    5
    # 查看当前page cache使用情况
    cat /proc/meminfo | grep -i cache

    # 查看系统页大小
    getconf PAGE_SIZE
  2. 影响因素

  • 系统总内存大小
  • 当前内存使用压力
  • 系统IO负载情况
  • 内核参数配置