1、engine提速

MMFIles engine和rocksdb storage engine

如果我们使用的memory mapped files(mmfiles engine),当运行一个修改操作新增一个document到collection时,其他想要从collection读取document的请求将会被阻止。

当使用mostly memory engine,当运行写修改操作时,整个collection都是锁定的,即使只有一个document被增加或修改。这可以保证没有同步的query可以影响现在的query。

当时用RocksDB storage engine,仅仅是document层次的锁定,可以允许同时的写入新的document.写操作也不会影响读操作。

MMFiles

RocksDB

默认引擎

可选引擎

数据都会加载到内存中

数据也会加载到硬盘上

索引是在内存中

hot set是在内存中,索引和数据在硬盘上

重新开始的话很慢,因为索引需要重建

很快的启动,因为不需要重建索引

collection仅存在内存中

数据会持久化到硬盘上

是collection级别的锁定,写操作会阻止读操作

可以同时读和写

在试验环境下使用MMFiles会更快一些,而在生产环境下还是要选择RocksDB

切换引擎:

在服务器启动的时候指定引擎

arangod --server.storage-engine [auto|mmfiles|rocksdb]

  如果已经在mmfiles引擎下工作,想把现有的数据导入新的rocksdb中,那么需要先备份

arangodump

  第二步,找个空的文件从新启动服务器,并使用rocksdb,可能需要修改C:\ProgramData\ArangoDB文件中ENGINE的内容

arangod --server.storage-engine rocksdb

  

第三步:将在备份的文件夹中的数据导入新的引擎下

arangorestore --input-directory "dump"

  

2、index提速

使用document key来查找

RETURN DOCUMENT('products/31931')

  这个查找是很快的,因为在_key或者_id上有primary index.下面的查找也会返回同样的数据。

FOR prod IN products
 FILTER prod._key == '31931'
 RETURN prod

  这个操作也是在_key级别筛选,因此和上面的查询速度是一样快的。

不同的是DOCUMENT方法必须传入_key或则_id参数,而下面的FOR语句可以是任何的属性,不一定要是_key。如:

FOR prod IN products
 FILTER prod.ASIN == '0851993575'
 RETURN prod

  但是这个查询是秒级别的查询,上面的查询是毫秒级别的查询。为什么会有这么大的差异呢?

这就好比去图书馆(图书馆相当于collection)借书(书相当于document),如果你说出图书的名称和编码(相当于_key)就可以不打开书的情况下找到这本书。而如果你说出的是书中的内容,那么找书的时候就需要每一本书都打开遍历其内容才能找到想要的书,这个速度势必慢多了,这中扫描方式叫做full collection scan。这就是索引的好处。

如果我们知道只有一个匹配结果,我们可以使用LIMIT来限制查询结果,找到结果就返回

FOR prod IN products
 FILTER prod.ASIN == '0851993575'
 LIMIT 1 // stop after first match
 RETURN prod

  如何避免full collection scan呢,就是对ASIN建立索引,让ASIN出现在书的封面上,不用去打开书就可以找到书。我们可以为ASIN建立一个hash索引,可以选择unique。

现在我们再次按照ASIN进行查询,发现它也变成毫秒级别的了。查看explain发现,执行过程中没有FILTER了,

创建索引选择的字段必须是惟一的,否则使用hash索引创建时会失败。

3、Joins

如我们想要将products collection中的categorykey替换为可读的category label:

FOR prod IN products
 LET categories = (
 FOR catKey IN prod.categoryKeys
 FOR cat IN categories
 FILTER cat._key == catKey
 RETURN cat.label
 )
RETURN MERGE(prod, {categories: categories})

  因为有category key的存在,这变得比较容易。

一个原则:

对于嵌套的FOR循环,请确保你要查找的字段是index.即这里的FILTER查询的字段是索引。

4、Hidden lookups

looksup in upsert operation(使用upsert更新或插入)upsert操作其实隐藏了lookups的操作。

示例:创建一个新的collection ranks

FOR prod IN products
 UPSERT { salesrank: prod.salesrank }
 INSERT { salesrank: prod.salesrank, count: 1 }
 UPDATE { count: OLD.count + 1} IN ranks

  这个UPSERT{salesrank:prod.salesrank}将要扫描所有的document.因为salesrank不是索引,所以我们需要为ranks collection创建一个索引salesrank,使用该索引后插入将变得很快。

最佳选择是使用COLLECT,在不用把salesrank设置为索引的情况下,运行速度比把salesrank设置为索引还要快。

