各种笔记
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

18 KiB

索引基础 索引允许快速访问文档,前提是在查询中使用了索引属性。尽管ArangoDB自动为某些系统属性建立索引,但用户可以自由地在文档的非系统属性上创建额外的索引。

可以在集合级别上创建用户定义的索引。可以通过指定索引属性的名称来创建大多数用户定义的索引。一些索引类型只允许索引一个属性(例如全文本索引),而其他索引类型只允许同时索引多个属性。

通过学习ArangoDB性能课程,学习如何有效地使用不同的索引 。

该系统属性_id,_key,_from并且_to通过ArangoDB会自动建立索引,而不需要用户为他们创造额外的索引。 _id并且_key被集合的主键 覆盖,_from并且_to被边缘集合的边缘索引自动覆盖。

使用该系统属性_id在用户定义的索引是不可能的,但索引_key,_rev,_from,和_to是。

默认情况下,创建新索引是在独占集合锁下完成的。创建索引时,该集合不可用。如果您必须在没有专用维护窗口的实时系统上执行索引创建,那么“前景”索引可能是不可取的。

对于可能长时间运行的索引创建操作,RocksDB存储引擎还支持在“后台”创建索引。该集合在索引创建期间仍然(大部分)可用,请参阅“ 在后台创建索引 ”部分以获取更多信息。

ArangoDB提供以下索引类型:

主要指标 对于每个集合,总会有一个主索引,该主索引是集合中所有文档的文档关键字(_key属性)的持久索引。主索引允许使用_key或_id属性快速选择集合中的文档。在_key或上执行相等查找时,将自动在AQL查询中使用它 _id。

还有一些专用功能来查找给定的文档,_key或者_id 始终使用主索引的文档:

db.collection.document(""); db._document(""); 当对持久性索引进行排序时,主索引可用于范围查询和排序。

集合的主索引不能删除或更改,也没有创建用户定义的主索引的机制。

边缘指数 每个边缘集合还具有一个自动创建的边缘索引。边缘索引可通过文档的_from或_to属性快速访问文档。因此,它可用于快速查找顶点文档之间的连接,并在查询顶点的连接边时调用。

对边缘集合_from 或_to边缘集合中的值执行相等查找时,会从AQL中使用边缘索引。还有一些专用函数可以找到给定的边_from或边_to值,这些边将始终利用边索引:

db.collection.edges(""); db.collection.edges(""); db.collection.outEdges(""); db.collection.outEdges(""); db.collection.inEdges(""); db.collection.inEdges(""); 边缘索引存储所有_from和_to属性的并集。它可以用于相等查找,但不能用于范围查询或排序。将自动为边缘集合创建边缘索引。

无法创建用户定义的边缘索引。但是,可以在用户定义的索引中自由使用_from和_to属性。

不能删除或更改边缘索引。

持久指数 持久索引是具有持久性的排序索引。存储或更新文档时,索引条目将写入磁盘。这意味着在重新启动服务器或初始加载索引的集合时,不需要从集合数据中重建索引条目。因此,使用持久索引可以减少集合加载时间。

持久索引类型目前可用于二级索引。这意味着持久性索引当前不能成为集合的唯一索引,因为该集合将始终存在内存中的主索引,并且可能还有更多的索引(例如Edge集合的edge索引)。

索引实现使用的是RocksDB引擎,它为插入,更新和删除操作提供了对数复杂性。由于持久索引不是内存索引,因此它不像所有内存索引一样将指针存储到主索引中,而是存储文档的主键。为了通过索引值查找通过持久索引检索文档,因此将在主索引中进行额外的O(1)查找以获取实际文档。

在对持久索引进行排序时,它可以用于点查找,范围查询和排序操作,但前提是必须在查询中提供所有索引属性,或者指定索引属性的最左前缀。

TTL(生存时间)索引 ArangoDB提供的TTL索引类型可用于自动从集合中删除过期的文档。

通过设置expireAfter值并选择包含文档创建日期和时间的单个文档属性来设置TTL索引。文档expireAfter在创建时间后几秒钟后过期。创建时间被指定为数字时间戳(Unix时间戳)或格式的日期字符串YYYY-MM-DDTHH:MM:SS,可以选择使用格式 的小数点后的毫秒数YYYY-MM-DDTHH:MM:SS.MMM和可选的时区偏移量。所有没有时区偏移的日期字符串都将被解释为UTC日期。

例如,如果expireAfter将设置为600秒(10分钟),并且索引属性为“ creationDate”,则存在以下文档:

