面经[随缘待续]

发布于 2021-12-13  54 次阅读


不想背八股文了, 随缘面试. 别问, 问就不会.

面经

操作系统

进, 线程

进程是什么?

进程就是一个正在执行的程序, 每个进程都有自己的PCB, 使得每个进程都能单独运行, 是资源分配的最小单位.

PCB是什么?

PCB中记录了进程当前状况的信息和管理进程所需的信息. 主要包含以下四个方面的信息.

  1. 外部标识符(用户), 内部标识符(系统).
  2. CPU信息, 主要是寄存器: 通用寄存器(用户), 指令计数器, 程序状态字, 用户栈指针.
  3. 调度信息: 进程状态, 进程优先级及其他进程调度算法需要的信息, 阻塞原因.
  4. 进程信息: 进程程序区和数据区指针, 进程同步和通信机制, 进程运行所需的资源清单, 下一个PCB的首地址.

进程间的通信方式有那些?

  1. 共享内存: 共享一些数据结构, 共享内存中的一篇区域.
  2. 管道: 匿名管道, 命名管道.
  3. C/S模式: RPC远程过程调用(存根), Socket.
  4. 消息传递通信: 直接消息传递(对称, 非对称), 间接消息传递(邮箱通信).

Socket分为文件型和网络型, 文件型的Socket是一个特殊文件, 网络型的Socket是协议+IP+端口号.

线程是什么?

为了提高并发度, 创建了新的CPU调度基本单位--线程. 每个线程都有一个TCB.

TCB是什么?

TCP中包含了所有控制和管理线程的信息. 其中包含以下内容:

  1. 唯一的线程标识符.
  2. 部分寄存器: 通用寄存器(用户), 指令计数器, 程序状态字.
  3. 栈指针: 指向这个线程存储局部变量和返回地址的栈的指针, 指向核心栈的指针.
  4. 调度信息: 线程优先级.
  5. 线程信息: 线程运行状态, 线程专用存储区(用于上下文切换), 信号屏蔽.

CPU调度

作业调度算法有那些?

  1. 先来先服务(FCFS).
  2. 短作业优先(SJF).
  3. 优先级调度算法(PSA).
  4. 高响应比优先调度算法(HRRN).

响应比 = (等待时间 + 响应时间) / 等待时间

进程调度算法有那些?

  1. 轮转调度算法.
  2. 优先级调度算法(静态优先级, 动态优先级).
  3. 多级反馈队列调度算法.

实时调度算法有那些?

  1. 最早截至时间优先算法.
  2. 最低松弛度优先算法.

松弛度 = 完成截至时间 - 剩余运行时间 - 当前时间

死锁

死锁的必要条件?

  1. 互斥(资源不可同时被多个进程使用).
  2. 不可抢占(资源必须等进程自己主动释放).
  3. 保持和请求(进程在已有资源的前提下再去申请资源才可能死锁)
  4. 循环等待(资源分配图中有闭环).

死锁的解决方法?

  1. 预防: 预防死锁的必要条件.
  2. 避免: 进程申请资源时先预测是否存在安全序列, 若无安全序列不分配资源, 避免死锁发生.
  3. 检测和解除: 死锁发生后检测出来然后解决.

检测方法: 化简资源分配图, 不能完全化简的进程一定发生死锁(充分条件).

死锁解除的算法?

  1. 终止全部进程.
  2. 逐个终止进程.

内存管理

静态链接和动态链接?

静态链接:

将多个模块一同链接为一个完整的模块(装入的时候要修改相对地址和外部调用符号).

动态链接:

装入时动态链接: 向内存中装入一个新模块时将其依赖的所有模块一同链接.

运行时动态链接: 当程序执行到需要调用外部接口时才将所需要的模块链接.

连续分区分配算法有那些?

基于顺序搜索
  1. 首次适应.
  2. 循环首次适应.
  3. 最佳适应.
  4. 最坏适应.
基于索引搜索
  1. 快速适应.
  2. 哈希.
  3. 伙伴系统.
页面置换算法
  1. 最佳置换算法.
  2. 先进先出算法.
  3. LRU算法.

