为什么程序和数据库之间的“默认”选择会产生不同呢?

作者 | Evan Jones

译者 | 弯月,责编 | 屠敏

出品 | CSDN(ID:CSDNnews)

以下为译文:

我可以肯定地说,哈希表远比有序数据结构更常见,Go中的map、Python中的dict、Java中的HashMap等都是哈希表,而树结构只在保存有序数据结构时才使用。有一次,在谈到Google优化C++的哈希表时,有人指出在整个Google的服务器中,有1%的CPU和4%的内存都被哈希表使用了。然而,数据库默认总是会使用有序索引,通常是B树。为什么程序和数据库之间的“默认”选择会产生不同呢?说到底两者都是为了同一个目标:访问数据。一年前,我曾就这个问题在Twitter上发表了推文,并得到了许多有趣的答案。下面就来总结一下我得到的答案。

常见的答案是,当在内存中存储数据时,哈希表的效率很高,而B树的设计旨在以块的形式访问较慢的存储。然而,这不是决定性的属性。我们也有为访问磁盘而设计的哈希表,例如MySQL的哈希索引;也有许多在内存中使用的树,例如Java的TreeMap、C++的映射;甚至连B树都有内存中使用的版本。

我认为最重要的答案是,B树更适合于“一般用途”,它们能够以较低的总成本访问大规模的持久数据。换句话说,即使在访问大部分工作负载都是单个值的数据时,B树的速度较慢,但考虑到罕见的操作和多个索引的成本,B树的表现还是更为突出。在本文中,我将简要解释哈希表和B树之间的差异,然后讨论持久性数据与内存数据在需求上有何不同。最后,尽管我认为“内存应该使用有序的数据结构而数据库应该使用哈希表”这种默认的做法可能是正确的,但我仍然会提出一些自己的看法。

哈希表与树

首先,让我们回顾一下这些数据结构之间的根本区别。在访问单个值时,哈希表的访问时间是常数O(1),而树的访问时间是对数O(log n)。对于单值查找,这意味着无论数据存储在内存中还是磁盘中,哈希表的速度都比较快。但是,尽管树有额外开销,但树中的值都是有序的。因此,我们可以高效地访问范围值,这意味着它们可以有效地支持多种操作。例如,我们可以查找所有以某个前缀开头的值,或者“前k个”值,换作哈希表的话,则我们需要扫描整个表。关于数据库中B树的使用,我强烈建议你阅读《Modern B-Tree Techniques》(http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.219.7269),作者Goetz Graefe在书中奉上了一篇精妙易懂且全面的调查报告。

另一个区别是,哈希表仅提供平均的常数时间访问。有意或无意导致的哈希冲突可能会引发不良行为,但更大的问题出在重新散列上。有时,随着表的增大,本来需要O(1)的插入操作可能需要经过缓慢的O(n)扫描才能将数据插入更大的表。我们可以通过使用多级哈希表来减少这种影响,但仍然会导致某些不可预测性。相反,树最差也能保持O(log n)。

持久性数据与内存数据

通常,数据库都用于存储需要永久存在的持久性数据。而程序通常仅需要临时存储数据,只有在需要重新启动的时候才需要持久存储。这意味着最终数据库往往会存储大量的数据,无论是记录数量还是总字节数都非常大。由于以下三个原因,我认为数据库使用有序数据结构更好:

1.当n小时,数据结构不是特别重要

我觉得程序中的大多数哈希表都很小,只有数千个元素或更少。在这种规模下,O(1)、O(log n)和O(n)之间的差异无关紧要。因此,在程序中,对于常见的单值查找而言,哈希表的速度更快;而对于罕见的全表扫描而言,哈希表的速度则稍慢一些。但是,随着数据集的增大,需要O(n)才能找到相关值就会让人觉得过于缓慢。

具体来说,假设在某个应用程序中,99%的访问都仅限于单个记录,只有1%的访问涉及10个连续的记录,而且需要从某个值开始。这时,如果我们使用哈希表,则单个记录访问的开销为1,而访问某个范围的开销为n(扫描整个表),所以总开销为0.99×1 + 0.01×n。如果我们使用树,则单个记录访问的开销为log(n),而访问某个范围的开销为log(n)+9,所以总开销为0.99×log(n) + 0.01×(log(n) + 9)。如下图所示:

当然,这里的“开销”是我乱编的,但是我们可以从中看出趋势:如果n很小,那么哈希表是更好的选择;但是如果n较大,那么即便扫描整个表的操作非常罕见,也会给总开销带来很大影响。这就是我们常说的O(n)与O(log n)的比较,这意味着在处理大型数据集时,有序数据结构更“平衡”的性能拥有巨大优势。

2. 存储不是免费的

如果元素的数量很少,那么相对廉价的方式是存储多个数据副本,并建立不同的索引。程序和数据库都会使用该技巧。但是,添加一个索引就需要O(n)的存储空间和O(n)的构建时间。针对一个非常大的数据库,添加一个二级索引可能需要花费几个小时甚至几天。因此,数据规模越大,在不同种类的查询中重新利用索引的优势也会越大。这对于有序索引是一个优势,因为我们可以通过多种方式来使用有序属性。例如,一种常见的数据库技巧是建立包含多列的索引,例如(位置,商店名称)。然后我们可以使用这个索引访问特定的(位置,商店名称),而且还可以记录单个(位置),甚至是位置键的前缀。如果是哈希表,则每种查询都需要单独的索引。

3.持久数据结构更为复杂

将数据存储在磁盘上,就不会因为机器崩溃而导致数据损坏或丢失。你需要仔细按照顺序写入数据,在写入数据之前记录日志,而且可能还需要详细的并发控制。与内存的数据结构相比,这些操作都需要更多代码。因此,许多数据库仅支持单一类型的持久索引。如果你只选择一个索引,那么最好选择一个可以广泛用于各种负载的索引,即便它的效率稍低。

这种额外的复杂性意味着除了必要的数据结构访问外,你还有更多工作需要完成,这将对常量因素造成很大影响。例如,你需要通过网络发送请求、经过解析、从磁盘复制一些数据、经过解析,获取锁等。这些都会减少O(1)与O(log N)之间的差异。

总结和教训

我认为默认情况下,正确的选择是:在程序中使用哈希表,在数据库中使用有序数据结构。但是,可能在很多情况下,我们使用了错误的选择。我怀疑很多程序都未能有效利用有序数据结构,因为相比之下,顺序迭代的难度则小得多。例如,如果我有两个按时间排序的集合,而且我想按时间顺序输出合并后的集合,那么就可以同时迭代两个集合,并从适当的迭代中选择数据。但是,我也可以将所有事件放在一个列表中,对其进行排序,然后输出。我知道我会先想到哪个。这种复杂性的折衷让我不禁联想:有没有办法可以在使用集合时嵌入数据库查询优化……

原文:https://www.evanjones.ca/ordered-vs-unordered-indexes.html

本文为 CSDN 翻译,转载请注明来源出处。

【End】

热 文 推 荐 

☞5G 是否有过度承诺之嫌?

☞3 天开发物联网应用!腾讯云 IoT 超级小程序来了

☞30 年间,软件开发行业为何 Bug 纷飞?

☞2019年区块链安全事件总结,全球损失超60亿美元 | 盘点

☞互联网诞生记: 浪成于微澜之间

☞GitHub宝藏项目标星1.6w+,编程新手有福了

☞马云穿布鞋演讲,任正非打的出行,盘点科技大佬们令人发指的节俭生活

点击阅读原文,即刻参加!

你点的每个“在看”,我都认真当成了喜欢