{ "creationDate" : 1550165973 } 该文档将使用创建日期时间值索引,该值1550165973将转换为人类可读的date 2019-02-14T17:39:33.000Z。此文档将在时间戳记1550166573(或 2019-02-14T17:49:33.000Z人类可读版本)之后的600秒后过期。

实际删除过期文件并不一定会立即发生。过期的文档最终将由后台线程删除,该线程定期遍历所有TTL索引并删除过期的文档。可以使用--ttl.frequency 启动选项配置调用此后台线程的频率。

无法保证何时将完全删除过期的文档,因此查询仍然可以找到并返回已经过期的文档。这些文件最终将在后台线程启动并具有删除过期文档的能力时被删除。但是可以保证,只有超过其到期时间的文档才会被真正删除。

请注意,自1970年1月1日(Unix时间戳)以来,必须以秒为单位指定index属性的数字日期时间值。要以这种格式从JavaScript计算当前时间戳,可以使用Date.now() / 1000;。要从任意Date实例计算它,有Date.getTime() / 1000。在AQL中,您可以 DATE_NOW() / 1000将任意Unix时间戳(以毫秒为单位)除以1000,以将其转换为秒。

另外,可以将index属性值指定为date字符串,格式为 YYYY-MM-DDTHH:MM:SS,可选的格式是小数点后的毫秒YYYY-MM-DDTHH:MM:SS.MMM和可选的时区偏移量。所有没有时区偏移的日期字符串都将被解释为UTC日期。

上面使用datestring属性值的示例文档为

{ "creationDate" : "2019-02-14T17:39:33.000Z" } 如果index属性不包含数字值或正确的日期字符串,则该文档将不会存储在TTL索引中,因此不会成为过期和删除的候选对象。为索引属性提供非数字值或什至不提供值是防止文档过期和删除的一种受支持方法。

地理索引 用户可以在集合中的一个或多个属性上创建其他地理索引。地理索引用于快速找到地球表面的位置。

地理索引存储二维坐标。它可以在两个单独的文档属性(纬度和经度)上创建,也可以在包含纬度和经度的单个数组属性上创建。纬度和经度必须是数字值。

地理索引提供了以下操作:查找坐标最接近给定比较坐标的文档,以及查找坐标在围绕比较坐标的指定半径内的文档。

地理位置索引是通过AQL中的专用功能(简单查询功能)使用的,当在AQL中将SORT或FILTER与距离功能一起使用时,它会隐式应用。否则,它将不会用于其他类型的查询或条件。

全文索引 全文索引可用于在文档中查找单词或单词前缀。全文索引只能在单个属性上创建,并且将对文档中包含的具有该属性文本值的所有单词进行索引。仅索引(指定)最小长度的单词。使用libicu提供的单词边界分析来完成单词标记化,该分析考虑了服务器启动时提供的所选语言。单词以小写形式索引。该指数支持完全匹配的查询(满语)和前缀查询,加上基本的逻辑操作,例如 and,or和not用于组合部分结果。

全文索引是稀疏的,这意味着它只会索引设置了index属性且包含字符串值的文档。此外,索引中仅包含具有可配置的最小长度的单词。

全文索引通过AQL中的专用功能或简单查询使用,但不会为其他类型的查询或条件启用。

对于高级全文搜索功能,请考虑ArangoSearch。

索引属性和子属性 顶级以及嵌套属性都可以建立索引。对于顶级属性,仅需要属性名称。要为单个字段建立索引,请将具有单个元素(属性键的字符串)的数组传递给sureIndex()方法的fields参数 。要在多个字段上创建组合索引,只需将更多成员添加到fields数组中:

// { name: "Smith", age: 35 } db.posts.ensureIndex({ type: "persistent", fields: [ "name" ] }) db.posts.ensureIndex({ type: "persistent", fields: [ "name", "age" ] }) 要索引子属性,请使用点符号指定属性路径:

// { name: {last: "Smith", first: "John" } } db.posts.ensureIndex({ type: "persistent", fields: [ "name.last" ] }) db.posts.ensureIndex({ type: "persistent", fields: [ "name.last", "name.first" ] }) 索引数组值 如果索引属性包含一个数组,则默认情况下,ArangoDB将整个数组存储为索引值。这样就无法通过索引访问数组的各个成员。

若要使索引将单个数组成员而不是整个数组值插入到索引中,需要为该属性创建一个特殊的数组索引。使用该collection.ensureIndex()函数可以像常规持久性索引一样设置数组索引。为了使持久索引成为数组索引,[*] 在创建索引以及使用IN运算符在AQL查询中进行过滤时,需要扩展索引属性名称。