IO

设备控制器的作用?

  1. 接收和识别CPU的命令.
  2. 识别设备地址, 存储设备状态信息(状态寄存器).
  3. 控制CPU和设备进行数据交换(总线, 数据寄存器), 设置了数据缓冲区, 兼管差错检测.

中断程序分类?

  1. 屏蔽中断.
  2. 嵌套中断.

没答上来的

操作系统原子操作的实现?

  1. CPU从内存中读写一个字节时其他的CPU无法使用这个字节(有些CPU还可以保证从缓冲行读写16/32/64bit时是原子的).
  2. 缓冲行通过MESI协议保证原子操作.
  3. 总线锁.

IO操作模型

  1. 阻塞IO模型(阻塞socket, Java BIO).
  2. 非阻塞IO模型(socket).
  3. IO复用模型(redis, nginx, Java NIO).
  4. 信号驱动IO模型.
  5. 异步IO模型(Java AIO).

select, poll, epoll的区别

select

轮询多个文件描述符, 文件描述符数量有上限. 通过拷贝使内核和用户传递消息.

poll

轮询多个文件描述符, 文件描述符数量无上限. 通过拷贝使内核和用户传递消息.

epoll

由事件去触发进程读/写.

水平触发, 只要内核缓冲区不空/满, 都触发读/写.
边缘触发, 当缓冲区满/空, 或接收/送出数据时可被动触发, 或调用epoll_ctl可读/可写参数主动触发.

计算机网络

常见端口号

协议端口
http80
https443
ftp21(20传输)
smtp25
pop3110
dns53
snmp161

常见状态码

状态码说明
200服务器成功返回用户请求的数据
201用户新建或修改数据成功
202一个请求已经进入后台排队(异步任务)
207用户删除数据成功
400用户发出的请求有错误, 服务器没有进行新建或修改数据的操作
401用户没有权限(令牌, 用户名, 密码错误)
403用户的访问是被禁止的
404用户发出的请求针对的是不存在的记录
405控制器, 方法不存在
422当创建一个对象时, 发生一个验证错误
500服务器发生错误

数据结构与算法

红黑树

  1. 根节点和叶节点都是黑色.
  2. 红色节点的子节点是黑色.
  3. 某个节点的所有路径上的黑色节点数量相同.

红黑树的高度至多是2log~2~(n+1).

查找算法

  1. 折半查找.
  2. 插值查找.
  3. 斐波那契查找.

排序算法

sort

排序方法时间复杂度空间复杂度稳定性
冒泡排序O(n^2^)O(1)稳定
直接插入排序O(n^2^)O(1)稳定
简单选择排序O(n^2^)O(1)稳定
快速排序O(nlog~2~n)O(log~2~n)不稳定
堆排序O(nlog~2~n)O(1)不稳定
归并排序O(nlog~2~n)O(n)稳定
希尔排序O(nlog~2~n)O(1)不稳定
计数排序O(n)O(k)稳定
基数排序O(nlog~10~n)O(log~10~n)稳定
桶排序O(n)O(k)由桶内的排序算法决定

快速排序

优化
  1. 选取中枢: 随机选取, 三数取中, 九数取中.
  2. 不必要的交换.
  3. 优化递归.
  4. 优化小数组.

Hash

Hash算法

  1. 取模余数法.
  2. 直接定址法(线性函数).
  3. 平方取中法.
  4. 折叠法.
  5. 随机数法.
  6. 数字分析法.

解决Hash冲突

  1. 开放地址法.
  2. 链地址法.
  3. 再Hash法.
  4. 公共溢出区法.

装填因子 = 表中记录数 / Hash表长度

Hash动态扩容

  1. 一次性rehash.
  2. 渐进式rehash.

CPP

多态和重载

其他

常指针和指针常量

指针常量: const修饰类型.

int const *p;
const int *p;

常指针: const修饰类型指针(效果等同引用).

int * const p;

堆和栈的区别

  1. 分配和管理的不同.
  2. 空间大小和增长方向的不同和是否产生碎片.

