唯一性索引优化实践(讲得好细哦)

主键索引和唯一性索引的区别与联系 主键索引与唯一性索引的核心区别在于约束特性和索引类型。主键索引强制非空且唯一,一个表只能有一个,通常作为聚集索引决定数据物理存储顺序;唯一性索引允许NULL值,可创建多个,默认为非聚集索引。主键索引更强调实体完整性,适合作为行唯一标识;唯一性索引侧重业务规则约束和查询优化。选择时,若需唯一标识且优化主键查询用主键索引,若需保证字段唯一性则用唯一性索引。 阅读详情

原文出处:搜索技术博客-淘宝

Unique索引,有时也称Primary Key索引,顾名思义就是对于这个索引字段每个doc的值都是唯一的,如各种id字段:product id,customer id, campaign id和bidword id等。这种类型的索引一般用来进行高效的查询,最典型的应用场景就是进行附表join查询,即对主表中查到的每一个doc,都在附表中查询其对应的附表doc信息。所以,对这种类型的索引进行优化会对整体查询性能有很好的提升,特别是在主表查询的结果很多的情况下。本文主要总结一下对于这种类型索引的优化实践,包括全量和实时增量的情况。

我们知道,在全量建索引时,在内存中一般用开链的哈希表来存储Token的Hash值及其倒排链的信息。假设有N个不同的tokens,那么这个hash数组的大小一般是取第一个大于N*(5/3)的质数P。结构如下图所示:


当一个段的索引建完以后,这个内存中的Hash表里面的tokens的哈希值及包含其倒排链和occ链等元信息的keyword terms一般被转成如下的三种数据结构之一存在文件中:


这几种数据结构的目的都是为了在查询时先mmap了这些文件以后,能对于一个给定的query keyword,快速根据其哈希值找到其对应的keyword term,进而定位到相应的倒排链和occ链等信息。不同的数据结构在不同的场景(数据特点)下对于内存空间的使用以及查询性能的影响也是不同的。下面先简要分析一下以上这几种常用数据结构的特点,然后再谈谈对于Unique类型的索引所采用的优化数据结构。

为了便于分析,假设我们有100万个不同的Tokens,每个Token的Hash值需用8个bytes表示(uint64_t)。Tokens对应的keyword terms 100万个,同时在一般情况下,每个keyword term的第一个元素就是其对应的token的hash值。在内存中建索引的时候,这个开链hash表数组的大小P取大于N*(5/3)的第一个质数,即3145739。

Closed Hash Table(闭链哈希表)

提到哈希表,不少人想到就是快,时间复杂度为O(1), 其实未必如此,这个在后面的优化讨论中再深入。对于闭链hash,其大小一般也是取第一个大于N*(5/3)的质数P来申请空间,所以空间占用一般会比较大。对于以上例子,即N=100万,那么这个Hash数组大小为P,为原始keyword terms大小的3.15倍。闭链Hash表事实上就是环形数组,如下图所示:


当查询一个token倒排链等信息的时候,首先计算其hash值,比如H,然后用H模P得到一个值作为下标,然后看这个闭链hash数组在这个下标下的元素是否是空值,如果为空(对于上图来说,就是元素的hash值为0),则直接返回表示没有查到;若不为空,则看看这个元素的hash值是否和查询值相等,若相等则找到返回,若不等则继续跟这个元素的后面元素依次进行比较,最后要么找到,要么碰到一个空元素说明没有查找到。

Skip List(跳表)

跳表,顾名思义,是能在查找的时候能快速跳过很多元素,然后在一个相对小的范围内搜索给定的一个query keyword的hash值对应的keyword term信息。跳表的实现原理是:


所以一个跳表的结构如下图所示:


在查询的时候,执行如下步骤:


注意由于按Hash值的值域进行分段跳跃,所以每个哈希值区间里面的元素个数是很可能是分布不均的,故每次二分查找的区间大小是不固定的。

