leveldb官方手册摘录

2023-10-30

本文内容摘自leveldb官方手册,版权归其所有

CHAPTER 1 基本概念

leveldb是一个写性能十分优秀的存储引擎,是典型的LSM树(Log Structured-Merge Tree)实现。

LSM树的核心思想就是放弃部分读的性能,换取最大的写入能力

LSM树写性能极高的原理,简单地来说就是尽量减少随机写的次数

对于每次写入操作,并不是直接将最新的数据驻留在磁盘中,而是将其拆分成
(1)一次日志文件的顺序写
(2)一次内存中的数据插入

leveldb正是实践了这种思想,将数据首先更新在内存中,当内存中的数据达到一定的阈值,将这部分数据真正刷新到磁盘文件中,因而获得了极高的写性能(顺序写60MB/s, 随机写45MB/s)。

1.1 整体架构

leveldb中主要由以下几个重要的部件构成:

  • memtable
  • immutable memtable
  • log(journal)
  • sstable
  • manifest
  • current

1.1.1 memtable

memtable就是一个在内存中进行数据组织与维护的结构。

memtable中,所有的数据按用户定义的排序方法排序之后按序存储,等到其存储内容的容量达到阈值时(默认为4MB),
便将其转换成一个不可修改的memtable,与此同时创建一个新的memtable,供用户继续进行读写操作。

memtable底层使用了一种跳表数据结构,这种数据结构效率可以比拟二叉查找树,绝大多数操作的时间复杂度为O(log n)

在这里插入图片描述

1.1.2 immutable memtable

memtable的容量到达阈值时,便会转换成一个不可修改的memtable,也称为immutable memtable

这两者的结构定义完全一样,区别只是immutable memtable是只读的。

当一个immutable memtable被创建时,leveldb的后台压缩进程便会将利用其中的内容,创建一个sstable持久化到磁盘文件中。

1.1.3 log

leveldb在写内存之前会首先将所有的写操作写到日志文件中,也就是log文件。

当以下异常情况发生时,均可以通过日志文件进行恢复

  1. 写log期间进程异常;
  2. 写log完成,写内存未完成;
  3. write动作完成(即log、内存写入都完成)后,进程异常
  4. Immutable memtable持久化过程中进程异常;
  5. 其他压缩异常(较为复杂,首先不在这里介绍);

第一类情况发生时,数据库重启读取log时,发现异常日志数据,抛弃该条日志数据,即视作这次用户写入失败,保障数据库的一致性
第二类,第三类,第四类情况发生了,均可以通过redo日志文件中记录的写入操作完成数据库的恢复。

每次日志的写操作都是一次顺序写,因此写效率高,整体写入性能较好。
此外,leveldb的用户写操作的原子性同样通过日志来实现。

1.1.4 sstable

除了某些元数据文件,leveldb的数据主要都是通过sstable来进行存储

虽然在内存中,所有的数据都是按序排列的,但是当多个memetable数据持久化到磁盘后,对应的不同的sstable之间是存在交集的,在读操作时,需要对所有的sstable文件进行遍历,严重影响了读取效率。

因此leveldb后台会 定期整合 这些sstable文件,该过程也称为compaction

随着compaction的进行,sstable文件在逻辑上被分成若干层,由内存数据直接dump出来的文件称为level 0层文件,后期整合而成的文件为level i层文件,这也是leveldb这个名字的由来

1.1.5 manifest

leveldb中有个版本的概念,一个版本中主要记录了每一层中所有文件的元数据

元数据包括
(1)文件大小
(2)最大key值
(3)最小key值

该版本信息十分关键,除了在查找数据时,利用维护的每个文件的最大/小key值来加快查找,还在其中维护了一些进行compaction统计值,来控制compaction的进行

goleveldb为例,一个文件的元数据主要包括了最大最小key,文件大小等信息;

// tFile holds basic information about a table.
type tFile struct 
{
	fd storage.FileDesc
	seekLeft int32
	size int64
	imin, imax internalKey
}

一个版本信息主要维护了每一层所有文件的元数据

