|
|
|
@ -71,6 +71,26 @@ zk实现分布式锁主要利用其临时顺序节点,实现分布式锁的步
|
|
|
|
|
4. Set集合:集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一 样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。利用 Set 的交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
|
|
|
|
|
5. Sorted Set有序集合(跳表实现):Sorted Set 多了一个权重参数 Score,集合中的元素能够按 Score 进行排列。可以做排行榜应用,取 TOP N 操作。
|
|
|
|
|
|
|
|
|
|
### Redis - ziplist、quicklist、listpack
|
|
|
|
|
|
|
|
|
|
ziplist 是一个特殊双向链表,不像普通的链表使用前后指针关联在一起,它是存储在连续内存上的。
|
|
|
|
|
|
|
|
|
|
1. zlbytes: 32 位无符号整型,记录 ziplist 整个结构体的占用空间大小。当然了也包括 zlbytes 本身。这个结构有个很大的用处,就是当需要修改 ziplist 时候不需要遍历即可知道其本身的大小。 这个 SDS 中记录字符串的长度有相似之处,这些好的设计往往在平时的开发中可以采纳一下。
|
|
|
|
|
2. zltail: 32 位无符号整型, 记录整个 ziplist 中最后一个 entry 的偏移量。所以在尾部进行 POP 操作时候不需要先遍历一次。
|
|
|
|
|
3. zllen: 16 位无符号整型, 记录 entry 的数量, 所以只能表示 2^16。但是 Redis 作了特殊的处理:当实体数超过 2^16 ,该值被固定为 2^16 - 1。 所以这种时候要知道所有实体的数量就必须要遍历整个结构了。
|
|
|
|
|
4. entry: 真正存数据的结构。
|
|
|
|
|
5. zlend: 8 位无符号整型, 固定为 255 (0xFF)。为 ziplist 的结束标识
|
|
|
|
|
|
|
|
|
|
连锁更新是 ziplist 一个比较大的缺点,这也是在 v7.0 被 listpack 所替代的一个重要原因。
|
|
|
|
|
|
|
|
|
|
ziplist 在更新或者新增时候,如空间不够则需要对整个列表进行重新分配。当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起「连锁更新」问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降。
|
|
|
|
|
|
|
|
|
|
quicklist 的设计,其实是结合了链表和 ziplist 各自的优势。简单来说,一个 quicklist 就是一个链表,而链表中的每个元素又是一个 ziplist。
|
|
|
|
|
|
|
|
|
|
listpack 也叫紧凑列表,它的特点就是用一块连续的内存空间来紧凑地保存数据,同时为了节省内存空间,listpack 列表项使用了多种编码方式,来表示不同长度的数据,这些数据包括整数和字符串。在 listpack 中,因为每个列表项只记录自己的长度,而不会像 ziplist 中的列表项那样,会记录前一项的长度。所以,当我们在 listpack 中新增或修改元素时,实际上只会涉及每个列表项自己的操作,而不会影响后续列表项的长度变化,这就避免了连锁更新。
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
|
|
|
|
### Redis 的数据过期策略
|
|
|
|
|
|
|
|
|
|
Redis 中数据过期策略采用定期删除+惰性删除策略
|
|
|
|
@ -149,6 +169,22 @@ Redis默认是快照RDB的持久化方式。对于主从同步来说,主从刚
|
|
|
|
|
3. 用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
|
|
|
|
|
4. value的大小:redis可以达到1GB,而memcache只有1MB。
|
|
|
|
|
|
|
|
|
|
### Redis6.0 多线程的实现机制
|
|
|
|
|
|
|
|
|
|
在 redis 6.0 以前,完整的 redis 线程模型是 主线程(1个)+ 后台线程(三个),三个后台线程分别处理:
|
|
|
|
|
1. 关闭 AOF、RDB 等过程中产生的大临时文件
|
|
|
|
|
2. 将追加至 AOF 文件的数据刷盘(一般情况下 write 调用之后,数据被写入内核缓冲区,通过 fsync 调用才将内核缓冲区的数据写入磁盘)
|
|
|
|
|
3. 惰性释放大对象(大键的空间回收交由单独线程实现,主线程只做关系解除,可以快速返回,继续处理其他事件,避免服务器长时间阻塞)
|
|
|
|
|
|
|
|
|
|
Redis 6.0之后,Redis 正式在核心网络模型中引入了多线程,也就是所谓的 I/O threading,至此 Redis 真正拥有了多线程模型。一般来说,一个正常的客户端请求会经历 建立连接、IO就绪监听/读、命令执行、IO写等一系列操作。
|
|
|
|
|
1. 主线程负责接收建立连接请求,获取 socket 放入全局等待读处理队列
|
|
|
|
|
2. 主线程处理完读事件之后,通过Round Robin 将这些连接分配给这些IO线程。
|
|
|
|
|
3. 主线程阻塞等待IO线程读取socket
|
|
|
|
|
4. 主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但并不执行回写 socket
|
|
|
|
|
5. 主线程阻塞等待 IO 线程将数据回写 socket 完毕
|
|
|
|
|
|
|
|
|
|
Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。
|
|
|
|
|
|
|
|
|
|
### Redis的几种集群模式
|
|
|
|
|
|
|
|
|
|
1. 主从复制
|
|
|
|
@ -843,6 +879,10 @@ Kafka中消息是以topic进行分类的,生产者通过topic向Kafka broker
|
|
|
|
|
* acks = 1:意味若 Leader 在收到消息并把它写入到分区数据文件(不一定同步到磁盘上)时会返回确认或错误响应。在这个模式下,如果发生正常的 Leader 选举,生产者会在选举时收到一个 LeaderNotAvailableException 异常,如果生产者能恰当地处理这个错误,它会重试发送悄息,最终消息会安全到达新的 Leader 那里。不过在这个模式下仍然有可能丢失数据,比如消息已经成功写入 Leader,但在消息被复制到 follower 副本之前 Leader发生崩溃。
|
|
|
|
|
* acks = all(这个和 request.required.acks = -1 含义一样):意味着 Leader 在返回确认或错误响应之前,会等待所有同步副本都收到悄息。如果和min.insync.replicas 参数结合起来,就可以决定在返回确认前至少有多少个副本能够收到悄息,生产者会一直重试直到消息被成功提交。不过这也是最慢的做法,因为生产者在继续发送其他消息之前需要等待所有副本都收到当前的消息。
|
|
|
|
|
|
|
|
|
|
另外可以设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的 Producer 能够自动重试消息发送,避免消息丢失。
|
|
|
|
|
|
|
|
|
|
除此之外,可以设置 unclean.leader.election.enable = false。这是 Broker 端的参数,它控制的是哪些 Broker 有资格竞选分区的 Leader。如果一个 Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成消息的丢失。故一般都要将该参数设置成 false,即不允许这种情况的发生。
|
|
|
|
|
|
|
|
|
|
### Kafka消息是采用Pull模式,还是Push模式
|
|
|
|
|
|
|
|
|
|
Kafka最初考虑的问题是,customer应该从brokes拉取消息还是brokers将消息推送到consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉取消息。push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式。Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达。
|
|
|
|
@ -855,11 +895,46 @@ Kafka最初考虑的问题是,customer应该从brokes拉取消息还是brokers
|
|
|
|
|
4. 批量发送:Kafka允许进行批量发送消息,先将消息缓存在内存中,然后一次请求批量发送出去
|
|
|
|
|
5. 数据压缩:Kafka还支持对消息集合进行压缩,Producer可以通过GZIP或Snappy格式对消息集合进行压缩
|
|
|
|
|
|
|
|
|
|
### Kafka中broker数量和partition数量设置
|
|
|
|
|
|
|
|
|
|
1. 一个partition最好对应一个硬盘,这样能最大限度发挥顺序写的优势。一个broker如果对应多个partition,需要随机分发,顺序IO会退化成随机IO。
|
|
|
|
|
2. 当broker数量大于partition数量,则有些broker空闲,此时增加partition会带来性能提升。而且是线性增长。
|
|
|
|
|
3. 当两者相等,则所有broker都启用,吞吐达到瓶颈。
|
|
|
|
|
4. 继续增加,则broker会不均衡,有点会分到更多的partition,顺序IO退化成随机IO。
|
|
|
|
|
|
|
|
|
|
### Kafka中partition数量和消费者数量设置
|
|
|
|
|
|
|
|
|
|
消费者的数量不应该比分区数多,因为多出来的消费者是空闲的,没有任何帮助
|
|
|
|
|
|
|
|
|
|
### Kafka判断一个节点还活着的两个条件
|
|
|
|
|
|
|
|
|
|
1. 节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接
|
|
|
|
|
2. 如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久
|
|
|
|
|
|
|
|
|
|
### 如何提升 Kafka 生产者的吞吐量
|
|
|
|
|
|
|
|
|
|
1. buffer.memory:设置发送消息的缓冲区,默认值是33554432,就是32MB。如果发送消息出去的速度小于写入消息进去的速度,就会导致缓冲区写满,此时生产消息就会阻塞住,所以说这里就应该多做一些压测,尽可能保证说这块缓冲区不会被写满导致生产行为被阻塞住。
|
|
|
|
|
2. compression.type:默认是none,不压缩,但是也可以使用lz4压缩,效率还是不错的,压缩之后可以减小数据量,提升吞吐量,但是会加大producer端的cpu开销。
|
|
|
|
|
3. batch.size:设置merge batch的大小,如果 batch 太小,会导致频繁网络请求,吞吐量下降。如果batch太大,会导致一条消息需要等待很久才能被发送出去,而且会让内存缓冲区有很大压力,过多数据缓冲在内存里。默认值是:16384,就是16kb,也就是一个batch满了16kb就发送出去,一般在实际生产环境,这个batch的值可以增大一些来提升吞吐量。
|
|
|
|
|
|
|
|
|
|
### Kafka中的副本
|
|
|
|
|
|
|
|
|
|
在kafka中有topic的概念,每个topic会包含多个partition,而副本所描述的对象就是partition,通常情况下(默认情况下kafka集群最少3个broker节点),每个partition会有1个leader副本,2个follower副本。3个partition副本会保存到不同的broker上,这样即使当某个broker宕机了,由于副本的存在,kafka依旧可以对外提供服务。
|
|
|
|
|
|
|
|
|
|
在partition 副本机制中,只有leader副本可以对外提供读写服务,follower不对外提供服务,只是异步的从leader中拉群最新的数据,来保证follower和leader数据的一致性。
|
|
|
|
|
|
|
|
|
|
当leader所在broker宕机挂掉后,kafka依托Zookeeper提供的监控功能 ,能够实时的感知到,并立即在存活的follower中选择一个副本作为leader副本,对外提供服务,当老的leader副本重新回来后,只能作为follower副本加入到集群中。
|
|
|
|
|
|
|
|
|
|
虽然利用offset可以解决主从切换数据不一致问题,但是会导致消息写入性能下降,牺牲了kafka的高吞吐的特性。kafka自身也考虑到了,主从切换对数据一致性的影响,采取了ISR来降低数据的不一致。
|
|
|
|
|
|
|
|
|
|
ISR本质上是一个partition副本的集合,只不过副本要想进入这个集合中是有条件的。这个条件是:和leader副本中的数据是同步的follower副本才可以进入到ISR中。
|
|
|
|
|
|
|
|
|
|
用来衡量follower是否符合“和leader副本是同步的”,不是依据两者数据的差异量,而是两者数据不一致持续的时间,如果持续的时间过长,那么就认为两者不同步。其实这种设定也是有道理的,试想:发送者一次批量发送了大量的消息,这些消息先写入到leader中,此时follower中没有这些数据,那么所有follower都变成和leader是不同步的了,但是不足之处也很明显:ISR中和leader数据差异比较大的follower,是有可能被选为新的leader的。
|
|
|
|
|
|
|
|
|
|
上述持续时间的长短有参数 replica.lag.max.ms 来定义,默认10s,如果ISR中的follower副本和leader副本数据存在差异的时间超过10s,那么这个follower副本就会被从ISR中踢出出去,当然如果该follower后续慢慢追上了leader副本,那么这个follower就会被重新添加到ISR中,也就是说iSR是一个动态的集合。
|
|
|
|
|
|
|
|
|
|
kafka 把所有不在 ISR 中的存活副本都称为非同步副本,通常来说,非同步副本落后 leader 太多,因此,如果选择这些副本作为新 leader,就可能出现数据的丢失。毕竟,这些副本中保存的消息远远落后于老 leader 中的消息。在 Kafka 中,选举这种副本的过程称为 Unclean 领导者选举。Broker 端参数 unclean.leader.election.enable 控制是否允许 Unclean 领导者选举。
|
|
|
|
|
|
|
|
|
|
## Dubbo
|
|
|
|
|
|
|
|
|
|
### Dubbo的容错机制
|
|
|
|
|