如何学习

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 一、这个技术出现的背景、初衷和要达到什么样的目标或是要解决什么样的问题 

 二、这个技术的优势和劣势分别是什么 

三、这个技术适用的场景。任何技术都有其适用的场景,离开了这个场景

四、技术的组成部分和关键点。

五、技术的底层原理和关键实现

六、已有的实现和它之间的对比

带宽: 每秒的吞吐量,即每 秒可以完成读/写的数据量 OPS:每 秒可以处理客户端业务请求的个 数

网络调优

存储节点服务器网络检查命令

1 ifconfig

ifconfig - configure a network interface

查看接口数是否正常, IP地址是否正常,“ errors”、“ overruns”、“ frame”计
数是否正常。所谓异常

所谓异常:
– 使用的接口无统计数据。
– 与后端10GE交换机相连的网口“ error”、“ overruns”、“ frame”统计数据
不为0。

overruns有计数,需要检查对应[交换机]接口有没有配置flow-control; frame有 计数,需要抓包确认有没有重传。

– SLOT5-0、 SLOT4-0收发包数差距很大。

  • 对于存储节点的RoCE网卡( 10GE组网),还应包含名称为SLOT4-0.2或
    SLOT5-0.2的网口的显示结果

  • 产品名称:200G网卡 Mellanox CX6 MCX653106A HDAT HDR IB卡 双口     该网卡适用于高速数据中心网络,支持InfiniBand(IB)网络技术,   适用于高性能计算(HPC)、云计算、大数据分析等场景。  

接口错误计数(errors/overruns/frame)

  • 关键统计项

    • errors: 物理层或协议栈错误(CRC错误、畸形包)。
    • overruns: 输入队列满导致内核丢弃数据包,通常因处理速度不足。
    • frame: 帧格式错误(如长度不匹配、对齐问题)。

|frame|校验错误或重传|物理层故障、MTU不匹配、重传冲突|更换硬件、统一MTU、抓包优化|

ens47f0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP group default qlen 8192

