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