Tiered Dictionary(分层词典)

Tiered dictionary的思路是分层查询定位。即,先二分查找一个小词典定位到一个大致的小范围,然后再在小范围内再继续搜索keyword term。实现的原理是:


所以,序列化好的tiered dictionary结构图如下:


那么对于任一个查询词,假设hash值为H,查找其对应的keyword term就比较简单了:


相对于skip list,tiered dictionary的查询比较稳定,因为它可以保证第二次搜索总是在一个元素较少的block中查询,而skip list无法做到这一点,这个前面提到过;但是skip list有时候可以在第一步查skip list小数组的时候就可以确定这个元素不存在,而tiered dictionary一般情况下做不到这点。

全量Unique索引优化

像很多数据结构的算法一样,在内存空间使用和查找时间之间往往需要一个权衡,Unique索引的优化也是这样,当然我们的目标还是在尽可能的在占用较少内存的情况下,使得查询速度更快。

不同于一般的字段索引,一次查询只查询一次,用在附表Join时候的Unique索引在一次查询中可能会被查询上万次(每个主表的doc结果都需要进行一次附表Unique索引查询),这就决定了查询速度是Unique索引实现的第一目标。我们看到,不管是skip list还是tiered dictionary,大部分时候都需要二分查找,特别是有时候对于不在里面的元素,二分查找比较的次数反而更多,这就决定了对于Unique索引如用这两种数据结构,在线查询的性能是很不高的,虽然它们俩是比较省内存的。

当然,我们最想达到的目标就是只比较一次,或者很有限几次就能确定一个hash值是否在一个段的某个unique索引中。我们很显然会想到哈希表,比如实现简单的闭链哈希表。的确,有些搜索引擎的索引也是这么做的。但是,对于闭链哈希表的实现,这里面有一个大坑!

对于hash table的实现,我们知道关键是hash function,记做H。好的H(x)的计算结果要分布均匀(uniform distributed)、冲突少(less collisions)。但对于闭链hash table的实现,除了冲突少,H还有一个非常重要的要求,那就是H(x)的结果集要避免簇拥在一起(avoid clustering),即要避免H(x)计算得到的数组下标是连在一起的,否则会发生非常悲惨的后果。这个其实不难理解,因为对于线性hash函数来说,闭链hash表在查找的时候若发生冲突,是依次向后比较查找,要么找到相应元素,要么碰到空元素没有找到返回。所以如果有大片的结果连在一起,如果查找的元素不在里面,同时又发生了冲突,查找到第一个空元素有时候需要比较很多次。这种情况很容易发生,比如在bidword id很多是相连在一起的情况,同时我们又采用简单取模的方式来计算hash数组存储下标。

当然,我们可以修改哈希函数来避免簇拥,这个我们增量索引优化的时候会采用。对于全量,为了在内存使用和查询效率上取得平衡,我们可以采用开链哈希表的方式来解决,其实实现也不复杂。

最简单的实现,就是将内存中的hash table里面的conflicting hash nodes list一条一条的序列化,内存中的主索引数组的元素分布情况不变,同时将conflicting nodes直接链在原hash主数组的后面。不过,为了链式存储,序列化好的每个keyword item里面会增加一个next指针和是否是每条链的最后一个节点的标记。存储好的结果如下图所示:


显然,有了这样的开链hash表结构,我们就可以保证每次都能在有限比较次数内确定一个hash值是否在索引中,而且最多比较次数就是最长的冲突链的长度。同时,我们知道一般用来建Unique索引的字段值基本上都是以加一的方式递增的,所以当哈希函数取为H(x) = x % P(P是一个较大质数),冲突显然是很少的。此外,在查询没有冲突的情况下,只需要比较一次就可以确定一个hash值是否在索引中;即使在比较查找发现有冲突的时候,大的内存跳跃查找也至多一次,因为除了第一个冲突节点,后面都所有其它的冲突节点都是存储在一起的,所以查询上具有较好的内存访问局部性,对CPU cache利用比较友好,从而查询性能比较好。