为什么数据库使用有序索引,而程序员却在使用哈希表?相关推荐

  1. 十年比肩?看国产数据库如何突出重围! | 新程序员

    长久以来,如何"去IOE"不仅是数据库产业的热议话题,更是国产数据库厂商纷纷致力的方向.但事实上,想要通过核心技术弯道超车几乎是不可能的.但在国家整体规划逾趋清晰下,进一步完善人才 ...

  2. java 快速从树节点找到数据_14期每日分享Java程序员分享超全哈希相关的知识

    什么是每日分享? 饥人谷每天为大家带来一篇程序员分享!内容都来自于热爱编程.热爱生活的小伙伴们!分享的话题与编程.生活.兴趣.爱好.运动等相关! 想要每天都进步一点点的小伙伴们快点下关注吧! 今天的分 ...

  3. 找到符合条件的索引_程序员写了多年CRUD,总结出数据库索引这几点值得注意...

    索引,被称之为数据库的目录,可以让我们快速地找到对应的数据.但是,索引其实是一把双刃剑,如果使用不合理,不仅查询数据的速度不会变快,反而适得其反,让查询变慢. 工作原理 索引是对数据库表中一列或多列的 ...

  4. db2 删除索引_程序员必须了解的知识点——你搞懂mysql索引机制了吗?

    一.索引是什么 MySQL官方对索引的定义为:索引(Index)是帮助MySQL 高效 获取数据的数据结构,而MYSQL使用的数据结构是: B+树 在这里推荐大家看一本书, <深入理解计算机系统 ...

  5. 云数据库时代已来,程序员该如何出击?

    在数据库出现之前,尽管数据无处不在,但很少的人会去浏览记录系统的完整的数据.第一次信息爆炸时代后,对思考结构化信息,产生了长久的影响.数据库的实践,框架和使用在当时被开创出来.这成为组织管理数据的固有 ...

  6. 黑马程序员-IOS学习笔记 99乘法表 打印

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流!------- 以前面试也遇到过,其实代码很少~~~ void print99(){ for(int i=1;i& ...

  7. Java程序员从笨鸟到菜鸟之(九)——数据库有关知识补充(事务、视图、索引、存储过程)

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.NET/csh624366188 一:事务 首先看一下什么是事务: 通俗的理解,事务是一组原子操作单元,从数据库角度说,就是 ...

  8. 关于数据库,程序员应该了解的那些事

    数据库的选型 对于很多程序员来说,公司选择什么样的数据库,基本不需要你来决定.当你加入一个公司的时候,公司的大部分技术选型已经确认,特别是数据库选型,因为数据库一旦选择,后期迁移的代价还是很大的. 随 ...

  9. 高薪程序员面试题精讲系列101之如何对数据库进行优化(下)?

    一. 面试题及剖析 1. 今日面试题 如何对数据库进行优化? 说说你是怎么进行数据库优化的? 2. 题目剖析 壹哥在前2篇文章中给大家讲了数据库优化的上部分和中间部分的内容,今天这篇文章我会继续讲解数 ...

最新文章

  1. 想快速入门NLP?我们测评了15门最火课程,帮你找到最适合的
  2. 随机数---等概率,特殊概率
  3. 设置eclipse启动时使用的jdk
  4. 怎么多次调用c语言dll,c-如何从注入到同一程序的另一个.dll中调用...
  5. 维纳过程(Wiener Process)与高斯过程(Gaussian Process)
  6. 33-高级特性之generator(1)
  7. Linux下nand flash读写测试
  8. 作文 进入中职计算机班,我的中职生活作文(精选5篇)
  9. 后端开发常用工具指令
  10. Taro小程序跨端开发入门实战
  11. IE中点击链接下载EXCEL文件直接以乱码方式打开解决
  12. 【微信小程序】退款功能教程(含申请退款和退款回调)
  13. 菜鸟打造智慧物流平台 引领物流新风潮
  14. 1231321321
  15. 【 C++ 】红黑树
  16. Day25:Python基础编程(函数)能力训练50天——回文数
  17. 网工压轴题个人总结背诵版   专题二 配置VTP和STP
  18. jdbc之oracle
  19. 图纸上标注的是实际尺寸吗_施工图纸上尺寸标注的标准是什么?
  20. BGP Next Hop Self简介

热门文章

  1. 软工作业 评价输入法
  2. 在Github上搭建你的博客
  3. ASP.NET2.0入门经典(第4版)—3.5 服务器控件的类型(2)--zt
  4. open vswitch常用操作
  5. PAT乙级1028 人口普查 (20 分)
  6. 英语4级口语是计算机评分吗,2017年11月英语四级口语评分标准
  7. Python与C++动态链接库交互 win10平台
  8. 中国酒精拭子市场趋势报告、技术动态创新及市场预测
  9. 中国体脂分析仪行业市场供需与战略研究报告
  10. 中国书写工具行业市场供需与战略研究报告