思考总结技术知识
Kafka
引以为傲的特性:顺序写、零拷贝
顺序写保证高性能,写入速度快。其并不是直接刷到磁盘,而是先提交到内存缓冲区,不用太担心机器宕机数据丢失,因为有ISR成员,通过参数设置同时写入几个才代表生产者发送消息成功,只要这几个成员不同时宕机就不会有问题,为了容灾我们可以配置跨机房、跨地域!
零拷贝保证高性能消费。这是操作系统的特性,如果操作系统不支持那么也就没这个特性了。意思就是消费者消费的数据直接从内核态拷贝到网卡发送给消费者。如果消费者版本低V1版本,而生产者产生的消息都是V2版本的,那零拷贝就失效了,因为需要Broker中转一下将消息解析并转化成V1,效率极低,因此我们要保证生产者消费者Broker等消息是一致的。
压缩就不说了。就是降低传输量同时存储成本也会降低(压缩后传输数据小,带宽占用就小了),要权衡带宽和生产者CPU使用情况,如果CPU很充足带宽不充足时则开启压缩,如果CPU已经很繁忙了,还开启压缩,无谓“雪上加霜”
概念:
-
Broker:kafka实例,多个Broker组成Kafka集群
-
处理过程
-
一个线程,进行Reactor响应式转发给网络线程池(默认3个线程)
-
网络线程池写入到共享队列中
-
IO线程池(默认8个线程)取出共享队列中的请求进行处理
-
对于repli_min_factor为1的,处理完本机的写入后即返回给对应网络线程,否则先存入缓冲队列,等其余负责的ack再返回给对应网络线程
-
网络线程接收到了后直接返回响应即可
-
-
-
Topic:
-
Partition
-
Leader副本
-
Follower副本
-
分片在同一个消费者组下,只能被其中某个消费者消费
-
-
Producer
-
TCP连接
-
和bootstrap.server下配置的所有ip建立连接
-
可配参数设置的定时更新集群元数据时,发现与某些Broker没建立连接则会建立
-
发送消息时发现与某个broker没建立连接则会建立
-
断连:超过可配参数的时间内没与Broker通讯过,会断开连接
-
-
发送过程
-
经过序列化器、拦截器、压缩器、转发器 到本地收集器(和要发往的Topic分片一致)
-
由生产者刚启动时的Sender线程,去本地收集器,每个分片内是一块块存储的,当大小达到可配置的参数值或者时间超过了可配置的参数值时,会发送给对应Broker的分片,这些未接收到回应的消息存在暂存队列,等收到发送成功即真正成功
-
-
-
Consumer Group
-
TCP连接
-
向任意一个Broker发送FindCoordinator请求(组id的哈希 % 50),默认内部偏移量Topic50个分片,不建议修改
-
向刚刚找到的Coordinator的Leader副本建立连接
-
拉取消息时,即pull时建立连接
-
pull的话就是在这个tcp上一直就请求的,那tcp就不会关了
-
-
拉取过程
-
根据偏移量从Leader副本上取数据,手动提交偏移量(Broker是无状态的,具体消费什么消息都是消费者自己根据偏移量索取的)
-
-
-
Consumer
-
虽然发送和消费都是Leader副本,但是Consumer只能看到已提交的消息,对于当前Leader副本最新写入的消息不一定是可见的!
-
Lead和Lag
-
Lead(这个一般不看):消费者当前消费的偏移量和分片最早的消息之间的差值(当值消费特别慢,数据过期了导致消息丢失)
-
Lag(这个看的多):消费者当前消费的偏移量和目前可消费的最新消息之间的差值(代表落后多少)
-
-
-
Coordinator(协调者)
-
消费者组相关
-
位移管理
-
Rebalance
-
增减topic
-
增减分片
-
增减消费者
-
上面2个是运维可接受
下面那个防止消费者误判导致进行不必要的Rebalance。解决方案是:增大消费者向Coordinator的心跳时间,也不能太大,否则真正故障了这个分片一直没实例消费,Lag越来越大
-
-
-
Controller
-
Broker间
-
Leader选举通知,元数据信息同步(它的元数据信息是从ZK获取的)
-
Redis
可以用来干什么?
TPC的模式,基本单位是RedisObject
-
缓存(高性能读写)
-
单线程IO复用
-
-
分布式锁(进程外共享)
-
单实例实现(value对应当前唯一id,实现简单,缺点是可靠性不高,实例挂了资源不可用)
-
删除要使用Lua脚本,否则如果当前读取value是当前id,但是刚好过期时间到了,锁被其他实例申请了,这时候删除锁就删错了!
-
-
多实例实现
-
RedLock。依次向各节点申请加锁,半数以上实例成功代表申请成功,否则向全部实例发送释放锁请求
-
-
内部结构什么样?
-
支持String、List、Hash、Set、Sorted Set
-
对于节省内存,底层做了很多优化。RedisObject如果是Long则使用int编码,Zset如果个数小于[可配置的值(是不是能配置不确定,好像默认是1000)]使用压缩列表
-
-
持久化
-
aof
-
always、everysec、no 【选择每秒,实现是异步1s同步到磁盘上,最多丢失1s的数据,算是性能和可用性的权衡】
-
aof重写
-
bgsave。fork子进程完成
-
-
-
rdb
-
二进制文件
-
bgrewriterof。fork子进程(fork过程随着内存大小阻塞时间加长),过程变化使用操作系统的COW,因此对于写入特别多的,内存最多可能翻2倍!
-
-
高可靠怎么支持?
-
主从架构
-
复制缓冲区(每个从库接入主库时,主库都会触发fork进程生成rdb,同时期间以及后续增量的命令都是这个缓冲区发送的)
-
复制积压缓冲区(环状的缓冲区。用于判断主从同步进度,如果主库写入覆盖掉了从库还未写入的,那个从库会再次出发新接入时的操作,即主库rbd发送给它,它来加载这样)
-
-
主-从-从架构
-
减少主库fork进程、复制缓冲区用的主库内存,把一部分从挂在从库下
-
-
分片集群
-
避免单实例量过大。每个分片实例采用主从架构
-
-
哨兵机器(下线、选主、切新)
-
下线需大于等于qutonum判断主观下线
-
切新需要大于等于qutonum且半数以上哨兵同意(半数是集群最初启动的哨兵的半数,而不是现在存活哨兵的半数,即原来5个哨兵,挂了3,如果qutonum为2,那2个都判定主库下线,主库不可写入,但是切新是切不了的,因为判定主观下线的哨兵数2 < 3)
-
-
脑裂
-
由于主库CPU负载过高一直未响应哨兵的应答,导致误判下线,切换期间主库又恢复可用了,从而客户端向这个主库写入成功了。
-
解决:心跳应答超时时间设置长一些,但是也不能太长,否则出现故障了一直不执行切主,业务不可用时间就会边长。
-
-
Mysql
-
Server层
-
连接器、缓存器、分析器、优化器、执行器
-
binlog(二进制的逻辑日志)
-
buffer pool 【数据页(干净页、脏页等)、change buffer(用于存放更新但是数据并未在内存数据页中记录,加速更新)】
-
-
执行引擎层
-
innodb等
-
写入两阶段提交:redo log prepare、binlog、redolog commit.(是crash-safe的)
-
redo log
-
索引B+树(聚簇索引、非聚簇索引)
-
-
MVCC
-
undo log 逻辑日志
-
快照读:select
-
当前读:增删改 和 查询加共享锁或者for update等
-
-
ACID
-
锁
-
看行锁就行。Mysql的锁是加在索引上的
-
RR下有间隙锁。个人理解RR下可能得幻读有个情况是:update一条开始查不到,但是其他事务插入的记录,此时记录事务id是自己,再查询就能看到了,就是幻读
-
RR下有间隙锁,这个想了解清楚还是得复习看看45讲,其实大致就是几个原则,45讲有写。
-
-
高可用
-
主从架构
-
relay log(同步方式可以同步,也可以异步)异步的话从切主可能会数据不一致
-
-
-
为什么索引采用B+树而不是B数?
-
B+树叶子节点有指针,范围查询效率更高。
-
B+树的真实记录都存在叶子节点,这样每个数据块大小有限情况下,每个块容纳的索引个数更多,这样树的高度会更低,IO次数也就更少了。
-
-
Mysql的Decimal(4,2):最长有4位,最多2位小数(即小数部分2位,整数部分最多[4-2]=2位)
-
0 --> 0.00
-
1.2 --> 1.20
-
123.1 --> 异常无法入库
-
1234.1 --> 异常无法入库
-
-
explain的一些看法
-
-
mysql之binlog与redolog落盘时机
-
binlog之binlog_flush_flag(事务提交之后才会记录binlog)
0:内存中,缓冲满了或者mysqk关闭才会落盘
1:写入Page Cache
2:直接强制刷到磁盘
redolog之innodb_flush_log_at_trx_commit
事务提交时点:将redolog缓冲区写入page cache/刷盘
0:不写磁盘交操作系统管理(满了则写入page cache)
1:事务提交则写入磁盘(说性能影响大,应该是直接刷到磁盘上了)
2:每隔1s写入磁盘(1s刷一次盘)
-
-
binlog_flush_flag 指的是 binlog 日志缓存使用的一种策略,其参数均默认为 0,表示优先将 binlog 日志记录在内存中,等到缓存满了,或者 MySQL 关闭的时候才落盘。
-
当取值为 0 时,表示采用的是内存缓冲区刷盘的策略。当缓冲区满了或 MySQL 关闭时,才会将 binlog 日志落盘。
-
当取值为 1 时,表示采用的是写入 Page Cache 的策略。每写入一条 binlog 记录,就会将其写入到 Page Cache 中,等到操作系统决定刷入磁盘。
-
当取值为 2 时,表示采用的是直接强制刷到磁盘的策略,每写入一条 binlog 记录,就会直接强制将其刷到磁盘。
innodb_flush_log_at_trx_commit 则是 InnoDB 引擎中控制事务日志(redo log)写入方式的参数。其默认取值为 1。
-
当取值为 0 时,表示事务提交时不进行写入,而是等到操作系统按照 Page Cache 决定什么时候刷入磁盘,可能会有丢失数据的风险。这是性能最好的一种方案。
-
当取值为 1 时,表示事务提交时立即将事务日志写入磁盘,确保数据的可靠性。这是性能最差但数据最稳定的一种方案。
-
当取值为 2 时,表示事务提交时将事务日志写入内存中的缓冲区,每秒钟往磁盘中刷入一次事务日志。这种方案可以减轻写入磁盘对性能的影响,但是可能会出现一些数据丢失的情况。
-
-
-
kafka在rebalance为什么可能出现重复消费的问题?
-
在 Kafka 中,当消费者组成员发生变化或者有新的消费者加入消费者组时,就会触发 Rebalance 操作,以重新分配对分区的消费权。在 Rebalance 过程中,Kafka 会先将分区的消费权交给新成员或者新分配的消费者,然后再撤销旧成员或者原分配的消费者对分区的消费权。
在 Rebalance 过程中可能会出现重复消费的问题,主要原因有两个:
-
原有消费者已经消费的消息在队列中仍然存在但未被提交(即消费过程中未发送 Acknowledge)。在 Rebalance 过程中,这些未提交的消息会被新的消费者重新消费。
-
新成员或者新分配的消费者在进行消费之前,该分区已经有了消息被原有消费者消费但还未被提交。这些消息也会被新成员或者新分配的消费者重新消费一遍。
为了避免出现重复消费,可以将 Kafka 中的 auto.offset.reset 参数配置为 latest,这样消费者在消费时会从最新的 offset(即最新的消息)开始消费,跳过已经消费过的消息。此外,在消费过程中尽量避免长时间占用分区的消费权,及时将已经消费的消息提交 Acknowledge,避免在 Rebalance 过程中出现未提交的消息。
-
-