FOR prod IN products
 COLLECT salesrank = prod.salesrank
 WITH COUNT INTO count
 INSERT { salesrank, count }
 INTO ranks

  

5、排序列表skip-list index

 查找卖的最好的产品

FOR prod IN products
 SORT prod.salesrank
 FILTER prod.salesrank > 0
 LIMIT 100
 RETURN prod

  这个查询是很慢的,并且如果要运行程序的话会一遍一遍的排序。我们可以为salesrank创建一个hash索引,但是它将没有任何帮助,因为hash索引对于查找很有帮助,对于比较索引值的大小没有任何用处。现在我们需要的其实是skip-list index.skip-list 其实是一个排好序的列表,用来返回一组数据是很快的。使用skip-list我们不需要一遍一遍的再进行排序。

6、同时使用hash index 和skip-list index?

如果我们想要某种类别产品的最好的销售商。那么我们可能需要用hash index来对category 进行索引,使用skip-list index对salesrank进行索引。但是实际是不可能的,我们只能选择一种。要么同时使用hash index,要么同时使用skip-list index.

 

FOR prod IN products
 SORT prod.salesrank
 FILTER 'Study & Teaching[13985]' IN prod.categories
 LIMIT 100
 RETURN CONCAT(prod.salesrank, '. ', prod.title)

  有另一种选择是我们可以对categories中的元素和salesrank同时使用skip-list index.这会创建一个很大的索引,但是它却是高效的查询。

FOR prod IN products
 FILTER prod.salesrank > 0 AND prod.salesrank < 100000
 SORT prod.salesrank
 FILTER 'Wild West[513098]' IN prod.categories
 LIMIT 100
 RETURN prod

  使用范围界限在salesrank会加快提取速度。

7、graph model

什么时候使用join连接,什么时候使用graph model呢?

一个原则:如果你预先不知道你查询的深度,那么使用graph遍历会比join更快一些。比如类别之间有从属关系Category relations。

如果要查找相似产品,我们可以首先创建相似产品的collection :similar collection 

FOR p IN products
 FOR s IN p.similar OR []
 LET to = FIRST(FOR p2 IN products
 FILTER p2.ASIN == s
 LIMIT 1
 RETURN p2._id)
 FILTER to != null
 INSERT {_from: p._id, _to: to} INTO similar

  

我们可以通过GRAPHS来创建可视化的图形。

我们可以通过创建的graph进行查找:

通过ASIN找到一个产品,通过similar找到相似的产品,通过相似产品的排序找到最畅销的相似的产品

FOR prod IN products
 FILTER prod.ASIN == '0140314504'
 FOR v, e, p IN 1..2 ANY prod GRAPH 'similar_products'
 OPTIONS {uniqueVertices: 'global', bfs: true}
 SORT v.salesrank
 LIMIT 10
 RETURN {
 title: v.title,
 parent: p.vertices[-2].title,
 rank: v.salesrank
 }

  

8、字符串查询

模糊匹配

FOR prod IN products
 FILTER LIKE(prod.title,'%avocado%',true)
 RETURN prod.title

  LIKE用正则表达式来构造查询,我们也可以使用 CONTAINS来查询

FOR prod IN products
 FILTER CONTAINS(LOWER(prod.title), 'avocado')
 RETURN prod.title

  这里虽然没有用索引但是CONTAINS的性能远大于LIKE正则表达式的性能。

我们还可以对字母进行排序,为title创建一个skip list index ,这时我们可以使用范围选择来进行筛选,如:

FOR prod IN products
 FILTER prod.title >= 'Avoc' AND prod.title < 'Avod'
 RETURN prod.title

  这个也可以用于在客户端自动完成建议匹配,该方法的一个限制是他是区分大小写。

最好的解决办法是对title建立全文索引。

FOR prod IN FULLTEXT(products, 'title', 'avocado')
 RETURN prod.title

  显示个数

FOR prod IN FULLTEXT(products, 'title', 'complete:avocado', 10)
 RETURN prod.title

  complete:avocado和avocado是一样的。除了complete之外还有"prefix:avoc,按照前缀搜索,这里不用区分大小写。

9、点中心索引Vertex centric indexes

如果在graph中需要去查询很多的edge,那么使用vertex-centric index将会大大的提高性能。

10、查询缓存

如果有很多人运行相同的查询,那么我们要一遍一遍的运行查询。这会导致低效,我们可以配置查询缓存来存储第一次查询的结果,以后只要数据没有发生变化,相同的查询请求将直接返回数据,而无需再次运行请求。