type version struct 
{
	s *session 			// session - version
	
	levels []tFiles 	// file meta
	
	// Level that should be compacted next and its compaction score.
	// Score < 1 means compaction is not strictly needed. These fields
	// are initialized by computeCompaction()
	cLevel int 		// next level
	cScore float64 	// current score
	
	cSeek unsafe.Pointer
	
	closing bool
	ref int
	released bool
}

当每次compaction完成(或者换一种更容易理解的说法,当每次sstable文件有新增或者减少),leveldb都会创建一个新的version

创建的规则是:

versionNew = versionOld + versionEdit

versionEdit指代的是基于旧版本的基础上,变化的内容(例如新增或删除了某些sstable文件)

manifest文件就是用来记录这些versionEdit信息的。

一个versionEdit数据,会被编码成一条记录,写入manifest文件中。

例如下图便是一个manifest文件的示意图,其中包含了3条versionEdit记录,
每条记录包括
(1)新增哪些sst文件
(2)删除哪些sst文件
(3)当前compaction下标
(4)日志文件编号
(5)操作seqNumber等信息

通过这些信息,leveldb便可以在启动时,基于一个空的version,不断apply这些记录,最终得到一个上次运行结束时的版本信息
在这里插入图片描述

1.1.6 current

这个文件的内容只有一个信息,就是记载当前的manifest文件名。

因为每次leveldb启动时,都会创建一个新的Manifest文件。因此数据目录可能会存在多个Manifest文件。

Current则用来指出哪个Manifest文件才是我们关心的那个Manifest文件

CHAPTER 2 读写操作

2.1 写操作

2.1.1 整体流程

leveldb的一次写入分为两部分:

  1. 将写操作写入日志;
  2. 将写操作应用到内存数据库中
注解
其实leveldb仍然存在写入丢失的隐患。在写完日志文件以后,操作系统并不是直接将这些数据真正落到磁盘中,而是暂时留在操作系统缓存中,
因此 当用户写入操作完成,操作系统还未来得及落盘的情况下,发生系统宕机,就会造成写丢失
但是若只是进程异常退出,则不存在该问题

2.1.2 写类型

leveldb对外提供的写入接口有:
(1)Put
(2)Delete两种

这两种本质对应同一种操作,Delete操作同样会被转换成一个value为空的Put操作

除此以外,leveldb还提供了一个批量处理的工具Batch,用户可以依据Batch来完成批量的数据库更新操作,且这些操作是原子性

在这里插入图片描述

2.1.3 batch结构

batch的组织结构
在这里插入图片描述

batch中,每一条数据项都按照上图格式进行编码。每条数据项编码后的第一位是这条数据项的类型(更新还是删除),之后是数据项key的长度,数据项key的内容;
若该数据项不是删除操作,则再加上value的
长度,value的内容

batch中会维护一个size值,用于表示其中包含的数据量的大小。
该size值为所有数据项key与value长度的累加,以及每条数据项额外的8个字节。
这8个字节用于存储一条数据项额外的一些信息

2.1.4 key值编码

当数据项从batch中写入到内存数据库中是,需要将一个key值的转换,
即在leveldb内部,所有数据项的key是经过特殊编码的,这种格式称为internalKey

internalkey在用户key的基础上,尾部追加了8个字节,用于存储
(1)该操作对应的 sequence number
(2)该操作的类型
在这里插入图片描述

其中,每一个操作都会被赋予一个sequence number。该计时器是在leveldb内部维护,每进行一次操作就做一个累加。由于在leveldb中,一次更新或者一次删除,采用的是append的方式,并非直接更新原数据。因此对应同样一个key,会有多个版本的数据记录,而最大的*sequence number**对应的数据记录就是最新的

此外,leveldb的快照(snapshot)也是基于这个sequence number实现的,即每一个sequence number代表着数据库的一个版本

2.1.5 合并写

在面对并发写入时,在同一个时刻,只允许一个写入操作将内容写入到日志文件以及内存数据库中。