以下示例tags在名为的集合中的属性 上创建持久数组索引posts:

db.posts.ensureIndex({ type: "persistent", fields: [ "tags[*]" ] }); db.posts.insert({ tags: [ "foobar", "baz", "quux" ] }); 然后,该数组索引可用于tags通过IN运算符从AQL查询中查找单个值:

FOR doc IN posts FILTER 'foobar' IN doc.tags RETURN doc 可以添加数组扩展运算符 [*],但这不是强制性的。您可以使用它来表示已使用数组索引,但是它纯粹是修饰性的:

FOR doc IN posts FILTER 'foobar' IN doc.tags[*] RETURN doc 以下FILTER条件将不使用数组索引:

FILTER doc.tags ANY == 'foobar' FILTER doc.tags ANY IN 'foobar' FILTER doc.tags IN 'foobar' FILTER doc.tags == 'foobar' FILTER 'foobar' == doc.tags 也可以在数组值的子属性上创建索引。如果index属性是一个对象数组,例如:

db.posts.ensureIndex({ type: "persistent", fields: [ "tags[*].name" ] }); db.posts.insert({ tags: [ { name: "foobar" }, { name: "baz" }, { name: "quux" } ] }); 然后,以下查询将使用数组索引(这确实需要 数组扩展运算符):

FOR doc IN posts FILTER 'foobar' IN doc.tags[*].name RETURN doc 如果存储的文档中的数组确实包含不具有子属性的元素,则该文档也将被索引为value null,该值在ArangoDB中等于不存在的属性。

ArangoDB支持使用[*]每个索引属性使用单个运算符来创建数组索引。例如,不支持如下创建索引:

db.posts.ensureIndex({ type: "persistent", fields: [ "tags[].name[].value" ] }); 数组值在插入数组索引之前将自动进行重复数据删除。例如,如果将以下文档插入到集合中,则重复的数组值bar将仅被插入一次:

db.posts.insert({ tags: [ "foobar", "bar", "bar" ] }); 这样做是为了避免对同一文档重复存储相同索引值,这不会带来任何好处。

如果将数组索引声明为unique,则会在将数组值插入索引之前对数组值进行重复数据删除,因此上述具有两个相同值的插入操作 bar不一定会失败

如果索引已经包含该bar值的实例,它将总是失败。但是,如果bar索引中不存在该值,则对数组值进行重复数据删除将实际上导致bar仅插入一次。

要关闭数组值的重复数据删除,可以将数组索引的重复数据删除属性设置为false。但是,重复数据删除的默认值为true,因此,如果未明确关闭重复数据删除,则会进行重复数据删除。

db.posts.ensureIndex({ type: "persistent", fields: [ "tags[*]" ], deduplicate: false });

// will fail now db.posts.insert({ tags: [ "foobar", "bar", "bar" ] }); 如果声明了数组索引,并且您在指定的属性中存储没有数组的文档,则该文档不会插入索引中。因此,以下对象将不被索引:

db.posts.ensureIndex({ type: "persistent", fields: [ "tags[*]" ] }); db.posts.insert({ something: "else" }); db.posts.insert({ tags: null }); db.posts.insert({ tags: "this is no array" }); db.posts.insert({ tags: { content: [1, 2, 3] } }); 数组索引能够索引显式null值。当查询null值时,它将仅返回那些已明确null存储在数组中的文档,而不会返回任何根本没有数组的文档。

db.posts.ensureIndex({ type: "persistent", fields: [ "tags[*]" ] }); db.posts.insert({tags: null}) // Will not be indexed db.posts.insert({tags: []}) // Will not be indexed db.posts.insert({tags: [null]}); // Will be indexed for null db.posts.insert({tags: [null, 1, 2]}); // Will be indexed for null, 1 and 2 将数组索引声明为稀疏对索引的数组部分没有影响,这尤其意味着null在稀疏版本中也对显式值进行索引。如果将索引与数组和常规属性组合在一起,则稀疏性将适用于该属性,例如:

db.posts.ensureIndex({ type: "persistent", fields: [ "tags[*]", "name" ], sparse: true }); db.posts.insert({tags: null, name: "alice"}) // Will not be indexed db.posts.insert({tags: [], name: "alice"}) // Will not be indexed db.posts.insert({tags: [1, 2, 3]}) // Will not be indexed db.posts.insert({tags: [1, 2, 3], name: null}) // Will not be indexed db.posts.insert({tags: [1, 2, 3], name: "alice"}) // Will be indexed for [1, "alice"], [2, "alice"], [3, "alice"] db.posts.insert({tags: [null], name: "bob"}) // Will be indexed for [null, "bob"] 请注意,仅在查询使用IN操作符对索引属性进行过滤时,使用数组索引的过滤仅在AQL查询中有效。其它比较运算符(==,!=,>,>=,<,<=,ANY,ALL,NONE)不能目前使用阵列的索引。