测试和线上效果证明,对于P4P广告bidword域的bid_adid Unique索引,当用以上的开链hash表存储结构替代原来的skip list实现时,查询性能提升3倍左右。

此外,在基本保持查询性能优化效果不变的情况下,我们还可以对以上开链hash表存储结构进行优化,从而占用更少的内存空间。

我们发现主hash数组里面的每个空元素也占用了一个keyword item的空间大小,但其实它们唯一的作用就是表明这个位置为空,所以我们可以用一个每个元素大小为32位(uint32_t)的数组来表明hash结果信息,其中1个bit用来表明此位置是否有hash结果,另外31个bit用来表示当有hash结果的时候,对应hash节点链在keyword items数组中的开始下标。这样就可以将整个keyword items按每条hash冲突链存储在一起了,next指针也不需要了,只需要用一个bit来标识其是否是hash冲突链的最后一个节点就可以了,一般这个flag可以从32位的doc id上面取一位来标识。故,改进后的存储结构为:


对于前面提到的那个例子,即有100万个tokens,每个Unique索引的keyword item保存token的哈希值和doc id,且有20万的hash结果是冲突的。那么,上面改进后的索引结构使用的内存空间约是原来的46%,节省了一半以上的内存空间。

【注: (3145739*4 + 1000000*12)/((3145739+200000)*16) = 45.9%; 16是因为原来的keyword item多需要额外4个bytes的next成员】

实时增量Unique索引优化

以上谈论的是对全量Unique索引的优化,实时增量索引是在内存中一条doc一条doc构建起来的,它不可能像全量时那样有一个完整的内存哈希表可以进行序列化存储。但是由于一个segment的总共doc数组数目是固定的,同时又是Unique索引,所以我们一般用闭链哈希表来存储实时增量的unique索引。

但我们前面提到过,闭链哈希表的实现有一点要非常小心,那就是哈希函数H在保存冲突少的同时也应该避免哈希结果的slots簇拥在一起。其实也就是让空元素能够均匀的分布在哈希数组中,这样即使在查询碰到哈希冲突的时候,也能够很快完成比较退出,即要么找到相应元素完成查找,要么很快就能碰到空元素表示查找不到退出。

怎么样才能避免哈希结果集簇拥在一起了呢?一个简单有效的办法为:


所以,修改后的实时增量段的Unique索引的存储结构为:


显然,修改后的算法占用了更多的内存空间。但由于是实时增量段,这些段的doc数据量一般比较小,而且会被定期合并生成和全量时候一样的索引结构,所以多一点内存空间影响不是太大。但对于查询性能的提升是非常大的,据测试和线上效果观测,经过这样的修改,查询性能提升10倍以上。


这篇文章是小编无意间看到的,其实里边大多数的东西我到现在都还没有理解,不过我会继续努力,我能告诉你这曾经是我遇到过的一道面试题么。


MongoDB索引:原理、实践与优化指南 索引之于数据库,就如同目录之于书籍——没有目录的情况下,要找到特定内容需要逐页翻阅;而有了目录,我们可以直接跳转到所需信息的位置。根据MongoDB官方统计,合理使用索引可以将查询性能提升100倍以上,同时减少95%的CPU和I/O资源消耗。 阅读详情

相关推荐

mysql 索引使用与优化

mysql 索引使用与优化

congge 1万+

4、提高插入数据的速度

在 MySQL 中,向数据表插入数据时,索引、唯一性检查、数据大小是影响插入速度的主要因素。本节将介绍优化插入数据速度的几种方法。 根据不同情况,可以分别进行优化。 对于 MyISAM 引擎的表,常见的优化方法如下: 1. 禁用索引 对非空表插入数据时,MySQL 会根据表的索引对插入的记录进行排序。插入大量数据时,这些排序会降低插入数据的速度。为了解决这种情况,可以在插入数据之前先禁用索引,等到数据都插入完毕后在开启索引。 禁用索引的语句为: ALTER TABLE table_name DISABLE