new和malloc的区别

特征new/deletemalloc/free
分配内存的位置自由存储区
内存分配成功的返回值完整类型指针void*
内存分配失败的返回值默认抛出异常返回NULL
分配内存的大小由编译器根据类型计算得出必须显式指定字节数
处理数组有处理数组的new版本new[]需要用户计算数组的大小后进行内存分配
已分配内存的扩充无法直观地处理使用realloc简单完成
是否相互调用可以,看具体的operator new/delete实现不可调用new
分配内存时内存不足客户能够指定处理函数或重新制定分配器无法通过用户代码进行处理
函数重载允许不允许
构造函数与析构函数调用不调用

Tips

  1. define只是在预处理时做的替换.
  2. []的优先级比*高.

MySQL

索引

索引有那几种类型?

主键索引, 唯一索引, 普通索引, 全文索引(?).

创建索引的原则?

  1. 最左前缀匹配原则.
  2. 查询频繁, 更新不频繁, 数据重复度低.
  3. 有外键的属性列.
  4. 属性非空(复合属性不全空).
  5. 属性区分度高.
  6. 属性长度短.

对于定义为text, image和bit的数据类型的列不要建立索引.
索引扩展?

百万级别或以上的数据如何删除?

先删除索引, 再删除数据, 再重建索引.

B和B+树的优缺点

B

优点:

  1. 查询频繁的数据放在离根节点近的节点可以提高查询效率.

缺点:

  1. 查询效率不稳定.
B+

优点:

  1. 查询效率稳定.
  2. 节点中可以存放更多的键值.
  3. 范围查询更高效. 不仅可以随机查询还可以顺序查询. 增删节点效率更高.
  4. B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据.

缺点:

  1. B优点取反.

B+树索引和Hash索引的优缺点?

Hash

优点:

  1. 适合用于等值查询.

缺点:

  1. 无法范围查询.
  2. 不支持索引排序.
  3. 任何时候都会回表查询.
  4. 大量属性值相同时会发生Hash碰撞, 此时等值查询效率降低.
B+

同上

聚簇索引和辅助索引的优/劣势?

聚簇索引

优势:

  1. 数据存储在叶节点中, 读取时从内存中读取, 效率高.
  2. 聚簇索引可以范围查询, 排序.

劣势:

  1. 插入新行或者主键更新导致分页时需要修改叶子节点数据, 维护成本高(插入速度严重依赖于插入顺序).
  2. 插入新值速度慢(防止键值重复).

聚簇索引在每个表中只有一个, 且是建立在主键上面的.

辅助索引

优势:

  1. 出现行移动或者数据页分裂时, 辅助索引不需改动, 仅当主键发生改变时, 才会更新二级索引, 辅助索引的维护成本降低.

劣势:

  1. 查询效率低.

辅助索引查询时也不一定回表. eg: 覆盖索引.

索引失效

  1. 违反最左前缀法则. (使用 '>', '<' ... 其他范围查询后, 该索引列后的索引都将失效.)
  2. 查询时在索引列上进行操作: 运算, 类型转换...(字符串不加单引号会发生类型转换导致索引失效)
  3. 查询的结果集超过总数的30%后索引失效, 改为使用遍历.
  4. 使用不等于, NOT IN等其他否定符号后失效. (覆盖索引除外)
  5. 用or分割开的条件, 如果or前的条件中列有索引, 而后面的列中没有索引, 那么涉及到的索引都不会被用到. 联合索引, 各索引列间使用'or'失效.

order by, group by, 排序时也会使用索引, 违背最左前缀法则或者待排序列无索引, 所有索引都失效.

索引优化

  1. 符合创建索引的原则.
  2. 避免索引失效.

事务

事务是什么?

事务是数据处理的最小操作单元, 是一组不可在分割的操作集合.

ACID是什么?是怎么实现的?

A

原子性, 事务中的操作要么全都执行, 要么全都回滚.

实现原理: 事务执行时, 会同时将逻辑操作写入回滚日志(undo log). 发生故障时根据回滚日志反向执行事务发生时的逻辑操作, 撤销更改.

