SSDB学习

ssdb把数据存储到磁盘上,性能接近于redis, 但又不像redis那样纯内存那样昂贵,所以可以存储数据量大,且对于读写速度要求大于传统DB的情况。

1. LSM原理 + 异步: (Log-Structured-mechanism), 磁盘内容的所有操作,都将会被顺序地写入一个类似日志的结构中,从而提供文件的写入效率。(通过异步)。同时该日志中还包含了索引信息以提高日志的读效率。日志会被划分为多个段(segment)来进行管理,很适合哪种存在大量小文件写入的场景。简而言之,LSM就是将对数据的更新操作暂时缓存在内存中,当达到一个指定的值,再将其批量写到磁盘,在写入过程中跟已有的数据做merge。在merge过程中,需要耗时较多,而此时会有新的数据写入,如果加锁,则会影响性能。所以采用的办法是,在merge前,先新建一块新的内存来接受新数据的写入, 而原先的内存结构只能读不能写。

2. 缓存:每个文件存储一个范围区间的数据,并且数据在文件中有序存储,可以为每个文件数据建立索引,从而提高数据的读取速度,为了让该模型的性能更接近直接内存操作,需要在该模型中引入缓存机制,包括索引缓存和块数据缓存,如果读取的数据局部性较好,则性能会明显提高。

3. 分布式关键性指标
1)可扩展性:可扩展性分为横向扩展和纵向扩展:
-- 横向扩展:通过添加新节点(增加新机器)
-- 纵向扩展:通过升级现有系统服务(可能是升级处理器、增加内存,或者更换整个机器)

2)数据一致性:对于分布式系统来说,数据有时是需要存储在多台机器上的,比如横向扩展,将数据同时存储在多台机器上,每台机器的数据是相同的,彼此来避免单机的性能瓶颈问题,提高系统的吞吐量。

CAP原理:
-- 一致性(Consistency): 所有节点在同一时间具有相同的数据
-- 可用性(Availability): 保证每个请求不管成功或者失败都有响应
-- 分区容忍(Partition tolerance): 系统中任意信息的丢失或者失败不影响系统的继续运作。
定理认为,分布式系统只能满足三项中的两项,而不可能满足全部三项。
对于分布式系统来说,网络故障等因素是不可避免的,因而系统必须具备足够的容错能力。因此只能在一致性和可用性之间做平衡的选择。

同步复制架构:
在分布式系统中,为了提高系统的吞吐量,需要多个节点来存储相同的数据,读取数据时可以均衡的从节点中选择一个来读取,这就需要维持各个节点之间的数据一致性来保证从任何一个节点中读取数据时都是最新的、一致的。对于大部分业务来说,数据只需要实现最终一致性即可,即在一个有限的可接受的时间范围内实现数据的一致,这个过程一般通过同步复制来完成。以MySQL为例,就是一台mysql server(slave)从另一台mysql server(master)进行日志的复制,然后再解析日志,并应用到自身。比较经典的同步复制架构有:Master-slave, Master-Master, Master-Slaves-Slaves, Master-Master-Slaves.

1)Master-Slave: 在实际应用中,通常都是向Master MySQL Server上写数据,然后从一个Master上复制到一个或者多个Slave上,然后从Slave上读。
2) Master-Master: 在简单的Master-Slave结构中只有一个Master, Master是单点运行,容易出现故障,且有时也需要对Master进行维护。因此一般采用给Master增加一个备用的master, 当master出现故障或者停机维护时,备用的master就充当master提供服务,在这个过程中需要维护maser和备用master之间的同步
3)Master-Slaves-Slaves:在有些应用场景中,可能读写压力差别比较大,比如读压力特别大。一个master可能需要10台或者攻多的slave才能支撑住读的压力,这时Master就比较吃力。因此可以将Master复制到一部分slaves上,然后剩下的slave再从这些slaves上复制

4. Merge-Dump存储引擎
Merge-Dump是一种通用的分布式存储引擎,同时支持数据的读、写、删除及顺序扫描,其分布式依赖于google的分布式文件系统GFS。
为了防止系统宕机,丢失数据,所有的写操作首先写日志(tablet log),然后再写入额到内存中的MemTable,当内存中的MemTable达到一个门限值时,就讲其归并导出(Merge-Dump)到磁盘,以支持数据的持久化,形成key有序的SSTable文件。由于写日志的过程是顺序的添加一条新记录,非常符合磁盘的顺序IO特性,因此有非常高的写操作性能。