you can you up up up的博客 2356

debian mirrors 镜像源 riscv 64 赛昉 星光 starfive vf2 InRelease‘ doesn‘t support architecture ‘riscv64‘

debian mirrors 镜像 riscv 64 赛昉 星光2 starfive vf2 InRelease' doesn't support architecture 'riscv64'

qq_29758759的博客 1257

数据库优化之索引

数据库优化 索引

liuwd的博客 4344

智能车竞赛新手必看:单车定向组直线竞速与八字慢速实战技巧

本文为智能车竞赛新手提供单车定向组直线竞速与八字慢速的实战技巧,涵盖赛道分析、动力调校、传感器配置等关键环节。通过真实案例和代码示例,详细讲解如何平衡速度与稳定性,帮助选手避免常见失误,提升比赛成绩。特别针对智能车竞赛中的直线竞速和八字慢速项目,提供从硬件选型到临场策略的全方位指导。

weixin_28718793的博客 434

mysql中的各种索引大总结

文章目录为啥不用二叉搜索树?为啥不用平衡二叉(avl)树?为啥不用b-树?为啥用b+树?(重点)索引聚簇索引聚簇索引的缺点 大家都知道mysql索引底层使用的是b+树的数据结构,树有这么多种,那为啥就选择b+树呢?那就从为啥使用b+树开始,到分析其原理的思路一步步分析吧。 为啥不用二叉搜索树? 定义: 1:非空左子树的所有键值小于其根结点的键值。 2:非空右子树的所有键值大于其根结点的键值。 3:左、右子树都是二叉搜索树。就是左节点<根节点<右节点。 看下图树太高了。查询效率太低,故不推荐。

风团团 6866

双足肌肉骨骼机器人

Clone Alpha的技术突破标志着人类进入生物-机械融合的新纪元,但其影响远超技术范畴。从伦理框架重建(如拉马克进化算法中的知识传递机制)到经济模式革新(如UBI应对机器人失业潮),需全球协作制定前瞻性政策。若放任技术无序扩张,人类社会或将面临“能力断层”与“控制权丧失”的双重危机;反之,若以人类福祉为核心导向,仿生机器人亦可成为弥合残障歧视、提升整体文明层级的钥匙。当下选择,将决定人类究竟是技术的创造者,还是被重构的客体。

百态·数智思维 | 聚焦大数据、量化与未来AI 985

索引优化方法

1.关联查询优化保证被驱动表的JOIN字段已经创建了索引 需要JOIN 的字段,数据类型保持绝对一致。LEFT JOIN 时,选择小表作为驱动表, 大表作为被驱动表 。减少外层循环的次数。INNER JOIN 时,MySQL会自动将 小结果集的表选为驱动表 。选择相信MySQL优化策略。 能够直接多表关联的尽量直接关联,不用子查询。(减少查询的趟数) 不建议使用子查询,建议将子查询SQL拆开结合程序多次查询,或使用 JOIN 来代替子查询。衍生表建不了索引。2.子查询优化MySQL从4.1版本开始支持子查询

qq_52896771的博客 4747

《拉依达的嵌入式\驱动面试宝典》—操作系统篇(二)

《拉依达的嵌入式\驱动面试宝典》 整合了所有我认为可能遇到的技术面试问题。所有问题及其答案均是我个人学习后查阅资料总结,每个回答都做了仔细的分析,在嵌入式及其驱动面试相关问题解析做到全网独一无二。学习或者转行到嵌入式、驱动开发领域学习的不二之选

拉依达不拉跨的博客 470

Qt版Dalsa线扫相机的采集Demo

Qt Dalsa

追赶时代的博客 604

mysql中的聚集索引(聚簇索引)、非聚集索引、稀疏索引、稠密索引