为了在写入进程较多的情况下,减少日志文件的小写入,增加整体的写入性能,leveldb 将一些“小写入”合并成一个“大写入”

流程如下:

  • 第一个获取到写锁的写操作
    • 第一个写入操作获取到写入锁
    • 在当前写操作的数据量未超过合并上限,且有其他写操作pending的情况下,将其他写操作的内容合并到自身
    • 若本次写操作的数据量超过上限,或者无其他pending的写操作了,将所有内容统一写入日志文件,并写入到内存数据库中
    • 通知每一个被合并的写操作最终的写入结果,释放或移交写锁
  • 其他写操作:
    • 等待获取写锁或者被合并;
    • 若被合并,判断是否合并成功,若成功,则等待最终写入结果;反之,则表明获取锁的写操作已经oversize了,此时,该操作直接从上个占有锁的写操作中接过写锁进行写入;
    • 若未被合并,则继续等待写锁或者等待被合并;
      在这里插入图片描述

2.1.6 原子性

leveldb的任意一个写操作(无论包含了多少次写),其原子性都是由日志文件实现的。一个写操作中所有的内容会以一个日志中的一条记录,作为最小单位写入

考虑以下两种异常情况

  1. 写日志未开始,或写日志完成一半,进程异常退出;
  2. 写日志完成,进程异常退出;

前者中可能存储一个写操作的部分写已经被记载到日志文件中,仍然有部分写未被记录,这种情况下,当数据库重新启动恢复时,读到这条日志记录时,发现数据异常,直接丢弃或退出,实现了写入的原子性保障

后者,写日志已经完成,已经数据未真正持久化,数据库启动恢复时通过redo日志实现数据写入,仍然保障原子性

2.1.7 日志、内存数据库

下章节介绍

2.2 读操作

leveldb提供给用户两种进行读取数据的接口:

  1. 直接通过Get接口读取数据;
  2. 首先创建一个snapshot,基于该snapshot调用Get接口读取数据;

两者的本质是一样的,只不过第一种调用方式默认地以当前数据库的状态创建了一个snapshot,并基于此snapshot进行读取

snapshot(快照)就是数据库在某一个时刻的状态
基于一个快照进行数据的读取,读到的内容不会因为后续数据的更改而改变。

由于两种方式本质都是基于快照进行读取的

2.2.1 快照

快照代表着数据库某一个时刻的状态,在leveldb中,作者巧妙地用一个整型数来代表一个数据库状态。

在leveldb中,用户对同一个key的若干次修改(包括删除)是以维护多条数据项的方式进行存储的(直至进行compaction时才会合并成同一条记录),每条数据项都会被赋予一个序列号,代表这条数据项的新旧状态。一条数据项的序列号越大,表示其中代表的内容越新

因此,每一个序列号,其实就代表着leveldb的一个状态。换句话说,每一个序列号都可以作为一个状态快照
在这里插入图片描述

利用快照能够保证数据库进行并发的读写操作

在 获 取 到 一 个 快 照 之 后 ,leveldb会 为 本 次 查 询 的key构 建 一 个internalKey( 格 式 如 上 文 所 述 ) , 其中internalKeyseq字段使用的便是快照对应的seq
通过这种方式可以过滤掉所有seq大于快照号的数据项

2.3 读取

在这里插入图片描述

leveldb 读取分为三步:

  1. 在memory db中查找指定的key,若搜索到符合条件的数据项,结束查找;
  2. 在冻结的memory db中查找指定的key,若搜索到符合条件的数据项,结束查找;
  3. 按低层至高层的顺序在level i层的sstable文件中查找指定的key,若搜索到符合条件的数据项,结束查找,否则返回Not Found错误,表示数据库中不存在指定的数据;
注解
注意leveldb在每一层sstable中查找数据时,都是按序依次查找sstable的。
0层的文件比较特殊。由于 0层的文件中可能存在key重合的情况,因此在0层中, 文件编号大的sstable优先查找。理由是文件编号较大的sstable中存储的总是最新的数据。
非0层文件,一层中所有文件之间的key不重合,因此leveldb可以借助sstable的元数据(一个文件中最
小与最大的key值)进行快速定位,每一层只需要查找一个sstable文件的内容
memory db或者 sstable的查找过程中,需要根据指定的序列号拼接一个 internalKey,查找用户 key一致,且 seq号不大于指定 seq的数据