5. SSDB的系统架构
ssdb参考了google的levelDV,采用了bigtable的merge-dump作为存储引擎,牺牲随机读换随机写,实现了数据的高效持久化存储。
SSDB作为存储系统,数据记录的存储介质包括内存以及磁盘文件,主要由一下部分组成:内存中的MemTable和Immutable MemTable以及磁盘上的Log文件和SSTable文件。

SSDB架构下的数据读写原理如下:
1)所有的更新都先写Log,再写MemTable, 数据的更新以追加新纪录的方式进行
2)MemTable中的数据达到一个门限值时,创建新的MemTable和Log文件,并将原MemTable转为Immutable MemTable, 等待后台进程将其Dump到磁盘,形成key有血的SSTable文件。
3)后台进程定期对SSTable文件进行归并排序,形成有层次的SStable文件结构
4)读数据时先读内存中的MemTable, 再读内存中的Immutable MemTable,最后读磁盘中的SSTable文件。

总体来说,ssdb所有记录都存储在Memtable, Immutable MemTable, SSTable文件中,Immutable Memtable在结构上和Memtable完全一样,区别仅在于Immutable Memtable是只读的,不允许写操作。当memtable占用的内存达到一个门限值时,则自动转换为Immutable Memtable,等待dump到磁盘中,同时会创建一个新的memtable供写操作写入新数据。
Memtable提供数据的读、写、删除操作接口,删除某个key的记录是以插入一条新纪录完成的,但会打算一个key的删除标记,真正的删除操作是惰性的,由以后的merge过程来做。

1)Memtable中的记录是key有序存储的,在系统插入新的记录时,要将其查到合适的位置上保持这种key的有序性。Memtable采用skiplist作为数据结构,取代传统的红黑树,由于skiplist对于树的平衡实现是基于一种随机算法,因此对skiplist的插入和删除都比较容易实现。redis为了提升写操作性能,也使用skiplist来作为核心数据结构存储数据。

2)Log文件主要用于系统宕机后恢复数据。如果没有Log文件,因为刚写入的记录是保存在内存中的,此时如果系统宕机,内存中的数据没有来得及Dump到磁盘,从而会丢失数据,为了避免这种情况,我们采取先写日志,再写内存的策略,这样即使系统宕机,也可以从log文件中恢复,不会造成数据的丢失。

3)SSTable是由内存中的数据不断导入并进行merge后形成的,所有SSTable文件构成一种层级结构,对于磁盘中的数据来说,第0层是最新的,即最低层,数据由底层想高层归并,在归并过程中,去掉重复的数据并删除已经被打上删除标记的数据。为了提高查询效率,用多个文件分开存储数据,每个文件存储一个范围内的key数据,这样只需要存储每个文件的最小key和最大key就可以快速定位到要查找的记录在哪个SSTable文件中。manifest文件就是用来管理这些key信息,同时Manifest还会存储每个SSTable文件所属的层次。
注:log中的Key是无序的,而SSTable中的key是有序的

SSDB的缓存机制
merge过程使得系统从磁盘中读取数据的性能有了一定的提升,但直接从磁盘中读取数据依然效率低,如果数据在SSTable文件中,则需要进行多次磁盘访问操作,在最优的情况下,即要找的数据在第0层,那么也需要2次磁盘读操作(一次是将SSTable文件中的索引部分读入内存,然后根据索引就可以确定key是在哪个物理块中存储;第二次是读入这个物理块的内容,然后在内存中查找)。
相比于内存,磁盘的IO性能上相差一个数量级,为此需要尽量的减少磁盘IO的次数。在SSDB中引入了两种缓存:索引缓存和数据库缓存。索引缓存负责缓存SSTable文件的索引信息,数据块缓存负责缓存物理块的内容。从磁盘查找数据时,首先判断key在哪个SSTable文件中,然后检查该SSTable文件是否在索引缓存中,若不在,则读取该SSTable文件并将其缓存,若在则通过索引定位到物理块,并检查该物理块是否在数据块缓存中,若不在则将该物理块读入内存并缓存,若在则直接在内存中查找。
SSDB就是通过这两个缓存机制来加快读取速度的。如果读取的数据局部性比较好,则性能会明显的提高。由于cache容量有限,而新访问的数据一般都会被cache, 因此容量满后采用LRU算法和FiveMinuteRule原则进行替换。(FiveMinuteRule原则即如果数据被访问的频率在5分钟以内,则就应该尽量将该数据写入到内存中,5分钟这个时间是可配置的)