目录 首先介绍这些索引的概念: 接着结合mysql进行讲解: 首先介绍这些索引的概念: 1、聚集索引 聚集索引:指索引项的排序方式和表中数据记录排序方式一致的索引 也就是说聚集索引的顺序就是数据的物理存储顺序。它会根据聚集索引键的顺序来存储表中的数据,即对表的数据按索引键的顺序进行排序,然后重新存储到磁盘上。因为数据在物理存放时只能有一种排列方式,所以一个表只能有一个聚集索引。......

简单,坚持 4万+

PyTorch 面试题及详细答案120题(54-70)-- 模型训练与评估

本文介绍了PyTorch模型训练的基本流程和核心概念。主要内容包括: 模型训练基本流程:通过代码示例展示了数据准备、模型定义、损失函数和优化器选择、训练循环(前向传播、反向传播、参数更新)的完整过程,并包含验证集评估的实现。 训练过程中的关键概念: Epoch:完整遍历一次训练集 Batch:将数据分成小批次 Iteration:一个Batch的参数更新过程 三者关系:1 Epoch = N/B Batch = N/B Iteration(N为样本数,B为batch_size) PyTorch数据加载机制:

大剑师兰特的GIS世界 488

主键约束、唯一性约束、唯一性索引的区别

主键约束(Primary Key Constraint)、唯一性约束(Unique Constraint)、唯一性索引(Unique Index)是保证数据唯一性的三种机制,但它们的语义、作用、实现方式有一定区别。

wang_666_888的博客 1216

【嵌入式Linux驱动开发】二十四、Linux I2C 驱动上手尝试

  人的前程关于眼界、关乎格局。   志之所趋,无远弗届,穷山复海不能限也;   志之所向,无坚不入,锐兵精甲不能御也。 一、I2C驱动框架简介   Linux内核将 I2C 驱动分为两部分: ①、 I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。 ②、 I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。 1....

悟已往之不谏 知来者之可追 1068

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

唯一性索引的意义

原文转自:http://blog.itpub.net/17203031/viewspace-700089/ 索引是我们经常使用的一种数据库搜索优化手段。适当的业务操作场景使用适当的索引方案可以显著的提升系统整体性能和用户体验。在Oracle中,索引有包括很多类型。不同类型的索引适应不同的系统环境和访问场景。其中,唯一性索引Unique Index是我们经常使用到的一种。   唯一性索引uni

万里晴空 9850

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

MybatisPlus逻辑删除与唯一性索引冲突:解决方案与最佳实践

在现代软件开发中,数据持久化层的优化和问题解决是确保应用性能和数据一致性的关键。MybatisPlus,作为Mybatis的增强版本,提供了许多便利功能,包括逻辑删除。然而,逻辑删除与数据库唯一性索引的结合可能会引发一些预料之外的冲突。本文将深入探讨这一问题,并提供一系列解决方案。

hao_haiyang的博客 2021

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

主键和唯一性索引的区别

https://blog.youkuaiyun.com/weixin_38750084/article/details/84885565

默默无闻的黄先生的博客 2152

Oracle多个字段联合索引保证唯一性