CHAPTER 3 日志

为了防止写入内存的数据库因为进程异常、系统掉电等情况发生丢失,leveldb在写内存之前会将本次写操作的内容写入日志文件中

在这里插入图片描述

leveldb中,有两个memory db,以及对应的两份日志文件。其中一个memory db可读写的,当这个db的数据量超过预定的上限时,便会转换成一个不可读的memory db,与此同时,与之对应的日志文件也变成一份frozen log

而新生成的immutable memory db则会由后台的minor compaction进程将其转换成一个sstable文件进行持久化,持久化完成,与之对应的frozen log被删除

3.1 日志结构

在这里插入图片描述

为了增加读取效率,日志文件中按照block进行划分,每个block的大小为32KiB

每个block中包含了若干个完整的chunk

一条日志记录包含一个或多个chunk
每个chunk包含了一个7字节大小的header前4字节是该chunk的校验码,紧接的2字节是该chunk数据的长度,以及最后一个字节是该chunk的类型。其中checksum校验的范围包括chunk的类型以及随后的data数据

chunk共有四种类型
fullfirstmiddlelast

一条日志记录若只包含一个chunk,则该chunk的类型为full

若一条日志记录包含多个chunk,则这些chunk的第一个类型为first, 最后一个类型为last,中间包含大于等于0个middle类型的chunk

由于一个block的大小为32KiB,因此当一条日志文件过大时,会将第一部分数据写在第一个block中,且类型为first,若剩余的数据仍然超过一个block的大小,则第二部分数据写在第二个block中,类型为middle,最后剩余的数据写在最后一个block中,类型为last

3.2 日志内容

日志的内容为写入的batch编码后的信息
在这里插入图片描述

一条日志记录的内容包含:

  • Header
  • Data

其中header中有
(1)当前dbsequence number
(2)本次日志记录中所包含的put/del操作的个数。紧接着写入所有batch编码后的内容

3.3 日志写

在这里插入图片描述

leveldb内 部 , 实 现 了 一 个journalwriter

首 先 调 用Next函 数 获 取 一个singleWriter,这个singleWriter的作用就是写入一条journal记录

singleWriter开始写入时,标志着第一个chunk开始写入。在写入的过程中,不断判断writerbuffer的大小,若超过32KiB,将chunk开始到现在做为一个完整的chunk,为其计算header之后将整个block写入文件。与此同时reset buffer,开始新的chunk的写入

若一条journal记录较大,则可能会分成几个chunk存储在若干个block

3.4 日志读

为了避免频繁的IO读取,每次从文件中读取数据时,按block(32KiB)进行块读取

每次读取一条日志记录,reader调用Next函数返回一个singleReader

singleReader每次调用Read函数就返回一个chunk的数据。每次读取一个chunk,都会检查这批数据的校验码、数据类型、数据长度等信息是否正确,若不正确,且用户要求严格的正确性,则返回错误,否则丢弃整个chunk的数据

循环调用singleReaderread函数,直至读取到一个类型为Lastchunk,表示整条日志记录都读取完毕,返回
在这里插入图片描述

CHAPTER 4 内存数据库

4.1 跳表(Skip List)


本章节关于跳表内容源自 wangzheng0822/algo,版权归wangzheng0822所有。

【强烈推荐作者课程 数据结构与算法之美!】


支持快速地:

  • 插入
  • 删除
  • 查找

某些情况下,跳表甚至可以替代红黑树(Red-Black tree)。Redis 当中的有序集合(Sorted Set)是用跳表实现的。

跳表的结构