C

一致性, 事务执行前后的数据完整性一致.

实现原理: 保证事务的AID特性, 保证数据的实体完整性, 参照完整性, 用户自定义完整性.

I

隔离性, 事务执行时隔离其他事务, 不被其他事务干扰.

实现原理: MVCC隔离事务写操作对读操作的影响, 锁机制隔离事务写操作对写操作的影响(所有写操作都要加x锁).

D

持久性, 事务执行后对数据库的改变是持久的.

实现原理: 事务执行前, 会先将逻辑操作写入重做日志(redo log). 数据修改丢失时根据重做日志重新执行操作.

MVCC是什么?

InnoDB会在每行数据后追加事务ID, 回滚指针, 隐式主键. 事务ID是当前最新的更新事务的ID, 回滚指针是指向改行数据的undo log, 隐式主键是一个隐藏自增主键, 在无主键时使用. ReadView维护当前时刻所有已提交事务ID, 活跃事务ID, 还没开始的事务ID. RC级别使用实时的ReadView, RR级别使用事务开始时的ReadView.

乐观锁和悲观锁是什么?

乐观锁: 乐观的认为事务间发生数据竞争的概率很低, 执行事务的途中不考虑读未提交, 不可重复读, 幻读问题, 直到提交时检查对应数据的版本号是否改变, 若改变则回滚.(CAS, 待补)

悲观锁: 悲观的认为每次数据操作都会出问题, 所以每次操作都要先获得悲观锁, 排它锁和共享锁都属于悲观锁.

乐观锁和MVCC无关, 乐观锁解决的写写冲突, MVCC负责读写冲突.

行锁(InnoDB)和表锁(Server)是什么?

行锁: 给某一行的数据的索引上锁(如果检索条件无索引, 将使用表锁.).

表锁: 给一张表的数据上锁(必须在事务开始时将所有要使用的表上锁, 事务结束时再全部释放.).

排它锁和共享锁是什么?

排它锁: 获取资源的x锁后可以读写, 禁止其他线程获得该资源的s, x锁(记录锁, 间隙锁, 临键锁都是排它锁).

共享锁: 获取资源的s锁后可以读, 禁止其他线程获得该资源的x锁.

锁的读是当前读而不是MVCC的快照读.

记录锁, 间隙锁和临键锁是什么?

记录锁: 封锁单个唯一索引记录的锁.

间隙锁: 封锁多个唯一索引记录或单/多个普通索引间的间隙的锁, 左开右开.

临键锁: 间隙锁 + 记录锁, 左开右闭.(还会锁住最后一个记录的右边所有区间)

记录锁, 间隙锁和临键锁都是排它锁.
临键锁在匹配到一条记录时退化为行锁, 没匹配到任何记录时退化为间隙锁.

插入意向锁是什么?

插入意向锁: 插入意向锁是一种特殊的间隙锁(行级锁), 在插入一行数据获取该行的排它锁前先要获得该间隙的插入意向锁.

意向排它锁和意向共享锁是什么?

意向排它锁: 事务要获取某些行的排它锁,必须先获得表的意向排它锁.

意向共享锁: 事务要获取某些行的共享锁,必须先获得表的意向共享锁.

意向锁都是表锁, 是为了让行锁和表锁共存(表锁加锁会和意向锁互斥, 意向锁不与任何行级锁互斥), 由数据库引擎自己维护.

自增锁是什么?

自增锁: 自增锁是特殊的表锁, 当要插入数据时给整张表自动加表锁, 执行完插入语句后立即释放.

日志

redo log(InnoDB)

redo log的作用是什么?

当发生故障时还有脏页未刷新到磁盘中时, 故障解决后可使用redo log恢复.

redo log是如何记录的?

在事务开始时就产生了redo log. 在事务中的操作执行后将操作写入redo log, 在事务相关的脏页被刷入磁盘中后就可以重用redo log的空间了, 在脏页被刷入磁盘前将redo log刷入磁盘.