今天在开发过程中遇到了需要两个字段联合保证唯一性,所以查阅了一些资料和博客,梳理一下以便之后用到!!! 首先,你要搞明白什么是唯一索引和唯一约束,其次它们之间的区别是什么? 前提 二者的定义 唯一索引:一种索引,不允许具有索引值相同的行,从而禁止重复的索引或键值。系统在创建该索引时检查是否有重复的键值,并在每次使用 INSERT 或 UPDATE 语句添加数据时进行检查。 唯一约束:唯一约束保证在一个字段或者一组字段里的数据与表中其它行的数据相比是唯一的(联合字段中,可以包含...

Eamon_Jun的博客 8571

唯一性约束、主键约束、唯一索引的区别

1.主键约束(PRIMARY KEY) 主键用于唯一地标识表中的每一条记录 可以定义一列或多列为主键 主键列上没有任何两行具有相同值(即重复值) 主键列上也不能为空值2.唯一性约束(UNIQUE) 唯一性约束用来限制不受主键约束的列上的数据的唯一性, 即表中任意两行在 指定列上都不允许有相同的值。3.唯一索引(INDEX) 创建唯

解忧杂货铺 6249

MySQL 索引一篇讲透:原理、分类、优化与面试总结

但很多时候,我们只是记住了“B+树比哈希更适合范围查询”、“联合索引要满足最左匹配”、“覆盖索引可以减少回表”,却并没有真正理解这些结论背后的原因。 这篇文章就从面试高频问题出发,把 MySQL 索引的原理、分类、底层结构、查询过程、失效场景和优化方法系统整理一遍,帮助自己把这块知识真正吃透。

2302_80067378的博客 5916

Mysql详解——索引优化

本篇文章是对Mysql索引的创建以及优化进行一个介绍,关于索引的底层原理可以看我另一篇文章:Mysql详解——索引详解 文章目录一、索引的创建和设计原则1. 索引的声明和使用1.1 索引的分类:1.2 索引的创建:在创建表时创建索引:在已经存在的表上创建索引:1.3 索引的删除:2. MySQL8.0中索引新特性2.1 支持降序索引2.2 支持隐藏索引3. 索引的设计原则3.1 数据准备:3.2 哪些情况适合创建索引1. 字段值有唯一性的限制2. 频繁作为WHERE查询条件的字段3. 经常GROUP BY和

OYMNCHR的博客 4304

sql server 创建唯一性非聚集索引语句_MySQL优化之索引

使用Navicat for MySQL操作索引1.选中需要加索引的表,点击设计表2.选择哪些列需要建立索引,可以通过添加索引的方式给一个表添加多个索引查看SQL执行计划强调:在SQL语句执行之前,会专门有个叫查询优化器的组件对我们写的SQL进行优化和调整,然后生成执行计划,也就是说,最终执行的SQL不一定是我们当初自己写的SQL,如果出现这个情况大家不要惊讶!语法:explain 查询语句;其中t...

weixin_39928099的博客 962

唯一性索引与逻辑删除冲突问题解决思路

今天做项目中遇到了一个问题:逻辑删除和唯一性索引同时存在的情况下,已经逻辑删除的数据由于唯一性索引的约束无法再次添加,该怎么办?这次又遇到了一样的问题,想到上次的那种方法不仅不合规,而且非常麻烦(为了一个唯一性逻辑判断再调用数据库查询然后再写逻辑判断),这次就找了一下解决方法,下面简单介绍一下;所有解决方法就一个思路:通过逻辑删除字段和约束字段组合为一个索引;比如有一张teacher表,唯一性索引为name,逻辑删除字段为is_delete:唯一性索引:为了解决冲突问题,首先需要修改一下索引,让逻辑删除字段

YXXXYX的博客 4021

听说唯一性索引搜索快?

@[toc] 我们建索引的时候,有全文索引、主键索引、唯一性索引、普通索引等,前面两个好理解好区分,大家都知道啥时候用,后面两个该如何区分呢?唯一性索引和普通索引该如何选择呢?今天我们就来聊聊这个话题。 1. 准备工作 假设我有如下表: CREATE TABLE `user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT N

yangjnsjbad的博客 365

主键约束,唯一性约束,唯一性索引

约束和索引的区别:前者用来检查数据的正确性,后者用来优化查询,目的不同。 唯一性约束和唯一性索引: 创建唯一性约束会创建一个约束和一个唯一性索引,创建唯一性索引只会创建一个唯一性索引。 主键约束和唯一性约束都会创建一个唯一性索引。    ...

xjh1997的博客 1973
上一篇: 【转】53个提高php效率的小细节--值得一看
下一篇: 当页面加载的时候就捕获其keydown事件所遇到的问题以及解决办法。
chjphp
博客等级 码龄14年 1粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值