本地全量缓存

全量缓存

  • 背景及目标

当前我们的数据流向是这样的:Hdfs->redis->程序内部缓存->缓存。存在redis性能不足。读写过于频繁。写频繁主要是从hdfs一条一条的读取, 没有批量写入。因为程序还不太稳定, 经常会重启,内部缓存被清空, 内存不大命中率低等原因, 导致读取效率不高。所以我们需要一个缓存解决以上这些问题,它得具有这些特征:

1.1 独立进程 --- 解决引擎重启数据丢失的问题

1.2 批量写入 -- 支持从hdfs中读取批量数据, 批量入库

1.3 全量缓存 -- 如果直接对接hdfs的话, 自己就相当于数据源头, 需要缓冲全量数据。(假设如果只缓存一部分数据,查找没有命中该缓存,去hdfs检索该数据? 几乎不可能的事情)

  • 功能
  1. 缓存数据
  2. 从hdfs, kafka拉取数据同步数据
  3. 支持c++, java接口从缓存中获取,删除数据, 甚至过期数据

4 网络优化,支持简单集群

  • 总体规划
  1. 缓存应该使用什么算法比较快?

缓存主要是用来查找的,目前查找效率最高的算法是哈希表。性能最好的情况下可以达到O(1), 最差的情况下(全部冲突,在一个堆里线性存储),性能为O(n), 平均性能为O(1+E)(E为冲突系数)

  1. 数据同步

目前数据推送过来是采用hdfs的,整个文件的推送。 将来为了更加实时的可能采用kafka推送。在这些过程中数据量会比较大, 占用大量带宽。 我们可能需要对数据进行压缩, 为了同步更快, 可能同步二进制流。

 

  1. 集群

当若干个缓存组成缓存集群, 且之间需要相互同步时候, 需要进行集群管理.

 

  1. 并发支持

主要考虑多线程, 以及减少锁冲突技术。 分段锁和读写复制减少锁技术或者采用乐观锁技术。

 

  1. 支持故障恢复

当程序崩溃时, 快速重启能够恢复。 有2种方法: 备份内存数据到文件, 从其他集群节点拿数据恢复, 都能加快重建过程。

 

  1. 共享内存

相对网络传输(用户buff->网络 网络->接收进程), 共享内存(内存->接收进程)更快。

  1. 支持jni接口
  • Hashmap优化

hashmap的组成主要由以下几个部分组成.

  1. hash函数

Hash函数要求均匀分布。 且尽量用&计算桶位置代替%提高hash效率

  1. hash冲突

有两种办法冲突采用链表形式或者二次散列法(实现复杂,性能更高些,因为可以利用cache line加速技术)

--- 因为本程序是全量加载就不应该存在冲突问题。

  1. 数据淘汰

数据淘汰比较有名的是lru算法, 是在资源不太够的情况, 淘汰非热点, 旧的数据, 并且回收的时候批量回收的算法。

-- 因为数据是推送过来的, 不能主动去拉取, 所以不需要进行数据淘汰。

当然还有一些数据跟关键字是否绑定在一起的问题, 主要还是考虑cache line 问题。

  • 共享内存

共享内存是最快速的进程间通信机制。操作系统在几个进程的地址空间上映射一段内存,然后这几个进程可以在不需要调用操作系统函数的情况下在那段内存上进行读/写操作。

共享内存与指针

程序之间可见部分内存,只有共享内存。 而且每次映射的共享内存地址很有可能不相同。

要用一些模板库就需要一些分配器。 分配器要实现内存分配, 释放, 以及元素位置和地址的之间的转换.

  • 设计图

6.1 集群结构图

                         6.1 集群结构图

  • 相关的库

主流的GNU C++和MSVC++出于编译器扩展的目的实现了hash_map,SGI有hash_map的实现,Boost也有类似于hash_map的unordered_map实现,google有dense hash_map和sparse hash_map两种实现(前者重时间效率,后者重空间效率)

  • 性能监控
  1. 主要监控以下几个指标

内存使用情况

平均查询时间

最近平均查询时间

  • 问题
  1. 当内存过小, 怎么办?

因为本身设计就是全量保持数据的, 作为最初数据源头, 所以不支持内存过小的情况

猜你喜欢

转载自blog.csdn.net/petershuang/article/details/85062621