redo log file是固定大小, 循环记录的. write pos表示当前记录的逻辑序列号(LSN), check point表示已经被刷新的脏页中的数据的位置. 当write pos要追上check point时, 会先刷新脏页.
每次启动时都会进行redo log恢复: 如果数据页的LSN小于重做日志的LSN, 则从check point开始恢复.
redo log包含两部分: 内存中的日志缓冲(redo log buffer), 硬盘中的日志文件(redo log).
redo log是物理日志, 记录的是变更后的数据, 所以恢复的时候很快.

redo log是如何从缓冲区刷到硬盘的?
延迟写

每秒将redo log buffer写入os buffer, 同时调用fsync()将os buffer中的数据写入磁盘.

实时写,延迟刷

事务提交时就将redo log buffer写入os buffer, 每秒调用fsync()将os buffer中的数据写入磁盘.

实时写,实时刷

事务提交时就将redo log buffer写入os buffer, 同时调用fsync()将os buffer中的数据写入磁盘.

其他

以下情况发生的时候, redo log buffer中的内容都会被刷入redo log file.

  1. redo log buffer空间不足.
  2. 实例关机.
  3. binlog切换.
  4. 做checkpoint.(待补)

undo log(InnoDB)

undo log的作用是什么?

undo log可用于事务回滚和MVCC查看先前版本的数据.

undo log是如何记录的?

事务开始时, 执行操作后都会写入相反的操作: insert->记下主键, delete->软删除, update->记下旧数据(若包含主键则是先删除再加)到undo log, 事务提交后, insert undo log页面链表都会被删除(重用), update undo log保留, 定时清理update undo log和已软删除的数据.

undo log页面写入的时候同样会写入redo log, 因为undo log也要持久化.
一个事务最多拥有四个页面链表: 普通表的 insert undo 链表, 普通表的 update undo 链表, 临时表的 insert undo 链表, 临时表的 update undo 链表. 一个页面链表对应一个段.(页面链表用时才分配)
当一个页面链表只有一页时且使用量小于3/4即代表该页面链表可被其他事务重用.
undo log是逻辑日志.

binlog(server)

binlog的作用是什么?

binlog中有数据库所有的写操作, 当需要复制数据库的时候可以使用binlog.

binlog是如何记录的?

当事务提交的时候会将写操作都记录到binlog中. 有以下3种方式将其刷入磁盘中.

  1. 由系统自动判断.
  2. 每个事务提交后都要写入.
  3. 每N个事务提交后写入.
binlog的格式有那些?
  1. statement: 记录原生的sql语句. 优点: 生成的文件小, 易阅读. 缺点: 有些数据会出错, 比如当前时间, 当前用户名...
  2. row: 记录行修改的数据. 优点: 可正确存储所有数据. 缺点: 生成的文件太大.
  3. mixed: 默认使用statement, statement会出错时使用row.

errorlog

errorlog的作用是什么?

帮助分析MySQL运行中出现的问题.

errorlog包含了什么?
  1. 服务器启动和关闭过程中的所有信息.
  2. MySQL运行中的错误.

general query log

general query log的作用是什么?

调试环境下开启general log有助于分析问题. 如: 分析哪些语句执行密集, 执行密集的select语句对应的数据是否能够被缓存...

general query log包含了什么?

记录了数据库执行的所有命令, 不管语句是否正确, 都会被记录.

slow query log

slow query log的作用是什么?

当MySQL遇到性能瓶颈时, 用于分析问题.

慢查询会导致CPU, IOPS, 内存消耗过高.

slow query log包含了什么?

记录了超过指定时间的所有命令.

relay log

relay log的作用是什么?

主从复制时, 主节点保存的binlog复制到从节点的relay log.

relay log包含什么?

记录了从主节点binlog复制过来的所有数据.

MySQL执行过程

主从复制, 读写分离

主库负责写, 从库通过binlog和relay log负责读.

解决主从同步的延迟

  1. 强制读主.
  2. 写请求必须等主从同步才能算完成.
  3. 加一层中间件.
  4. 加一层缓存.

Innodb和Myisam的区别

区别InnodbMyisam
事务YN
外键YN
行锁YN
聚簇索引YN
是否保存表的行数NY

