Mapreduce
因为mapreduce是数据一定落磁盘的,不涉及内存中的数据进行RPC通讯传输。所以,mr引入了环形缓冲区思想。
环形缓冲区内存大小是固定的,并且完全不涉及垃圾回收。所以这就是mr为什么非常稳定的极其重要原因之一
Hadoop NameNode
1 将NameNode数据传输压力交给JournalNode角色解决
2 将单点故障镜像备份,每隔几秒帮NameNode日志与镜像的定期合并(为了减少下次重启启动时间)交给SecondaryNameNode解决
3 重要:分段加锁和双缓冲方案,高效写磁盘
内存双缓冲:
磁盘写优化为内存写,通过两块内存CurrentBuffer和SyncBuffer,两个默认512mb
client数据直接写入CurrentBuffer,当CurrentBuffer写满后交换内存到SyncBuffer
通过SyncBuffer写入磁盘,CurrentBuffer可以继续写入数据不影响写入性能
正在执行写磁盘操作时,会判断当前txid是否<=写磁盘数据的最大threadlocal txid,来判断是否写入内存中
如果小于等于最大txid则直接return,因为正在执行写磁盘的操作已经包含了该数据。(批处理)
写磁盘分段加锁:
写内存,内存转换分段加锁,写磁盘不加锁
flush 最耗费时间的写磁盘操作没有加锁,分段加锁相互不影响
Kafka
kafka的数据RPC通讯是模仿的netty,所以涉及到了大量netty的优秀思想
1 零拷贝
2 javaNIO reactor设计模式
3 缓冲区对读写channel的解耦
4 文件读取的高性能
1 磁盘顺序写
2 跳表设计快速定位文件
跳表中key为文件名的偏移量,多层索引提高定位文件效率,空间换时间
3 定位文件后,通过稀疏索引快速定位文件中的数据
稀疏索引则存在上面图中展示ide xxx.index 中
5 内存池
内存池
ConcurrentHashMap> batches 数据结构
1 数据批处理16kb发送,缓存中不同分区的多个批数据队列,如果在同一个节点上,kafka会合并这些队列为一个请求发送,提高网络传输性能
2 内存池设计 -避免内存中批信息频繁full GC
目的1:当发送端数据量极大时,ByteBuffer就会无限制地频繁申请,可能会引发OOM
目的2:发送完数据后,ByteBuffer就会释放,会频繁的引发FullGC
内存池一共32mb,每个16kb的批信息创建后记录在内存池中,来达到以固定的内存空间来复用内存
当缓冲池满了以后,也就是说消息写入的速度大于向broker发送的速度,那么就阻塞写入,直到缓冲池中有空余内存时为止
6 自定义实现高性能适合多读少写的CopyOnWriteMap,batches = new CopyOnWriteMap<>() 具体看前文文章
7 写入缓存对应批次的分段加锁
Hbase
Hbase前文
由高延迟的HDFS随机写,优化为1次磁盘顺序写+1次内存排序写
1 查询先查内存的读缓存,没有再查磁盘
2 内存数据设计良好高性能的数据结构 (ConcurrentSkillListMap),排序后的跳表,读缓存查询快
3 磁盘数据 + 布隆索引
4 范围分区 + 排序
先排序,然后范围分区 + 分段扫描
注:排序好后位置已确定,磁盘中再插入不好解决。所以数据先放入内存中排序,插入时则内存中位置转换即可,最后溢写磁盘
5 跳表结构
元数据表记录各个数据分段存在哪些节点,大大提高性能。然后再创建root元数据表记录各个元数据表分段信息,形成跳表的思路
1.x 之前默认每个region最大128mb,空间很小所以加入三层,user table,meta table, root table
1.x 之后默认每个region最大10G,空间变大后改为两层,去掉了root table
另外客户端中还会缓存元数据
6 读缓存 + 写缓存
内存缓存会做常用的热缓存,提高缓存命中率。数据先写内存,内存不足时,则数据内存中排序后有序的写入磁盘,读写缓存分离
当读磁盘时,读取涉及多个磁盘文件,添加布隆过滤器来判定数据是否在这个磁盘文件中,不在则直接下一个磁盘文件不用扫描
当磁盘中分段数据太大,超过10G则split拆分为2,split是直接拆分,依然前后保持数据有序。