SSDB的读写流程:
SSDB的put, delete操作都是些操作,以追加新记录的方式进行。写操作包括两个过程:首先在日志文件的尾部以顺序写的方式追加Key/Value记录,然后向日志文件中写成功后再将KV记录插入到内存中的Memtable中。顺序写符合磁盘的顺序访问特性,因而写日志并不会使写操作的性能下降太多。同事Memtable采用skiplist作为核心数据结构,将KV记录有序存储,插入一条新记录的过程就是查找合适位置,然后修改响应的指针即可完成插入操作。由于写操作仅涉及这两个过程,因而有非常高的写入效率。
写操作将KV记录写入到内存中的Memtable中就算完成,写操作非常简单,但读操作复杂。因为内存容量有限,当Memtable占用的内存容量达到一个门限值时就需要将它转换成Immutable Memtable,然后再由后台到处到磁盘形成SSTable文件,所有的新插入的数据记录都将会走这个流程,并且所有的update操作都是以插入记录的方式进行,所以内存中的Memtable中的数据记录永远都是最新的(如果Memtable中的某条记录的key在其他的存储介质中也存在,则表示Memtable中的这条key记录是一个新的修改操作,因而查询时需要查这个最新的值即可),因而整个的读操作过程为先读内存中的Memtable,再读内存中的Immutable Memtable,然后再一次由底层到高层读磁盘中各层的SSTable文件,在这个过程中只要独到需要的KV数据记录就返回(若找到数据而不返回,则在后续的查找中有可能查到脏数据),整个过程结束后若都没有查找到响应的数据记录,则表示要查找的key不存在。
相对于写曹邹,读操作要复杂很多,因而写性能要高出读性能很多,也就是说,基于LSM的持久化缓存模型,是牺牲随机读换取顺序写,适合写操作比较频繁的应用场景。

6. SSDB的分布式实现
分布式系统:多台机器通过互相协调共同执行某项任务,就可以将这套系统成为分布式系统。从这个意义上讲,Memcached集群,MySQL sharding集群,Google GFS&Bigtable, 以及国内众多专用的NoSQL系统都是分布式系统。分布式系统的难点主要在于可扩展性,随着机器数量的增多,集群能力是否能够接近线性扩展,因集群规模扩大而产生的容错需求、负载的动态迁移等问题,对系统的设计师一个巨大的挑战。

冗余备份:一致性哈希解决了缓存服务器因扩展或者宕机时需要修改规则引擎从而导致大部分缓存都失效的问题。在服务器宕机时,通过一致性哈希的映射会把映射到宕机服务器的数据映射到下一个相邻的服务器,从而避免影响整个系统,但是原来宕机服务器的数据却没办法继续访问。为了解决这个问题,最容易想到的办法是,为每台服务器配备一个备用服务器,当往主服务器中写数据时,也同时也往备用服务器中写数据,保证主备服务器的数据的一致性,这样当主服务器宕机后,备用服务器直接接管任务。为了保证备份服务器也宕机时出问题,通常备份保留2份,即所谓的3点备份策略:如hash环中有A, B, C, D, E...等节点,那么A, B范围内的key映射存储到B,同时备份到C, D.

7. SSDB和Redis对比:
SSDB的读和写的性能都会略低于redis, 但SSDB打破了redis内存容量的限制,当内容容量不足时,数据会被写入到磁盘,而不会像redis那样选择丢弃,也不会像memcached那样使用LRU算法丢弃旧数据。redis虽然也支持持久化,但依然后内存容量的限制,因为他们仅仅写日志以便系统宕机后恢复数据而已,不支持从日志中查找数据,因为日志中的数据是无序的,从一个无序的集合中查找一条指定的记录是非常低效的。

你可能感兴趣的:(NoSQL)