跳表是对链表的改进。对于单链表来说,即使内容是有序的,查找具体某个元素的时间复杂度也要达到 O ( n ) O(n) O(n)。对于二分查找来说,由于链表不支持随机访问,根据 firstlast 确定 cut 时,必须沿着链表依次迭代 std::distance(first, last) / 2 步;特别地,计算 std::(first, last) 本身,就必须沿着链表迭代才行。此时,二分查找的效率甚至退化到了 O ( n log ⁡ n ) O(n \log n) O(nlogn),甚至还不如顺序遍历。

单链表查找示例

跳表的核心思想是用空间换时间,构建足够多级数的索引,来缩短查找具体值的时间开销。

具有二级索引的跳表示例

例如对于一个具有 64 个有序元素的五级跳表,查找起来的过程大约如下图所示。

五级跳表示例

复杂度分析

对于一个每一级索引的跨度是下一级索引 k k k 倍的跳表,每一次 down 操作,相当于将搜索范围缩小到「剩余的可能性的 1 / k 1 / k 1/k」。因此,查找具体某个元素的时间复杂度大约需要 ⌊ log ⁡ k n ⌋ + 1 \lfloor \log_k n\rfloor + 1 logkn+1 次操作;也就是说时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)

跳表查询过程示例

前面说了,跳表是一种用空间换时间的数据结构。因此它的空间复杂度一定不小。我们考虑原链表有 n n n 个元素,那么第一级索引就有 n / k n / k n/k 个元素,剩余的索引依次有 n / k 2 n / k^2 n/k2, n / k 3 n / k^3 n/k3, …, 1 1 1 个元素。总共的元素个数是一个等比数列求和问题,它的值是 n − 1 k − 1 \frac{n - 1}{k - 1} k1n1。可见,不论 k k k 是多少,跳表的空间复杂度都是 O ( n ) O(n) O(n);但随着 k k k 的增加,实际需要的额外节点数会下降。

高效地插入和删除

对于链表来说,插入或删除一个给定结点的时间复杂度是 O ( 1 ) O(1) O(1)。因此,对于跳表来说,插入或删除某个结点,其时间复杂度完全依赖于查找这类结点的耗时。而我们知道,在跳表中查找某个元素的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)。因此,在跳表中插入或删除某个结点的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)

在跳表中插入一个元素

跳表索引的动态更新

为了维护跳表的结构,在不断插入数据的过程中,有必要动态维护跳表的索引结构。一般来说,可以采用随机层级法。具体来说是引入一个输出整数的随机函数。当随机函数输出 K K K,则更新从第 1 1 1 级至第 K K K 级的索引。为了保证索引结构和数据规模大小的匹配,一般采用二项分布的随机函数。

在跳表中插入一个元素并更新索引

4.2 内存数据库

4.2.1 键值编码

内存数据库中,key称为internalKey,其由三部分组成:

  • 用户定义的key:这个key值也就是原生的key值;

  • 序列号:leveldb中,每一次写操作都有一个sequence number,标志着写入操作的先后顺序。由于在leveldb中,可能会有多条相同key的数据项同时存储在数据库中,因此需要有一个序列号来标识这些数据项的新旧情况。序列号最大的数据项为最新值;

  • 类型:标志本条数据项的类型,为更新还是删除
    在这里插入图片描述

4.2.2 键值比较

比较规则:

  • 首先按照字典序比较用户定义的keyukey),若用户定义key值大,整个internalKey就大;
  • 若用户定义的key相同,则序列号大的internalKey值就小;

4.2.3 数据组织

goleveldb为示例,内存数据库的定义如下

    type DB struct
     {
    	cmp comparer.BasicComparer
    	rnd *rand.Rand
    	mu sync.RWMutex
    	kvData []byte
    	// Node data:
    	// [0] : KV offset
    	// [1] : Key length
    	// [2] : Value length
    	// [3] : Height
    	// [3..height] : Next nodes
    	nodeData []int
    	prevNode [tMaxHeight]int
    	maxHeight int
    	n int
    	kvSize int
    }

其中kvData用来存储每一条数据项的key-value数据,nodeData用来存储每个跳表节点的链接信息。