数据包分片与重组

  • 若发送方 MTU(例如 9000) > 路径中某设备 MTU(例如 1500):
    发送方未设置 DF(Don't Fragment) 标志:数据包被分片传输,降低效率。
    发送方设置 DF 标志:接收端返回 ICMP Fragmentation Needed 错误,导致通信失败

2 【ethtool】ethtool 网卡诊断、调整工具、网卡性能优化| 解决丢包严重

 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

ethtool - query or control network driver and hardware settings

ethtool ens49f1
Settings for ens49f1:
        Supported ports: [ TP ]
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Supported pause frame use: Symmetric
        Supports auto-negotiation: Yes
        Supported FEC modes: Not reported
        Advertised link modes:  10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Advertised pause frame use: Symmetric
        Advertised auto-negotiation: Yes
        Advertised FEC modes: Not reported
        Speed: 1000Mb/s
        Duplex: Full
        Port: Twisted Pair
        PHYAD: 1
        Transceiver: internal
        Auto-negotiation: on
        MDI-X: on (auto)
        Supports Wake-on: pumbg
        Wake-on: d
        Current message level: 0x00000007 (7)
                               drv probe link
        Link detected: yes

ethtool -i SLOT4-1: 看网口驱动版本,可以识别接口类型和厂商,比如cxgb4是
Chelsio卡, ixgbe是Intel 10GE卡。

lspci |grep Ether:确认用的是什么型号的网卡

3. netstat

netstat -s |grep Retrans检查重传个数,确认是否有重传。
TCPLostRetransmit: 1146

4. 实时查看网卡流量 sar -n DEV 2

  • sar - Collect, report, or save system activity information

  • sar -r -n DEV -f /var/log/sa/sa16

-n { keyword [,…] | ALL } Report network statistics.

查看网口收发数据的速率。

对于存储节点的RoCE网卡( 10GE组网),此命令无法查询到该网卡的收发速
率。

https://my.oschina.net/u/4279744/blog/3881002

https://blog.csdn.net/volitationLong/article/details/81741754

一、重传现象的直接解读

  • TCPLostRetransmit: 此计数器表示:
    • 显式触发的重传(如超时重传或快速重传)。
    • 值持续增长说明网络在持续丢包或延迟突变。

二、排查方向

1. 确定是否为偶发或持续问题

1
2
# 每隔 2 秒统计一次(观察趋势)
watch -n2 "netstat -s | grep -i retrans"
  • 偶发性增长:可能与应用突发流量或短暂网络抖动相关。
  • 持续增长:表明存在根因(如链路故障、MTU 不匹配、硬件故障)。

2. 关联 MTU 问题(参考历史会话)

  • 确认 MTU 一致性

    1
    2
    3
    4
    
    # 检查服务端、客户端、交换机的 MTU 配置
    ip link show eth0 | grep mtu         # Linux
    show interfaces GigabitEthernet1/1   # Cisco
    display interface GigabitEthernet0/0/1 | include MTU  # 华为
    
  • MTU 不匹配的典型影响:引起分片(ICMP Fragmentation Needed),若 DF 位被设置,则直接丢包,触发 TCP 重传。


3. 网络路径排查

  • 路径延迟和抖动

    1
    2
    
    # 测试到目标地址的延迟和抖动
    mtr -n -r -c 100 <目标IP>
    
  • 路径丢包检测

    1
    2
    
    # 持续 ping 观察丢包率
    ping -c 1000 -i 0.1 <目标IP> | grep "packet loss"
    

4. 深入分析 TCP 连接状态

  • 查看活跃连接的 RTT 和重传细节

    1
    
    ss -ti  # 关注 `rtt` 和 `retrans`
    
  • 输出示例

    1
    2
    
    ESTAB  0      0      192.168.1.100:ssh   192.168.1.2:58234
    cubic wscale:6,7 rto:234 rtt:102.3/24.3 ato:40 retrans:5
    
    • rto(重传超时时间)、rtt(往返时间)、retrans(该连接的重传次数)。

5. 硬件或驱动问题

  • 检查网卡错误计数

    1
    
    ethtool -S eth0 | grep -E "err|drop|fail"
    
    • 高 rx/tx_errors 或 discards 表明网卡或驱动异常。

三、优化和解决措施

1. 基础网络优化

  • 调整 TCP 参数(Linux 示例):

    1
    2
    3
    4
    5
    6
    
    # 增大 TCP 缓冲区
    sysctl -w net.ipv4.tcp_rmem='4096 87380 6291456'
    sysctl -w net.ipv4.tcp_wmem='4096 16384 4194304'
    
    # 启用 TCP 窗口缩放
    sysctl -w net.ipv4.tcp_window_scaling=1
    
  • 启用 BBR 拥塞控制算法(对抗丢包):

    1
    
    sysctl -w net.ipv4.tcp_congestion_control=bbr
    

2. MTU 问题修复

  • 若确认 MTU 不匹配(如跨设备 MTU 不一致),需全局统一 MTU。

3. QoS 或流量限速

  • 对关键业务流量实施优先级标记(DSCP/TOS):

    1
    2
    3
    
    # Linux 使用 tc 限速和优先级调度
    tc qdisc add dev eth0 root handle 1: htb
    tc class add dev eth0 parent 1: classid 1:10 htb rate 1gbit prio 0
    

4. 替换故障硬件

  • 若网卡错误计数持续增长,尝试更换网卡或使用多网卡绑定(Bonding)。

四、高级诊断工具

1. 抓包分析重传详情

1
2
# 抓取与目标 IP 的 TCP 流量(保存到文件)
tcpdump -i eth0 host <目标IP> and tcp -w retrans.pcap
  • Wireshark 分析
    • 过滤 tcp.analysis.retransmission 查看具体重传包。
    • 确认重传是 超时重传(间隔长)还是 快速重传(连续重复 ACK)。

2. 内核跟踪工具(Linux)

  • 动态追踪重传事件

    1
    
    perf record -e tcp:tcp_retransmit_skb -ag
    

五、总结

现象 可能原因 优先级排查项
TCP 重传持续增长 网络丢包、高延迟、MTU 不匹配 1. 路径丢包率 2. MTU 一致性
伴随机 NIC 错误计数增长 网卡硬件/驱动故障 ethtool -S 和 dmesg 日志
仅限于特定服务 应用层配置或服务器性能问题 检查服务日志和资源占用

关键点

  1. 先区分偶发还是持续性问题,再确定是否需要深入排查。
  2. 高重传率常见于跨公网传输或无线网络环境,若在内网中持续出现需视为严重故障。
  3. 结合 MTU 配置、路径质量、硬件状态多维度分析

提问:io_submit io_getevents 之间 需要做其他处理嘛? 例如 epoll监控什么?

Redis 通过子进程持久化异步刷盘线程(bio) 和 写时复制(COW) 技术,保证持久化操作不阻塞单线程主事件循环。具体流程如下:

步骤 主线程 子进程/后台线程
RDB 生成 fork() 创建子进程 遍历内存生成 RDB 文件
AOF 日志追加 写命令追加到 aof_buf 缓冲区 ——
AOF 刷盘(默认) —— 后台线程每秒调用 fsync

设计精髓:所有磁盘 I/O 操作均委托给子进程或后台线程,主线程专注于内存数据操作和网络事件处理,确保单线程模型的高效性

另外感叹一下 antirez 代码层面上的架构能力,几年前看过 redis 2.8 的代码,从 2.8 的分支直接切到 5.0 之后,原来阅读的位置并没有偏离主线太远。历经几个大版本的迭代,加了 N 多功能之后,代码主体逻辑依旧没有大改,真的是做到了对修改关闭,对扩展开放。向大佬学习

https://www.bookstack.cn/read/aliyun-rds-core/0bca1b2aa30e6bb3.md

redis是单线程的,持久化写操作比较耗时,怎么解决的 Redis 虽然是单线程事件循环模型的主线程设计,但通过多线程辅助异步持久化策略解决了持久化写操作的耗时问题。具体实现包括以下关键点:


一、持久化方案的分类

Redis 提供两种持久化方式,均采用子进程后台线程异步执行,避免主线程阻塞:

  1. RDB(快照持久化)

    • 触发方式save(阻塞主线程,已弃用) / bgsave(后台异步)。
    • 原理:主线程 fork 子进程生成内存快照并写入磁盘。
    • 优势:全量备份,恢复速度快;文件紧凑,占磁盘空间小。
    • 缺点:可能丢失最后一次快照后的数据。
  2. AOF(追加写日志)

    • 触发方式always(同步写,高安全)/ everysec(每秒异步刷盘,默认)/ no(内核决定刷盘时间)。
    • 原理:记录所有写操作命令,支持重写压缩(bgrewriteaof)。
    • 优势:数据丢失风险低;可读性强。
    • 缺点:文件体积大;恢复速度慢。

二、异步持久化的实现机制

1. RDB 异步处理

  • 主线程:仅负责接收写请求并更新内存数据,bgsave 命令触发时会 fork 出一个子进程。

  • 子进程:专用于执行 rdbSave(),生成 RDB 文件。

    • 写时复制(Copy-On-Write):子进程共享父进程内存页,仅在内存修改时复制脏页,减少内存开销。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    // redis/src/rdb.c
    int rdbSave(char *filename) {
        // 子进程独立完成磁盘写入
        snprintf(tmpfile, sizeof(tmpfile), "temp-%d.rdb", (int)getpid());
        fp = fopen(tmpfile, "w");
        // 遍历数据库生成快照数据...
        fflush(fp);
        fsync(fileno(fp));
        fclose(fp);
        // 原子替换旧RDB文件
        rename(tmpfile, filename);
    }
    

2. AOF 异步处理

  • 写缓冲(AOF Buffer):主线程将写操作追加到 aof_buf 内存缓冲区。

  • 刷盘策略

    • everysec(默认):由后台线程 bio(Background I/O)每秒调用 fsync 同步到磁盘。
    • 流程分离:主线程仅负责追加到缓冲区,后台线程负责刷盘,互不阻塞。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    // redis/src/aof.c
    void flushAppendOnlyFile(int force) {
        // 刷盘操作由 bio 线程异步执行
        if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {
            // 提交异步任务到 bio 队列
            bioCreateBackgroundJob(BIO_AOF_FSYNC, NULL, NULL, NULL);
        } else {
            fsync(server.aof_fd); // 同步操作(仅强制时由主线程执行)
        }
    }
    

3. 后台线程(Bio)

Redis 在启动时会初始化 3 类后台线程(默认配置未开启,需手动启用):

  • BIO_CLOSE_FILE:延迟关闭大文件句柄。
  • BIO_AOF_FSYNC:异步执行 AOF 刷盘(fsync)。
  • BIO_LAZY_FREE:异步内存回收(如 UNLINK 命令)。

关键函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// redis/src/bio.c
void *bioProcessBackgroundJobs(void *arg) {
    while(1) {
        listNode *ln = listFirst(bio_jobs[type]);
        bio_job *job = ln->value;
        
        // 根据任务类型执行操作(如 fsync、free 等)
        if (type == BIO_AOF_FSYNC) {
            fsync(job->fd);
        } else if (type == BIO_LAZY_FREE) {
            free(job->arg1);
        }
        listDelNode(bio_jobs[type], ln);
    }
}

三、性能优化补充

写时复制(COW)的取舍

  • 优势:子进程无需阻塞主线程,内存复制量最小化。
  • 风险:若主线程频繁修改数据(如大规模写入),子进程需复制大量脏页,导致 内存膨胀 和 CPU 开销
  • 建议:避免在持久化期间执行大量写入操作。

AOF 重写优化

  • 机制bgrewriteaof 命令触发子进程生成新的 AOF 文件,与主线程共享内存数据。
  • 增量写入:重写期间的新写操作会被记录到 AOF 重写缓冲区,确保数据一致性。

四、总结

Redis 通过子进程持久化异步刷盘线程(bio) 和 写时复制(COW) 技术,保证持久化操作不阻塞单线程主事件循环。具体流程如下:

步骤 主线程 子进程/后台线程
RDB 生成 fork() 创建子进程 遍历内存生成 RDB 文件
AOF 日志追加 写命令追加到 aof_buf 缓冲区 ——
AOF 刷盘(默认) —— 后台线程每秒调用 fsync

设计精髓:所有磁盘 I/O 操作均委托给子进程或后台线程,主线程专注于内存数据操作和网络事件处理,确保单线程模型的高效性。

reids bioCreateBackgroundJob 怎么实现的

提问4:Redis 的 bioCreateBackgroundJob 是后台任务处理的核心接口,用于将阻塞式操作(如磁盘 I/O、内存回收等)提交到后台线程异步执行,避免主线程阻塞。以下是其实现原理的详细分析:


一、后台线程的初始化

Redis 在启动时(main() 函数) 通过 InitServerLast() 调用 bioInit() 初始化后台线程池。 其核心步骤如下:

  1. 线程池创建
    默认创建 3 类后台线程(通过 BIO_NUM_OPS 控制类型数量):
    • BIO_CLOSE_FILE(异步关闭大文件句柄)
    • BIO_AOF_FSYNC(异步执行 AOF 刷盘)
    • BIO_LAZY_FREE(异步内存回收)
  1. 任务队列初始化
    每个后台线程类型对应一个任务队列(链表结构 bio_jobs[BIO_NUM_OPS]), 用于存储待处理的任务。

  2. 线程启动
    每个线程执行 bioProcessBackgroundJobs 函数,循环监听任务队列。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// redis/src/bio.c
void bioInit(void) {
    for (j = 0; j < BIO_NUM_OPS; j++) {
        pthread_t tid;
        pthread_mutex_init(&bio_mutex[j], NULL);
        pthread_cond_init(&bio_newjob_cond[j], NULL);
        pthread_cond_init(&bio_step_cond[j], NULL);
        bio_jobs[j] = listCreate();  // 初始化任务队列
        
        // 创建线程,绑定处理函数 bioProcessBackgroundJobs
        if (pthread_create(&tid, &attr, bioProcessBackgroundJobs, (void*)(long)j) != 0) {
            serverLog(LL_WARNING, "Bio thread for job type %d can't be initialized: %s", j, strerror(errno));
            exit(1);
        }
        bio_threads[j] = tid;  // 记录线程ID
    }
}

二、任务提交(bioCreateBackgroundJob)

当需要异步执行任务时(如 AOF 刷盘),主线程通过 bioCreateBackgroundJob 将任务添加到对应类型的任务队列:

  1. 任务参数封装
    将任务类型、待操作的文件描述符(fd)或其他参数(如释放的内存地址)封装到 bio_job 结构体。

  2. 线程安全入队
    对目标任务队列加锁,将任务插入链表尾部,并通知后台线程有新任务待处理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// redis/src/bio.c
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {
    struct bio_job *job = zmalloc(sizeof(*job));
    job->time = time(NULL);
    job->arg1 = arg1;  // 例如:AOF的fd、待释放的内存地址
    job->arg2 = arg2;
    job->arg3 = arg3;

    // 加锁保护任务队列
    pthread_mutex_lock(&bio_mutex[type]);
    listAddNodeTail(bio_jobs[type], job);  // 插入队列尾部
    
    // 通知后台线程有新任务
    pthread_cond_signal(&bio_newjob_cond[type]);//不通知别人wait
    pthread_mutex_unlock(&bio_mutex[type]);
}

三、任务处理(bioProcessBackgroundJobs)

后台线程的核心逻辑是循环监听任务队列,按顺序执行任务:

  1. 阻塞等待新任务
    若任务队列为空,线程通过条件变量 pthread_cond_wait() 进入阻塞状态,等待主线程唤醒。

  2. 获取并执行任务
    从队列头部取出任务,执行对应的操作(如 fsyncclosefree 等)。

  3. 任务清理
    任务执行完毕后,释放 bio_job 结构体内存。

 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
// redis/src/bio.c
void *bioProcessBackgroundJobs(void *arg) {
    while(1) {
        listNode *ln;
        int type = (unsigned long)arg;

        pthread_mutex_lock(&bio_mutex[type]);
        if (listLength(bio_jobs[type]) == 0) {
            // 等待新任务通知
            pthread_cond_wait(&bio_newjob_cond[type], &bio_mutex[type]);
            continue;
        }

        // 获取队列头部任务
        ln = listFirst(bio_jobs[type]);
        bio_job *job = ln->value;

        // 执行任务:根据类型调用 fsync()、close()、free() 等
        if (type == BIO_AOF_FSYNC) {
            fsync((long)job->arg1);  // arg1 对应 AOF 文件描述符
        } else if (type == BIO_CLOSE_FILE) {
            close((long)job->arg1);
        } else if (type == BIO_LAZY_FREE) {
            free(job->arg1);  // arg1 对应待释放的内存地址
        }

        // 移除已完成任务
        listDelNode(bio_jobs[type], ln);
        zfree(job);
        pthread_mutex_unlock(&bio_mutex[type]);
    }
}

四、关键设计点

机制 说明
无锁主线程 主线程提交任务仅需对队列加锁,无时间敏感操作,不会长时间阻塞主线程。
线程隔离 每个后台线程处理独立任务类型,避免竞争(例如 AOF 刷盘与内存回收互不影响)。
轻量化封装 bio_job 结构简单,仅存储必要参数,减少内存开销。
唤醒机制 条件变量通知确保后台线程及时响应新任务,降低延迟。

五、典型应用场景

1. AOF 刷盘(everysec 策略)

  • 主线程:将写命令追加到 aof_buf 缓冲区。
  • 后台线程:每秒执行一次 fsync,将缓冲区数据刷盘。

2. 惰性删除(UNLINK 命令)

1
2
3
4
5
6
7
8
9
// redis/src/db.c
void dbAsyncDelete(redisDb *db, robj *key) {
    if (dictDelete(db->dict, key->ptr) == DICT_OK) {
        // 异步释放内存
        if (server.lazyfree_lazy_user_async) {
            bioCreateBackgroundJob(BIO_LAZY_FREE, key->ptr, NULL, NULL);
        }
    }
}

总结

Redis 通过 bioCreateBackgroundJob 将阻塞操作分配至后台线程,其核心设计是:

  1. 主线程与后台线程解耦:主线程仅负责提交任务,避免 I/O 阻塞。
  2. 任务队列机制:使用线程安全的队列和条件变量实现任务调度。
  3. 资源隔离:不同类型任务由独立线程处理,避免相互干扰。
1
2
3
## write不够,需要fsync 
int fsync(int fd);
fsync的功能是确保文件fd所有已修改的内容已经正确同步到硬盘上,该调用会阻塞等待直到设备报告IO完成

//https://www.bookstack.cn/read/aliyun-rds-core/0bca1b2aa30e6bb3.md

提问:“Redis 在处理 AOF/RDB 等磁盘持久化任务时,选择通过单线程事件循环 + 后台 BIO 线程的机制实现异步化,而非直接使用 Linux 内核的异步 I/O(AIO)。这种设计是出于以下哪些考量?

这句话的核心是强调 BIO 线程的资源隔离性,即 Redis 通过 BIO 线程将磁盘操作(如持久化)与主线程的事件循环解耦,从而实现资源使用的可观测性和可控性。具体可从以下两个层面理解:


1. 线程调度优先级差异化(Scheduling Priority)

  • 操作系统的线程调度策略允许为不同线程设置优先级(例如 Linux 的 nice 值或 sched_setscheduler),Redis 可配置 BIO 线程的优先级略低于主线程:
    • 示例:主线程负责处理客户端命令,需要快速响应,因此被设置为较高优先级(如 nice=-10); -而 BIO 线程执行后台持久化任务,设置为较低优先级(如 nice=10)。
    • 效果:当 CPU 资源紧张时,内核调度器会优先执行主线程任务,避免磁盘刷盘阻塞用户请求处理。

2. 独立监控 BIO 线程状态(Observability)

  • 线程级独立监控:因为 BIO 线程独立于主线程存在,运维团队可以直接观察其状态:

    • 挂起检测:BIO 线程若因同步 I/O(如 fsync)长时间阻塞,可通过 top/htop 查看线程状态(如 D 状态表示不可中断 sleep),或通过 strace -p <tid> 跟踪系统调用。
    • 延迟分析:针对 BIO 线程独立的性能指标(如 bio_delay)可以快速定位磁盘性能问题,避免与主线程的延迟指标混淆。
  • 资源隔离的工程意义

    • 熔断能力:若 BIO 线程堆积过多任务(如 AOF 持久化过慢),可直接触发告警并降级持久化策略(如关闭 AOF)。
    • 性能调优:通过调整 BIO 线程数量(Redis 默认启动 3 个 BIO 线程)或绑定 CPU 核,优化磁盘吞吐与主线程延迟的平衡。

类比理解

想象一个公司的客服团队:

  • 主线程是客服热线接线员(高优先级):需要快速接听电话,直接服务客户,若让接线员同时处理财务报销(类比磁盘 I/O),会导致电话响应延迟。
  • BIO 线程是财务部(低优先级):财务流程可以独立运作(线程隔离),即使报销流程积压(磁盘负载高),客服团队仍能正常接听电话。财务部的效率也可以通过独立监控(如报销处理时长报表)来优化。

总结

“资源隔离”是通过优先级区分独立监控两个机制实现的:

  1. 优先保障主线程的低延迟:通过调度策略确保主线程优先获取 CPU。
  2. 持久化操作的影响可观测、可控制:BIO 线程的运行状态与主线程解耦,运维干预更精、

Redis 可配置 BIO 线程的优先级略低于主线程:

提问6:Redis 可配置 BIO 优先级在redis.CONF 怎么配置

1. BlueStore 的异步 I/O 架构

BlueStore 直接管理裸设备(如 /dev/nvme0n1),绕过文件系统,直接使用 libaio 提交异步 I/O 请求。其核心流程如下:

(1) I/O 请求的提交

  • 写入路径
    • 客户端请求通过 OSD 接收后,数据先写入 RocksDB 的 WAL(Write-Ahead Log)(基于 libaio 异步刷盘)。
    • 主数据通过 BlueFS(BlueStore 内部日志管理)异步提交到 NVMe SSD 的数据区域。
  • 读取路径
    • 直接从 NVMe SSD 异步读取数据块,通过回调机制将数据返回给客户端。

(2) 多线程与队列深度控制

  • 线程池模型
    • 每个 OSD 实例配置 osd_iodepth_threads 参数(默认值通常为 2-4,根据硬件调整)。
    • 每个线程独立处理一批 I/O 请求,通过 libaio 批量提交给内核。
  • 队列深度(iodepth)
    • 每个线程维护一个 AIO 队列,队列深度由 bluestore_iodepth(默认 32) 控制,表示单个线程同时未完成的 I/O 请求数。
    • 总并发能力 = osd_iodepth_threads × bluestore_iodepth
1
2
3
4
# 示例配置(ceph.conf)
[osd]
bluestore_iodepth = 32       # 每个线程的 AIO 队列深度
osd_iodepth_threads = 8      # 并发处理 I/O 的线程数

(3) libaio 的事件回调

  • 完成事件轮询
    • 线程通过 io_getevents 系统调用异步获取已完成的 I/O 请求事件。
    • 完成事件触发回调函数,更新元数据(如 RocksDB 中的对象状态)。

2. NVMe SSD 高队列深度的适配

NVMe SSD 的硬件队列深度可支持 数万级并发请求(如 64K 队列),Ceph 通过以下设计匹配这一特性:

(1) 多线程扩展

  • 提高 osd_iodepth_threads(如设置为 NVMe SSD 的 CPU 核数),增加并行提交 I/O 的通道。
  • 每个线程绑定独立的 CPU 核心,避免多线程竞争(需结合 cgroup 或 taskset 手动绑核)。

(2) 队列深度调优

  • 增大 bluestore_iodepth(如 128),允许单个线程提交更多未完成请求,填充 NVMe SSD 的硬件队列。
  • 动态调整:BlueStore 根据负载自动扩缩队列深度,避免因突发流量导致队列枯竭。

(3) 批量请求合并

  • 对小块随机写入(如 4K)合并为更大的连续请求(如 64K),减少 I/O 次数并提高 SSD 吞吐量(需 NVMe 支持 NVMe 命令融合)。

3. 性能问题排查与调优

(1) 监控指标

  • OSD 延迟分布

    1
    
    ceph osd perf      # 查看每个 OSD 的 commit/apply 延迟
    
  • AIO 队列状态

    1
    
    ceph daemon osd.<id> perf dump | grep bluestore -A 20
    

(2) 调优建议

  • SSD 配置

centos7.6 挂载镜像配置本地yum源

mount -o loop /opt/CentOS-7-x86_64-DVD-2003.iso /var/www/html/centos7/

条命令的作用是将一个ISO镜像文件挂载到一个指定的目录,以便可以通过网络或其他方式访问其内容。具体来说,命令的各个部分含义如下:

  1. mount:这是Linux系统中的一个命令,用于将文件系统(如硬盘分区、光盘、USB存储设备或ISO镜像文件等)挂载到一个指定的挂载点,使其内容可以被访问。

  2. -o loop:这是一个选项,表示使用循环设备(loop device)。循环设备是一种虚拟设备,允许将一个文件(如ISO镜像文件)当作一个块设备来挂载。这样,ISO文件中的内容就可以像一个真实的磁盘分区一样被访问。

  3. /opt/CentOS-7-x86_64-DVD-2003.iso:这是要挂载的ISO镜像文件的路径。ISO文件通常是一个光盘镜像文件,包含了操作系统的安装文件或其他数据。

  4. /var/www/html/centos7/:这是挂载点的路径,即ISO文件将被挂载到的目录。挂载完成后,ISO文件中的内容将可以通过这个目录访问

若客户现场使用了infiniband网络,请安装第三方Mellanox驱动 。驱动信息如下:

l   下载地址:http://www.mellanox.com/page/software_overview_ib

l   版本信息:MLNX_OFED_LINUX-5.0-2.1.8.0-rhel7.8-x86_64[JZ(1] 。


 [JZ(1]已修改

在 Linux 系统中,磁盘格式化和挂载是两个重要的步骤,它们的顺序和作用是相互关联的。以下是为什么需要先格式化磁盘再进行挂载的原因:

1. 格式化磁盘的作用

  • 创建文件系统:格式化磁盘的目的是在磁盘上创建一个文件系统(如 ext4xfs 等)。文件系统是操作系统用来组织和管理磁盘数据的结构,它定义了如何存储、检索和管理文件和目录。

  • 分配存储空间:格式化会将磁盘划分为多个逻辑区域,例如元数据(如文件名、权限、时间戳等)和实际数据存储区域。没有文件系统,操作系统无法识别磁盘上的数据结构。

  • 初始化磁盘:格式化会清除磁盘上的旧数据(如果磁盘之前有数据),并初始化磁盘的文件系统结构,使其处于一个干净的状态。

2. 挂载磁盘的作用

  • 将磁盘与文件系统关联:挂载是将格式化后的磁盘(或分区)与一个已存在的目录(挂载点)关联起来。挂载后,操作系统可以通过挂载点访问磁盘上的数据。

  • 使磁盘可用:挂载操作使得磁盘上的文件系统成为文件树的一部分,用户可以通过标准的文件操作命令(如 lscpmv 等)访问和管理磁盘上的数据。

  • 指定挂载点:挂载点是一个目录路径,它决定了磁盘数据在文件系统中的位置。例如,将 /dev/sdb 挂载到 /data/beegfs/beegfs_mgmtd/,意味着 /data/beegfs/beegfs_mgmtd/ 下的所有文件和目录实际上存储在 /dev/sdb 上。

3. 为什么必须先格式化再挂载

  • 文件系统是基础:挂载操作依赖于磁盘上已经存在的文件系统。如果没有先格式化磁盘,磁盘上没有文件系统结构,操作系统无法识别磁盘的内容,也就无法将其挂载到文件系统中。

  • 避免数据混乱:如果直接挂载一个未格式化的磁盘,操作系统可能会将其视为一个空设备,或者因为无法识别其内容而报错。格式化可以确保磁盘有一个清晰的结构,避免数据混乱或损坏。

  • 初始化存储:格式化磁盘是一个初始化过程,它为磁盘分配了存储空间和