分库分表

水平/垂直分库分表.

分库分表策略

  1. hash取余.
  2. 根据主键划分.
  3. 按照时间, 地理位置划分.

Redis

外部接口数据类型

String

二进制安全的字符串, 故可存储图像和其他序列化对象. 内部存储的数据结构为sds和long.

ps: 若value为数字则转为long.

Hash

hash表. 内部存储的数据结构为dict和ziplist.

ps: hashmap中键值对个数不超过512且所有的value不超过64字节时使用ziplist. 反之使用dict.

List

内部存储的数据结构为双向链表, 可作deque使用. 内部存储的数据结构v3.2前使用linkedlist和ziplist, 之后都使用的是quicklist.

ps: list中元素个数不超过512且所有的元素不超过64字节时使用ziplist, 反之使用linkedlist.

Set

存储不重复值的无序集合. 内部存储的数据结构是dict和intset.

ps: set中元素个数不超过512个且都是整数时使用intset, 反之使用dict.

Sorted Set

存储不重复值的有序集合. 内部存储的数据结构是zset和ziplist. 加入了score参数, 根据score参数来排序. zset由dict和skiplist组成, dict用来查询数据到分数的对应关系, 而skiplist用来根据分数查询数据.

ps: set中元素个数不超过128个且所有的元素不超过64字节时使用ziplist, 反之使用zset.

内部数据结构

#### Sds

sds是redis用来存储字符串的数据结构.

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

一共有五种header故一共五种数据结构, 让不同长度的字符串使用不同大小的header是为了减少内存.

  • len: 字符串的真正长度, 不包括结尾的"\0".
  • alloc: 字符串的最大容量, 不包括结尾的"\0".
  • flags: 表示header类型.
  • buf: 字符串数组, 为兼容C语言字符串, 真正的字符串数据后以"\0"作结尾符. 为防止频繁修改字符串存储大小, 会有空余未用字节.

header是指结构体中除真正存储数据的字符串数组buf外的其他参数.

Dict

dict是一个用于维护key-value映射关系的数据结构.

dict本质是一个hash表, 当装载因子超出阈值(0.1~1)时会扩/缩容, 触发重哈希, 重哈希采用的是增量式哈希.

Redis的一个database中所有key到value的映射, 就是使用一个dict来维护的.

Ziplist

ziplist是一个用于存储字符串或整数的数据结构.

ziplist本质是一个双向链表. 链表中各项采用变长的方式存储, 而且使用的是一块连续的内存.

整数是按真正的二进制表示进行编码的, 而不是编码成字符串序列.

Quicklist

quicklist是一个用于存储字符串或整数的数据结构.

quicklist本质是一个双向链表. 链表中的所有节点都是ziplist, 这样就结合了双向链表和ziplist的优点.

可规定每个ziplist节点的元素个数或节点所占字节大小.

Intset

intset是一个用于存储整数的数据结构.

typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;

intset是一个有序整数集合, 便于使用二分法查找.

  • encoding表示集合中每个数的存储类型, 分为三种: int16, int32, int64.
  • length表示集合中元素个数.
  • contents是存储整数的数组.

新创建的数组都以int16作为编码方式, 当增加新数据时根据数据大小调整编码方式.

Skiplist

skiplist是一个用于存储字符串或整数的数据结构.

skiplist本质是一个拥有多级索引的有序链表.

持久化

RDB

为当前的数据库数据创建快照存储于特定文件中.

手动备份:

  • 使用bgsave命令fork新子进程异步备份, 然后子进程调用save函数.
  • 使用save命令同步备份.

save函数流程

save

自动备份:

  • 使用serverCron中save m n配置规则自动触发bgsave.
  • 主从复制时, 主节点自动bgsave.
  • 未开启AOF时, 关机时自动bgsave.
  • ...

save m n 表示m秒内有n个key的值发生变化就备份.

AOF

每次执行指令后将指令追加到文件中.

