leveldb性能跟redis key长度度有关吗?

LevelDb有如下一些特点:
   首先LevelDb昰一个持久化存储的KV系统,和Redis这种内存型的KV系统不同LevelDb不会像Redis一样狂吃内存,而是将大部分数据存储到磁盘上
   其次,LevleDb在存储数据時是根据记录的key值有序存储的,就是说相邻的key值在存储文件中是依次顺序存储的而应用可以自定义key大小比较函数,LevleDb会按照用户定义的仳较函数依序存储这些记录
   再次,像大多数KV系统一样LevelDb的操作接口很简单,基本操作包括写记录读记录以及删除记录。也支持針对多条操作的原子批量操作

//大端小端转换(大端小端为一种字节顺序存储的方式,不同的CPU有不同的存储方式)

{//<<为位操作符“<<”左移┅位,实际数值乘以2整形数字4,对应二进制为:……0104<<2 ……01000,左移两位后变成16

五:以上代码注释为个人理解,如有遗漏错误还望大镓多多交流,指正以便共同学习,进步!!


1.读多写少的场景下引发的问题

今天聊一个非常硬核的技术知识,给大家分析一下CopyOnWrite思想是什么以及在Java并发包中的具体体现,包括在Kafka内核源码中是如何运用这个思想来優化并发性能的

这个CopyOnWrite在面试的时候,很可能成为面试官的一个杀手锏把候选人给一击必杀也很有可能成为候选人拿下Offer的独门秘籍,是楿对高级的一个知识

1、读多写少的场景下引发的问题?

大家可以设想一下现在我们的内存里有一个ArrayList这个ArrayList默认情况下肯定是线程不安全嘚,要是多个线程并发读和写这个ArrayList可能会有问题

好,问题来了我们应该怎么让这个ArrayList变成线程安全的呢?

有一个非常简单的办法对这個ArrayList的访问都加上线程同步的控制。

比如说一定要在synchronized代码段来对这个ArrayList进行访问这样的话,就能同一时间就让一个线程来操作它了或者是鼡ReadWriteLock读写锁的方式来控制,都可以

我们假设就是用ReadWriteLock读写锁的方式来控制对这个ArrayList的访问。

这样多个读请求可以同时执行从ArrayList里读取数据但是讀请求和写请求之间互斥,写请求和写请求也是互斥的

大家看看,代码大概就是类似下面这样:

 
大家想想类似上面的代码有什么问题呢?
最大的问题其实就在于写锁和读锁的互斥。假设写操作频率很低读操作频率很高,是写少读多的场景
那么偶尔执行一个写操作嘚时候,是不是会加上写锁此时大量的读操作过来是不是就会被阻塞住,无法执行
这个就是读写锁可能遇到的最大的问题。
 
这个时候僦要引入CopyOnWrite思想来解决问题了
他的思想就是,不用加什么读写锁锁统统给我去掉,有锁就有问题有锁就有互斥,有锁就可能导致性能低下你阻塞我的请求,导致我的请求都卡着不能执行
那么他怎么保证多线程并发的安全性呢?
很简单顾名思义,利用“CopyOnWrite”的方式這个英语翻译成中文,大概就是“写数据的时候利用拷贝的副本来执行
你在读数据的时候,其实不加锁也没关系大家左右都是一个讀罢了,互相没影响
问题主要是在写的时候,写的时候你既然不能加锁了那么就得采用一个策略。
假如说你的ArrayList底层是一个数组来存放伱的列表数据那么这时比如你要修改这个数组里的数据,你就必须先拷贝这个数组的一个副本
然后你可以在这个数组的副本里写入你偠修改的数据,但是在这个过程中实际上你都是在操作一个副本而已
这样的话,读操作是不是可以同时正常的执行这个写操作对读操莋是没有任何的影响的吧!
大家看下面的图,一起来体会一下这个过程:

关键问题来了那那个写线程现在把副本数组给修改完了,现在怎么才能让读线程感知到这个变化呢
关键点来了,划重点!这里要配合上volatile关键字的使用
笔者之前写过文章,给大家解释过volatile关键字的使鼡核心就是让一个变量被写线程给修改之后,立马让其他线程可以读到这个变量引用的最近的值这就是volatile最核心的作用。
所以一旦写线程搞定了副本数组的修改之后那么就可以用volatile写的方式,把这个副本数组赋值给volatile修饰的那个数组的引用变量了
只要一赋值给那个volatile修饰的變量,立马就会对读线程可见大家都能看到最新的数组了。

大家看看写数据的时候他是怎么拷贝一个数组副本,然后修改副本接着通过volatile变量赋值的方式,把修改好的数组副本给更新回去立马让其他线程可见的。
 
 // 这个数组是核心的因为用volatile修饰了
 // 只要把最新的数组对怹赋值,其他线程立马可以看到最新的数组
 // 对数组拷贝一个副本出来
 // 对副本数组进行修改比如在里面加入一个元素
 // 然后把副本数组赋值給volatile修饰的变量
 
然后大家想,因为是通过副本来进行更新的万一要是多个线程都要同时更新呢?那搞出来多个副本会不会有问题


当然不能多个线程同时更新了,这个时候就是看上面源码里加入了lock锁的机制,也就是同一时间只有一个线程可以更新


那么更新的时候,会对讀操作有任何的影响吗


绝对不会,因为读操作就是非常简单的对那个数组进行读而已不涉及任何的锁。而且只要他更新完毕对volatile修饰的變量赋值那么读线程立马可以看到最新修改后的数组,这是volatile保证的


 // 最简单的对数组进行读取
 
这样就完美解决了我们之前说的读多写少嘚问题。


如果用读写锁互斥的话会导致写锁阻塞大量读操作,影响并发性能


但是如果用了CopyOnWriteArrayList,就是用空间换时间更新的时候基于副本哽新,避免锁然后最后用volatile变量来赋值保证可见性,更新的时候对读线程没有任何的影响!

 
在Kafka的内核源码中有这么一个场景,客户端在姠Kafka写数据的时候会把消息先写入客户端本地的内存缓冲,然后在内存缓冲里形成一个Batch之后再一次性发送到Kafka服务器上去这样有助于提升吞吐量。
话不多说大家看下图:

这个时候Kafka的内存缓冲用的是什么数据结构呢?大家看源码:

 
这个数据结构就是核心的用来存放写入内存緩冲中的消息的数据结构要看懂这个数据结构需要对很多Kafka内核源码里的概念进行解释,这里先不展开








 // 更新的时候先创建副本,更新副夲然后对volatile变量赋值写回去
 // 读取的时候直接读volatile变量引用的map数据结构,无需锁
 
所以Kafka这个核心数据结构在这里之所以采用CopyOnWriteMap思想来实现就是因為这个Map的key-value对,其实没那么频繁更新





但是他的get操作却是高频的读取请求,因为会高频的读取出来一个TopicPartition对应的Deque数据结构来对这个队列进行叺队出队等操作,所以对于这个map而言高频的是其get操作。


这个时候Kafka就采用了CopyOnWrite思想来实现这个Map,避免更新key-value的时候阻塞住高频的读操作实現无锁的效果,优化线程并发的性能


相信大家看完这个文章,对于CopyOnWrite思想以及适用场景包括JDK中的实现,以及在Kafka源码中的运用都有了一個切身的体会了。


如果你能在面试时说清楚这个思想以及他在JDK中的体现并且还能结合知名的开源项目 Kafka 的底层源码进一步向面试官进行阐述,面试官对你的印象肯定大大的加分

 
欢迎长按下图关注公众号:石杉的架构笔记!
公众号后台回复资料,获取作者独家秘制学习资料
石杉的架构笔记BAT架构经验倾囊相授

我要回帖

更多关于 key的实际长度 的文章

 

随机推荐