JUC并发容器——跳表

跳表 SkipList

  • 跳表是随机化的一个数据结构,以O(logn)的期望时间支持查找和插入。
  • 跳表是链表的优化,在有序链表的基础上,它把一维的线性链表做了一些提取,相当于新建了若干层索引,借索引减少比较次数。
  • JDK中没有SkipList的代码实现(和其他数据结构相比,其实并不复杂),但却有Doug Lea书写的ConcurrentSkipList

https://www.jianshu.com/p/d12389b80a19

在网上学习了一些材料。
https://www.baidu.com/link?url=bQLDt5r-VYz3-_s57DWkzcWtM8o0He-asgeHBPRc9mTKoPnPoUQ3eaPEwESkJZ_19HDfaXVUFIRTWggzzYDQpq&wd=&eqid=a940f0ba001a8a4c000000065daad90a
这一篇:https://www.zhihu.com/question/30527705

复制代码
AVL树:最早的平衡二叉树之一。应用相对其他数据结构比较少。windows对进程地址空间的管理用到了AVL树

红黑树:平衡二叉树,广泛用在C++的STL中。map和set都是用红黑树实现的。我们熟悉的STL的map容器底层是RBtree,当然指的不是unordered_map,后者是hash。

B/B+树用在磁盘文件组织 数据索引和数据库索引

Trie树 字典树,用在统计和排序大量字符串


AVL是一种高度平衡的二叉树,所以通常的结果是,维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,
更多的地方是用追求局部而不是非常严格整体平衡的红黑树。当然,如果场景中对插入删除不频繁,只是对查找特别有要求,AVL还是优于红黑的。

红黑树的应用就很多了,除了上面同学提到的STL,还有
epoll在内核中的实现,用红黑树管理事件块
nginx中,用红黑树管理timer等
Java的TreeMap实现
著名的linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块

B和B+主要用在文件系统以及数据库中做索引等,比如Mysql:B-Tree Index in MySql

trie 树的一个典型应用是前缀匹配,比如下面这个很常见的场景,在我们输入时,搜索引擎会给予提示
还有比如IP选路,也是前缀匹配,一定程度会用到trie


跳表:Redis中就使用跳表,而不是红黑树来存储管理其中的元素(应该说的是一级元素-直接的Key,里面的value应该是有不同的数据结构)。

首先,跳表是skiplist?不是ziplist。ziplist在redis中是一个非常省内存的链表(代价是性能略低),所以在hash元素的个数很少(比如只有几十个),
那么用这个结构来存储则可以在性能损失很小的情况下节约很多内存(redis是内存数据库啊,能省还是要省的)。好这个问题清楚了。

在server端,对并发和性能有要求的情况下,如何选择合适的数据结构(这里是跳跃表和红黑树)。
如果单纯比较性能,跳跃表和红黑树可以说相差不大,但是加上并发的环境就不一样了,
如果要更新数据,跳跃表需要更新的部分就比较少,锁的东西也就比较少,所以不同线程争锁的代价就相对少了,
而红黑树有个平衡的过程,牵涉到大量的节点,争锁的代价也就相对较高了。性能也就不如前者了。
在并发环境下skiplist有另外一个优势,红黑树在插入和删除的时候可能需要做一些rebalance的操作,这样的操作可能会涉及到整个树的其他部分,
而skiplist的操作显然更加局部性一些,锁需要盯住的节点更少,因此在这样的情况下性能好一些。
复制代码
另外Redis作者描述的使用跳表的原因:

复制代码
请看开发者说的,他为什么选用skiplist The Skip list

There are a few reasons:

  1. They are not very memory intensive. It’s up to you basically.
    Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive
    than btrees.
    注:跳表的一个缺点是耗内存(因为要重复分层存节点),但是作者也说了,可以调参数来降低内存消耗,和那些平衡树结构达到差不多。

  2. A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list.
    With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.
    注:redis经查有范围操作,这样利用跳表里面的双向链表,可以方便地操作。另外还有缓存区域化(cache locality)不会比平衡树差。

  3. They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch
    (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.
    注:实现简单。zrank操作能够到O(log(N)).

About the Append Only durability & speed, I don’t think it is a good idea to optimize Redis at cost of more code
and more complexity for a use case that IMHO should be rare for the Redis target (fsync() at every command).
Almost no one is using this feature even with ACID SQL databases, as the performance hint is big anyway.

About threads: our experience shows that Redis is mostly I/O bound. I’m using threads to serve things from Virtual Memory.
The long term solution to exploit all the cores, assuming your link is so fast that you can saturate a single core,
is running multiple instances of Redis (no locks, almost fully scalable linearly with number of cores),
and using the “Redis Cluster” solution that I plan to develop in the future.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值