nodeData中,每个跳表节点占用一段连续的存储空间,每一个字节分别用来存储特定的跳表节点信息。

• 第一个字节用来存储本节点key-value数据在kvData中对应的偏移量;
• 第二个字节用来存储本节点key值长度;
• 第三个字节用来存储本节点value值长度;
• 第四个字节用来存储本节点的层高;
• 第五个字节开始,用来存储每一层对应的下一个节点的索引值;

4.2.4 基本操作

PutGetDeleteIterator等操作均依赖于底层的跳表的基本操作实现

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

leveldb官方手册摘录 的相关文章

  • leveldb之log文件

    leveldb之log文件 1 log文件在LevelDb中的主要作用是系统故障恢复时 xff0c 能够保证不会丢失数据 因为在将记录写入内存的Memtable之前 xff0c 会先写入Log文件 xff0c 这样即使系统发生故障 xff0
  • DispatcherServlet 源码阅读(1)

    有时间还是应该多看看源码 DispatcherServlet 是一个实实在在的 Servlet xff0c 所以 Spring MVC 引入后不会改变 Servlet 容器的行为 xff0c 仍然是解析 web xml 部署文件 xff0c
  • TeaPearce/Conditional_Diffusion_MNIST 源码阅读

    文章目录 tqdm超参数预运算nn Module register buffer绘制动画ddpmforward U net噪声预测模型信息向量掩码向量conext mask上采样层的信息融合恢复阶段 总结后记 tqdm dataset sp
  • kube-proxy源码阅读(iptables实现)

    Reference 文章目录 1 入口2 ProxyServer创建及调用3 ProxyServer 核心调用流程3 1 func o Options Run err3 2 func o Options runLoop error3 3 f
  • 【Python源码阅读】PYC 文件剖析

    pyc 文件相信大家见怪不怪 xff0c 大家经常在 pycache 里面见到这些文件 这些文件存储了 python 编译出来的字节码文件 xff0c 还有一些元信息 xff08 例如版本号 xff0c 对应文件的修改时间 xff09 接下
  • LevelDB源码分析之从Put说起

    之前分享的文章leveldb实现原理分析详细描述了LevelDB的实现原理 xff0c 本文从Put接口来看下leveldb数据写流程的源码实现 LevelDB的读写接口在类DB中定义 xff08 leveldb db h xff09 xf
  • 大数据时代的Tcaplus游戏存储

    大数据时代的Tcaplus游戏存储 shiweizhang 2015 10 27 1 7k浏览 游戏开发数据分析场景 想免费获取内部独家PPT资料库 观看行业大牛直播 点击加入腾讯游戏学院游戏开发行业精英群711501594 摘要 大数据具
  • LevelDb

    LevelDb 之一 初识LevelDb 说起LevelDb也许您不清楚 但是如果作为IT工程师 不知道下面两位大神级别的工程师 那您的领导估计会Hold不住了 Jeff Dean和Sanjay Ghemawat 这两位是Google公司重
  • Deformable Detr代码阅读

    前言 本文主要是自己在阅读mmdet中Deformable Detr的源码时的一个记录 如有错误或者问题 欢迎指正 deformable attention的流程 首先zq即为object query 通过一个线性层 先预测出offset
  • leveldb官方手册摘录

    本文内容摘自leveldb官方手册 版权归其所有 CHAPTER 1 基本概念 leveldb是一个写性能十分优秀的存储引擎 是典型的LSM树 Log Structured Merge Tree 实现 LSM树的核心思想就是放弃部分读的性能
  • Leveldb源码分析--13

    8 FilterPolicy Bloom之2 8 5 构建FilterBlock 8 5 1 FilterBlockBuilder 了解了filter机制 现在来看看filter block的构建 这就是类FilterBlockBuilde
  • LevelDB.NET 使用

    LevelDB是google实现的非常高效的kv数据库 多数是和redis比较 这里记录下如何使用 新建项目 Nuget添加类库 通过反编译发现运行时是 NET 4 0 这里我用4 5测试需要选择64位平台 代码 写数据 db Put Wr
  • LevelDB使用指南

    这篇文章是levelDB官方文档的译文 原文地址 LevelDB library documentation 这篇文章主要讲leveldb接口使用和注意事项 leveldb是一个持久型的key value数据库 key value可以是任意
  • Windows下 VS2015编译RocksDB

    Windows下 VS2015编译RocksDB VS2015编译RocksDB RocksDB 是一个来自 facebook 的可嵌入式的支持持久化的 key value 存储系统 也可作为 C S 模式下的存储数据库 但主要目的还是嵌入
  • LSM树存储模型

    大规模分布式存储系统 原理解析与架构实战 读书笔记 之前研究了Bitcask存储模型 今天来看看LSM存储模型 两者虽然同属于基于键值的日志型存储模型 但是Bitcask使用哈希表建立索引 而LSM使用跳跃表建立索引 这一差别导致了两个存储
  • leveldb注释7–key与value

    作为一个kv的系统 key的存储至关重要 在leveldb中 主要涉及到如下几个key user key InternalKey与LookupKey memtable key 其关系构成如下图 user key就是用户输入的key 而Int
  • LevelDB源码分析之内存管理类arena

    LevelDB源码分析之内存管理类arena Leveldb的大部分内存管理依赖于C 语言的默认实现 也就是不对内存进行管理 只是在memtable的实现中用到了一个简单的内存管理器 arena 因为memtable的内部实现skip li
  • JDK源码阅读之AbstractStringBuilder类

    AbstractStringBuilder类源码阅读 AbstractStringBuilder类的作用 AbstractStringBuilder类的类图 AbstractStringBuilder类的重点方法 属性变量 构造方法 精华方
  • Quartz框架多个trigger任务执行出现漏执行的问题分析

    一 问题描述 使用Quartz配置定时任务 配置了超过10个定时任务 这些定时任务配置的触发时间都是5分钟执行一次 实际运行时 发现总有几个定时任务不能执行到 二 示例程序 1 简单介绍 采用spring quartz整合方案实现定时任务
  • LevelDB 与 std::map

    在我们的应用程序中我们使用std map存储 键 值 数据并使用序列化将该数据存储在磁盘上 通过这种方法 我们发现磁盘 I O 是性能瓶颈 并且使用 key 查找值并不是很快 我遇到过 LevelDB 并考虑使用它 但我有一些问题 Leve

