LSM树(Log-Structured Merge Tree)是一种存储结构,被广泛应用于许多NoSQL数据库,如HBase、LevelDB和RocksDB。LSM树的核心思想是通过顺序写来提高写性能,同时通过分层的设计来稍微降低读性能,从而实现高性能的写入操作。
LSM树由以下三个重要组成部分构成:
MemTable(内存表):MemTable是存储在内存中的数据结构,用于保存最近更新的数据。它按照键(Key)有序地组织数据。不同的LSM树实现可能使用不同的数据结构来保证内存中键的有序性,例如HBase使用跳跃表(Skip List)。由于数据存储在内存中,因此需要通过预写式日志(Write-ahead Logging,WAL)来保证数据的可靠性[1]。
Immutable MemTable(不可变内存表):当MemTable达到一定大小后,会转化为Immutable MemTable。Immutable MemTable是将MemTable转换为SSTable(Sorted String Table)的一种中间状态。在转存过程中,不会阻塞数据更新操作[1]。
SSTable(有序键值对表):SSTable是存储在磁盘中的数据结构,包含有序的键值对。为了加快SSTable的读取速度,可以通过建立键的索引和使用布隆过滤器来加速键的查找。LSM树将所有的数据操作记录保存在内存中,当操作记录达到一定数量后,批量顺序写入磁盘。与B+树不同,LSM树的数据更新是以日志的形式进行的,每次数据更新都是追加一条更新记录。这样的设计可以实现顺序写入,只需将Immutable MemTable顺序写入持久化存储即可,而无需修改之前的SSTable中的键,从而保证了顺序写入的性能。因此,当MemTable达到一定大小并转存为SSTable后,不同的SSTable中可能存在相同键的记录,而最新的记录才是准确的[1]。
LSM树的设计虽然大大提高了写性能,但也带来了一些问题:
冗余存储:对于同一个键,除了最新的记录外,其他记录都是冗余的且无用的,但仍然占用存储空间。因此,需要进行Compact操作来清除冗余记录[1]。
读放大:在LSM树中进行数据查找时,需要从最新的SSTable开始逆向查询,直到找到目标键的记录。在最坏情况下,需要查询所有的SSTable。为了优化查找速度,可以使用索引和布隆过滤器[1]。
下面介绍两种常见的LSM树的Compact策略:
Redis的SDS(Simple Dynamic String)是Redis中使用最多的基础数据结构之一。它具有以下特色:
动态扩展内存:SDS可以动态地扩展内存,允许字符串的内容进行修改和追加。这与其他语言中的字符串类型不同,它们通常分为可变和不可变两种类型。
二进制安全:SDS可以存储任意二进制数据,而不仅仅是可打印字符。它是二进制安全的,意味着它可以存储任意字节的数据,而不会因为特殊字符而导致数据损坏。
与传统C语言字符串兼容:SDS与传统的C语言字符串类型兼容,可以在需要传入C语言字符串的地方使用SDS。它的类型定义与C语言字符串相同,都是char*类型。
Go语言的init函数是一个特殊的函数
Tomcat中的Listener机制允许我们在Servlet Context生命周期中的特定时间点执行自定义代码。
Tomcat支持以下几种类型的Listener:
装配在ServletContext级别。在web应用启动和关闭时触发。