赞
踩
写在前面:近期,参加2018/12/16日开源操作系统大会,听了华中科大在OSDI'18上的一个工作(《Write-Optimized and High-Performance Hashing Index Scheme for Persistent Memory》),结合研究生期间对 NVM的学习,整理以下内容。
一 非易失存储介质的机遇和挑战
1.1 机遇(这部分就直接贴我的毕业论文)
近年来数据密集型应用发展迅速,其密集的I/O 访问给页缓存系统造成了很大压力,内存系统需要提供足够的页面缓存空间来保证应用的服务质量,仅仅通过优化缓存替换算法已经不足以满足要求,构建大的缓存系统才是解决问题的根本途径。
传统的计算机系统使用DRAM 作为主存存储介质,然而DRAM 的存储机理导致了它存在高能耗和低集成度两方面的缺陷,限制了页缓存系统容量的扩充,具体体现着这样几个方面:
(1) 功耗问题:DRAM 利用电容中存储的电荷数的多少来表示逻辑上的0-1,但是电容会存在漏电的现象,如果电荷不足会导致数据出现差错,因此需要通过周期性预充电过程来保证数据不丢失,除了基础的数据存储操作所需能耗外,DRAM 设备为了保证数据可靠而产生的刷新能耗也给系统增加了很大的负担。据统计,典型的数据中心中,内存的耗电量占了整体计算机系统总耗电量的30%以上;在手机平板电脑等移动设备中,达到了21.8% 以上。
(2) 集成度问题:由于内部电容设备结构的制约,DRAM 设备难以进一步缩小。研究表明16nm 的集成密度已经是DRAM 技术的极限,而且DRAM 的集成度的提升是以牺牲访问性能为代价。
近年来,新型非易失存储技术得到了飞速的发展,为研究适合高效的存储系统带来了机遇。现今引起广泛关注的几种储器件主要有阻变存储器(Resistive Random Access Memory, RRAM)、相变存储器(Phase Change Memory, PCM),和自旋存储器(Spin-transfer Torque RAM, STT-RAM),它们同时具有了数据持久性和字节寻址的特性,为内存系统的变革提供了理论支持。
非易失存储介质的组成和设计原理的不同,使得它们在单元大小、单元存储位数、访问延迟、寿命和能耗开销等方面的特点有所不同。下表对比了多种新型非易失存储器件和传统意义上的存储介质在性能方面的差异。非易失存储介质由于其:低能耗、容量易于扩展,将有潜力取代DRAM作为计算机内存。
1.2 挑战
为什么NVM引用于索引会引发这么多新的思考?因为NVM具有以和DRAM截然不同的性质:
从硬件层面上:
① 硬件上可擦除次数有限。不同于DRAM,NVM的擦写次数有限,这个特性有点类似于用来做SSD的Flash介质(在NAND闪存中每个块的最大擦写次数是一百万次,而NOR的擦写次数是十万次),所不同的是Flash作为外存介质本身所承担的写次数就要相对内存少很多,因为内存层本身对写就有很多优化策略来优化写(因为毕竟外存相对内存速度还是很慢的),比如writeback、日志技术,屏蔽了很大一部分写。
② 不对称的读写延迟和能耗.STT-RAM 和PCM 在读访问操作时间的量级都与DRAM较为接近,约在20ns 左右,写操作
所需时间上,NVM 存在着读写不均衡的问题,例如,PCM 的写性能表现较差,写操作所需时长约为读的5 倍左右。
③ Write optimization matters【这个我不理解】
从应用层面看:
由于NVM其非易失的特性,一旦掉电数据仍然可以保持。
二 NVM在对于操作系统中索引结构的一些机遇和挑战
2.1 常见的索引方式对比: 树形索引结构 VS HASH索引结构
| 树形索引结构 | HASH索引结构 |
Pros | 对于区间索引有优势
解释:由于树的性质(左子树上的所有值小于根节点,右子树上的所有节点大于根节点,如此迭代),所以树的节点本身就是有序的。
多说一句,MYSQL 的innodb引擎中表数据本身就是按B+ Tree组织的一个索引结构;MyISAM的索引也是使用B+ Tree树,所不同的是索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。 | 对于定值索引有优势,都是O(1)时间。
解释:我们不考虑哈希冲突,对于查询和插入都是可以通过哈希函数直接计算到索引位置,故为O(1)时间;即便有哈希冲突,通常都是用一个链表存放该索引位置的多个冲突数据,只要该哈希索引的散列性质不太差都不会出现冲突数据很多的情况,所以对冲突数据的寻址时间也可以认为是O(1)。
据说,【memcache和redis就是用的hash索引方式】,由于我也没研究过,之后再求证。 |
Cons | 对于定值插入和查找操作都是O(logN)量级
解释:这个很好理解,就是树高 | 不支持区间索引
解释:用哈希索引做区间查询简直是噩梦,需要对每一个值都进行不断的查找。 |
NVM在树形索引上的应用相关文章:
CDDS B-tree [FAST’11], NV-Tree [FAST’15], wB+-Tree [VLDB’15], FP-Tree [SIGMOD’16], WORT [FAST’17], FAST&FAIR [FAST’18]
2.2 哈希索引中NVM技术面临的挑战和机遇
传统哈希索引并不适用于NVM,主要体现在以下几点:
① 当前技术不能很好的应用NVM对于非易失的特性不能充分应用。
之前的很多策略都是针对DRAM掉电数据丢失的特性设计的,有很多为了维持一致性而增加的开销,这对于NVM是可以改进的。比如:Cache line flush:linux内核中的Buffer和PageCache的作用是利用部分不用的内存用了缓存数据,那对于cache中的脏数据,需要定期的或者主动调用flush来进行数据同步,否则更新就会丢失;logging:比如说MYSQL中,如果每一条数据都需要访问磁盘,找到那条记录在做更新,那么代价会非常大,目前MYSQL就是采用日志(WAL write ahead logging)的方式,通过将更新写入内存中的日志,然后由checkpoint触发数据的同步。诸如buffer、logging技术都需要考虑数据一致性问题,如果使用NVM,那么以上两个机制就会有很大的改善空间。【论文中还提到到了copy-on-write,暂时不理解NVM对于COW中有什么可以被改进的地方】
② 当前已经存在的算法不能很好的适应NVM读写不对称的特性降低写操作会带来性能的损耗。
针对DRAM设计的哈希算法通常都使用格外的写来解决哈希冲突问题[INFLOW’15, MSST’17],也有人提出了写友好的哈希算法,例如(PCM-friendly hash table (PFHT) [INFLOW’15], Path hashing [MSST’17])但是都是以降低访问性能为代价的。
③ 哈希表的扩容问题
当哈希表已经不能处理新的元素插入时,就要进行resize,将hash表大小扩容,传统的扩容方式就是out-of-place,然后依次将旧表中的哈希元素挪到新表上,扩容的过程中会消耗内存(因为是需要新开辟一块空间嘛,而且通常都是旧表两倍大小),消耗CPU(因为需要对元素进行重新的哈希计算),且在扩容过程中对于新的操作(应该指查询,这个过程中不知道是否允许修改和插入)可能会涉及到两张表,如果一张表上没有该元素,就要去另一张表查找。
哈希表的resize过程会引起很多写操作和memoryfence,严重损耗NVM性能,写操作这个很好理解,memory fence是什么呢?
2.3 已有的哈希算法表现:
[1] B. Debnath et al. “Revisiting hash table design for phase change memory”, INFLOW, 2015.
[2] P. Zuo and Y. Hua. “A write-friendly hashing scheme for non-volatile memory systems”, MSST, 2017
2.3.1 Cuckoo
2.3.1.1 简要介绍
Cuckoo是一种解决哈希冲突的方法,其基本思想是用2个哈希函数来处理地址冲突。哈希函数是成对的,每一个元素都有两个,分别映射到两个位置,一个是记录的位置,另一个是备用位置。这个备用位置是处理碰撞时用的。cuckoo hashing处理碰撞的方法,就是把原来占用位置的这个元素踢走,插入操作如下:
第一步: 对key值hash,生成两个hash key值,hashk1和 hashk2, 如果对应的两个位置上有一个为空,那么直接把key插入即可。
第二步: 否则,任选一个位置,把key值插入,把已经在那个位置的key值踢出来。
第三步: 被踢出来的key值,需要重新插入,直到没有key被踢出为止。如此往复。直到被踢的次数达到一个上限,才确认哈希表已满,并执行rehash操作。
2.3.1.2 性能分析
|
| 解释 |
Memory Efficiency | Good | 相对于传统的哈希算法,cuckoo可以通过2个哈希函数降低哈希冲突,从而提高空间利用率 |
Search | Good | O(1) |
Deletion | Good | O(1) |
Insertion | Bad | 一个插入过程可能会引起踢掉一个位置的元素,引起连锁位置变动,这个效应称为Cascading write[INFLOS'15],甚至可能引起resize,因此为Bad |
NVM writes | Bad | 当哈希空间负载率很高时,引起的额外写代价很大,eg:大于50%时,When the hash table has a high load factor, e.g., > 50%, an insertion usually causes tens of eviction operations [MSST, 2017] |
Resizing | Bad | resize操作要求所有数据都执行reHash操作,时间复杂度为O(n) |
Consistency | Bad |
|
2.3.1.3 Cuckoo hash的一些变种
Hopscotch hashing ,发表于HERLIHY, M. , SHAVIT, N . , ANDTZAFRIR , M . Hopscotch Hashing. In DISC ( 2008) .
是cuckoo哈希算法的改进版,提高了其利用率和并发度[INFLOS'15]。该算法处理碰撞的方法是分配连续的备选位置,连续分配的位置称为hop distance,
通常hop distance大小设为cache line的整数倍,这样可以较好的利用CPU,因为CPU每次访问数据都是一次性读入一个cache line大小。
对于插入操作。插入操作如下:
第一步: 对key值hash,根据hash值找到位置,如果为空直接把key插入即可。
第二步: 否则,依次遍历hop distance找到一个空位置,把key值插入。
第三步: 如果hop distance中没有空位置,则尝试将该范围内的元素剔除,被踢出来的key值,需要重新插入,直到没有key被踢出为止。如此往复。
直到被踢的次数达到一个上限,才确认哈希表已满,并执行rehash操作。
总结:因为NVM在性能上的读写不对称性,Cuckoo和Hopscotch在insert操作时,cascading write会引起极大的性能降低。
2.3.2 FPHT
(待更新)
2.3.3 Path Hashing (MSST'17)
2.3.3.1 简要介绍
与2-path和cuckoo hash算法一样,Leveling Hash也是使用两个hash函数来降低哈希冲突,所不同冲突时他们的处理方式。Leveling Hash 在逻辑上构建了一个二叉树结构,叶子节点是原始映射的哈希地址,非叶子节点是发生哈希冲突时的备选位置。实际上存储方式是如右图所示的扁平的结构,每个叶子节点的备选位置的地址是直接可以通过计算得到的。不同level的entry分布在不同的内存页,所以不同level搜索的过程可以并发起来
2.3.3.2 详细介绍
① l的备选路径path-l的地址计算算法
叶子节点l的备选路径path-l的地址计算算法如下:
【举例说明】以上图L=3的树为例,叶子节点l(存在l-th位置)的备用节点的位置计算如下:
9 13 15
l=l/2取下整 = 1
P[1]=9=1 + 2^4 - 2^3
P[2]=13=1 + 2^4 - 2^2
P[3]=15=1+ 2^4 - 2^1
② 插入元素
使用两个哈希算法h1,h2计算出哈希位置l1和l2,
如果l1或l2位置为空(也就是token==0),那么就直接将元素插入空的位置,并把token值置为1;
如果l1或l2位置不为空(也就是token==1),那么用①中的算法找到位置l1和l2的备选路径path-l1,path-l2,
如果path-l1,path-l2有空的位置,找到路径上空的位置插入,并把token值置为1;
如果path-l1,path-l2没有空的位置,那么就说明需要resize哈希表了。
③ 删除元素
删除过程就是插入过程的逆过程
2.3.3.3 性能分析
(待更新)
2.3.4 Level Hashing
(待更新)
References:
<Cuckoo Hash 基本思想和代码实现> https://blog.csdn.net/suwei19870312/article/details/7442786
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。