随机推荐

  • signed和unsigned区别

    signed和unsigned用于修饰整数类型 包括char 从ANSI C89标准开始支持 signed表示有符号 unsigned表示无符号 有符号数的最大取值要比无符号的小约一半 因为有符号数的最高一位被用来表示符号 默认的int s
  • 剑指offer 专项突破版 119、最长连续序列

    题目链接 思路 同样的可以转化为并查集来做 可以把相邻的数字放到一个子集中 每当搜索到一个数字时就判断和他相邻的数字是否在集合中 如果在就合并 为了方便记录每个集合的大小 可以用一个count集合记录每个子集的大小 在合并集合的时候也要更新
  • vue开发环境准备-  配置npm私服地址

    1 通过config命令 npm config set registry https ip repository npm group 2 命令行方式 npm registry https ip repository npm group in
  • 剑指offer—16.数值的整数次方——分析及代码(Java)

    剑指offer 16 数值的整数次方 分析及代码 Java 一 题目 二 分析及代码 1 二分求解 1 思路 2 代码 3 结果 三 其他 一 题目 给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent
  • 微信小程序监听返回后执行操作

    例如 点击主页面是index进入子页面list list页面onUnload进行监听 onUnload getCurrentPages 获取当前的页面栈 var pageList getCurrentPages var prevPage p
  • Spring整合Ehcache管理缓存

    前言 Ehcache 是一个成熟的缓存框架 你可以直接使用它来管理你的缓存 Spring 提供了对缓存功能的抽象 即允许绑定不同的缓存解决方案 如Ehcache 但本身不直接提供缓存功能的实现 它支持注解方式使用缓存 非常方便 本文先通过E
  • R 与甲骨文数据挖掘

    特点 使用 Oracle Data Miner 和 Oracle R Enterprise 自动化预测分析过程 深入了解企业中常用的各种统计模型 以及如何使用各种 SQL PLSQL ORE ODM 和本机 R 包将它们自动化以进行预测分析
  • MD5

    package com bochy md5 import java security MessageDigest import java security NoSuchAlgorithmException import com sun ma
  • Linux基础——sudo命令

    sudo 用户提升权限的命令 1 新建用户 并且设置密码 root sanchuang useradd panlinfeng root sanchuang echo 123456 passwd panlinfeng stdin 更改用户 p
  • 爱立信携手河北移动护航张家口"雪如意"国际赛事;亚马逊云科技宣布1000万美元教育和奖学金计划

    国内市场 爱立信携手河北移动护航 雪如意 国际赛事 其中 爱立信与河北移动共同探讨 并采用了 2 6G 4 9G载波聚合功能 该功能可对张家口赛区现有核心赛区热点区域的网络进行整合服用 将下行峰值速率提升至3Gbps以上 让网络吞吐率实现了
  • C语言自定义数据类型——联合体

    一 联合体 1 定义 联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员 特征是这些成员公用同一块空间 所以联合也叫共用体 2 类型声明 union Un int i char c 注 每个成员之间仍是用分号隔开 联合体结束
  • 23.合并K个有序链表

    题目描述 给你一个链表数组 每个链表都已经按升序排列 请你将所有链表合并到一个升序链表中 返回合并后的链表 解题思路 思路一 将所有链表的元素插入vector中 然后将vector排序 再重新形成新的链表 时间复杂度 O N Log N 空
  • java实现五子棋

    java在初期就可以写一些简单的电脑小程序 使用GUI编程可以考验我们的代码能力 之后我会在出一个可能几万字的GUI编程入门 现在我们先看看五子棋 照片我放在最后 代码之后又详细解释 package 数字图像化处理AWT import ja
  • ugui和ngui手写虚拟摇杆功能比较

    第一 ugui点击父图片就移动子图片位置的功能不如ngui好写 第二 ugui没有OnPress回调方法 得手写监听OnPress的事件 第三 ugui用OnDrag来控制子图片有局限性 当鼠标超出摇杆界限时OnDrag没法继续执行 第四
  • 使用docker搭建dvwa环境

    docker search dvwa 搜索官方仓库中关于DVWA的镜像列表 docker pull citizenstig dvwa 下载镜像到本地 docker image ls 列出本地镜像列表信息 docker run name dv
  • java中怎样测试webwervice_junit4单元测试--web项目中模拟登录会话,做全流程测试...

    junit4相对于junit3 基于注解的方式写单元测试用例 使用过程中方便很多 如下缩写均是代码片段 摘录其中关键部分 重要是理解其中知识点 一 编写测试用例基类 RunWith SpringJUnit4ClassRunner class
  • Qt的核心剖析:信息隐藏

    如果你阅读了 Qt 的源代码 你会看到一堆奇奇怪怪的宏 例如 Q D Q Q 我们的Qt源码之旅就从理解这些宏说起 下面先看一个C 的例子 class Person public Person Person string name void
  • UNIX环境高级编程(中文第三版)

    UNIX环境高级编程第三版 操作系统神级书 资料收集于网络 如有侵权 请联系我
  • xss靶场第四关过关秘籍

    首先寻找注入点 输入试一哈 结果发现我们输入的 lt gt 被过滤掉了 我们不使用 lt gt l了接下来换一个方法 我们可以将我们的js代码编写到onclick属性中试一哈 弹窗就说明过关了 看一哈在还有没有别的方法 测试后发现构造事件
  • leveldb官方手册摘录

    本文内容摘自leveldb官方手册 版权归其所有 CHAPTER 1 基本概念 leveldb是一个写性能十分优秀的存储引擎 是典型的LSM树 Log Structured Merge Tree 实现 LSM树的核心思想就是放弃部分读的性能