顶点中心索引 如上所述,图形最重要的索引是边缘索引,它索引边缘集合的_from和_to属性。它们提供了对源自或到达给定顶点的所有边的非常快速的访问,这允许快速找到图中顶点的所有邻居。

在许多情况下,我们希望运行更具体的查询,例如,在给定顶点起源的边中仅查找那些时间戳大于或等于某个日期和时间的边。确切地说,这是通过“以顶点为中心的索引”实现的。从某种意义上讲,它们是每个单个顶点处的边缘集合的局部索引。

从技术上说,他们在ArangoDB实现为索引,其排序完整边集合首先_from,然后通过其他属性出境遍历,或者先通过_to,然后由其他属性入境遍历。对于在ANY方向上的遍历,需要两个索引,一个索引与_from另一个_to索引作为第一个索引字段。

例如,如果我们在属性_from和 timestamp边缘集合上有一个持久索引,则可以通过在索引中进行一次范围查找来非常快速地回答上述问题。

从ArangoDB 3.0开始,可以创建排序索引(类型为“持久”)来索引特殊边缘属性_from或_to 其他属性。从ArangoDB 3.1开始,当FILTER优化器找到适当的语句时,这些将用于图遍历。

例如,要创建上述类型的顶点中心索引,您只需

db.edges.ensureIndex({"type":"persistent", "fields": ["_from", "timestamp"]}); 在阿兰戈什。然后,查询如

FOR v, e, p IN 1..1 OUTBOUND "V/1" edges FILTER e.timestamp >= "2018-07-09" RETURN p 如果有许多来自顶点的边,"V/1"但是只有少数带有最近时间戳记的边,则速度会更快。请注意,即使顶点中心索引实际上可能更快,优化器也可能会基于估计的成本而选择默认边缘索引而不是顶点中心索引。对于高度连接的图形和RocksDB存储引擎,更可能选择以顶点为中心的索引。

在后台创建索引 在v3.5.0中引入

默认情况下,创建新索引是在独占集合锁下完成的。这意味着只要创建索引,集合(或相应的分片)就不可用于写操作。如果您必须在没有专用维护窗口的实时系统上执行索引创建,那么“前景”索引可能是不可取的。

索引也可以在“后台”中创建,而不在整个索引创建过程中使用排他锁。该集合基本上保持可用,因此在创建索引时其他CRUD操作可以在该集合上运行。这可以通过在创建索引时设置inBackground属性来实现。

要在arangosh中在后台创建索引,只需指定inBackground: true,如以下示例所示:

// create the persistent index in the background db.collection.ensureIndex({ type: "persistent", fields: [ "value" ], unique: false, inBackground: true }); db.collection.ensureIndex({ type: "persistent", fields: [ "email" ], unique: true, inBackground: true });

// also supported for geo and fulltext indexes db.collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude"], inBackground: true }); db.collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude"], inBackground: true }); db.collection.ensureIndex({ type: "fulltext", fields: [ "text" ], minLength: 4, inBackground: true }) 行为 仍在构建过程中的索引将无法通过ArangoDB API看到。但是,在仍然开始创建索引的同时,不可能通过sureIndex API 两次创建相同的索引。AQL查询也不会使用这些索引,直到索引报告已完全创建。请注意,在索引完全准备好之前,最初的sureIndex调用或HTTP请求仍将阻塞。因此,现有的单线程客户端程序可以安全地将inBackground选项设置为true并继续像以前一样工作。

如果要在后台建立索引,则不能重命名或删除集合。这些操作将阻塞,直到索引创建完成。前景索引也是如此。

在索引构建中断之后(即由于服务器崩溃),部分构建的索引将被删除。然后,在ArangoDB集群中,可能会在受影响的分片上自动重新创建索引。

性能 后台索引创建可能比“前景”索引创建慢,并且需要更多RAM。在繁重的写操作(特别是许多删除,更新或替换操作)下,后台索引创建需要在RAM中保留已删除文档的列表。如果此列表增加到数千万个条目,这可能变得难以为继。

建立索引始终(内部)是繁重的写操作,在负载较少的时候建立索引始终是一个好主意。