流程
  1. 将执行的命令追加到AOF缓冲区末尾.
  2. 将AOF缓冲区内容传入内核缓冲区, 根据配置(always/no/everysec)来决定是否将内核缓冲区内容写入磁盘.
  3. 精简AOF命令, 重写到AOF文件.

always: 每次都调用fsync(), 是安全性最高, 性能最差的一种策略.
no: 不会调用fsync(). 性能最好, 安全性最差.
everysec: 仅在满足同步条件时调用fsync(). 这是官方建议的同步策略, 也是默认配置, 做到兼顾性能和数据安全性, 理论上只有在系统突然宕机的情况下丢失1秒的数据.

混合持久化

流程
  1. 根据配置文件, 先以RDB或AOF方式写入文件前半部分.
  2. 再将重写缓冲区的增量AOF命令追加到AOF文件后半部分.

恢复时亦要根据文件前半部分持久化方式分为两种情况恢复, 一为纯AOF方式, 二为前半部分使用RDB后半部分使用AOF.

RDB和AOF的优缺点

RDB
优点
  1. 因为RDB是一个紧凑压缩的二进制文件, 代表Redis在某一个时间点上的数据快照, 所以非常适合用于备份, 全量复制等场景. 适用于灾难恢复, 数据迁移. 且恢复速度更快.
  2. 因为RDB是fork一个新的子进程所以不影响Redis性能.
缺点
  1. 因为RDB文件一个固定格式的二进制文件, 所以可读性和兼容性很差.
  2. 因为RDB是fork的一个子进程所以占用两倍内存. 子进程会占用CPU, 导致数据量比较大时会消耗更多的时间. 子进程拷贝了父进程的数据, 在子进程持久化的时间段中无法增加新数据, 故无法实现实时或秒级持久化.
AOF
优点
  1. 因为AOF文件是明文, 故可读性, 兼容性高.
  2. 根据fsync配置, 可以实现实时或秒级持久化.
缺点
  1. 因为写入的是指令, 所以即使有重写机制, 但是在相同的数据集情况下, AOF文件通常比RDB文件大.
  2. 因为要频繁的将内存缓冲区命令同步到AOF文件, 所以对Redis性能有影响.

Nginx

Nginx负载均衡算法

加权轮询

参数解释
  1. effective_weight: 有效权重. 初始值为配置文件中规定的weight值.
  2. total: 所有服务器的effective_weight.
  3. current_weight: 当前权重. 初始值都为零.
算法
  1. 遍历集群中所有服务器, 计算当前权重, current_weight += effecitve_weight. 计算total.
  2. 选择current_weight最大的服务器, 并执行 current_weight -= total.
  3. 若返回有误, effective_weight--. 若返回正常且effective_weight小于配置文件中的值, effective_weight++.

IP哈希

ip哈希的应用场景主要是在分布式缓存.

算法
  1. 通过hash算法计算IP对应得hash值.
  2. 根据所得hash值以及各个服务器对应得权重确定将要处理请求的服务器.

由于ip, hash算法以及各个服务器的权重都是固定的, 所以是一个固定的服务器来处理同一个IP的所有请求.

一致性哈希

算法
  1. 生成节点权重乘以160的数量的虚拟节点, 并根据hash值将所有虚拟节点放置于hash环上对应位置, 若有hash值相同的节点只取第一个.
  2. 通过hash算法计算IP对应hash值, 置于hash环中, 然后顺时针寻找距离最短的节点来处理该ip的请求.

最少连接

参数解释
  1. weight: 配置的权重.
  2. conns: 服务器连接数量.
算法
  1. 计算集群中每个服务器的conns/weight, 选取最小的来处理请求.
  2. 若最小conns/weight的服务器有多个, 则这几个服务器之间采用加权轮询.

场景应用

QPS限流

漏桶算法

  1. 请求进入漏桶, 若漏桶已满则溢出.
  2. 漏桶按照固定速率出请求.

无法处理突发大量请求, 突发大量请求会有部分被抛弃.

令牌算法

  1. 等待上次请求的预发令牌都已发出(令牌按照固定速率生成).
  2. 按照当前的请求数量预发请求的令牌.

人生如逆旅,我亦是行人。