如何改善数据库的结构,以及如何使用索引来缩短搜索时间

Posted 人邮异步社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何改善数据库的结构,以及如何使用索引来缩短搜索时间相关的知识,希望对你有一定的参考价值。

本章的示例演示了如何改善数据库,如何添加索引以提高查询性能,以及如何通过限制可添加到索引中的值来确保数据完整性。另外,本章还将介绍如何自动删除过时的数据,确保报告只依据最新的漏洞信息。

10.1 定义数据库索引

在MongoDB中,将键设置为索引相当于告诉底层系统,在文档集合中多次搜索该键值对的操作可能会被执行,因此MongoDB将为这个键去维护值索引,从而极大地提高搜索和检索操作的速度。

在MongoDB中,为何要给文档集合添加索引呢?原因有2个。首先,通过将某个键设置为索引,可更快速而高效地搜索这个键。当数据库很大,而且还编写了大量数据库查询操作的分析脚本时,这种做法将变得更为重要。

其次,索引有助于确保数据完整性。索引具有独一无二性,它向MongoDB指出,只能有一个文档有给定的键值,这让数据库更有弹性,能够避免意外的数据重复。例如,将IP地址键设置为独一无二的索引,那么在添加新文档时,如果其IP地址与现有文档的IP地址相同,那么这种操作将引发错误。

10.1.1 设置索引

要设置索引,可使用createIndex命令。这里只将每个集合中独一无二的标识字段(即hosts集合中的IP地址和vulnerabilities集合中的OID)设置为索引。设置索引的语法如下:

db.hosts.createIndex(keyname:1, unique:1)

在上述代码片段中,keyname是索引的键,而unique:1告诉MongoDB,这个键是独一无二的。

接下来,打开MongoDB并设置2个索引,如程序清单10-1所示。

程序清单10-1 创建索引

   $ mongo
   > use vulnmgt
   switched to db vulnmgt
   > db.hosts.createIndex(ip:1, unique:1)
 
       "createdCollectionAutomatically" : false,
       "numIndexesBefore" : 1,
       "numIndexesAfter" : 2,
       "ok" : 1
   
   > db.vulnerabilities.createIndex(oid:1, unique:1)
   
       "createdCollectionAutomatically" : false,
       "numIndexesBefore" : 1,
       "numIndexesAfter" : 2,
       "ok" : 1
   

创建索引可能需要较长的时间,这取决于文档集合的规模。如果文档集合很大,就更应该创建索引。如果createIndex命令成功了,它将返回一个JSON文档,文档中包含有关该索引的信息。

10.1.2 测试索引

创建独一无二的索引后,对其进行测试,确认不能通过现有的键值来插入新文档。为此,可找出一个既有的键,并尝试使用这个键值来新建一个文档,如程序清单10-2所示。

程序清单10-2 测试唯一性约束

   > db.hosts.find(ip: "10.0.1.18")
    "_id" : ObjectId("57d734bf1d41c8b71daaee13"), "mac" :  "vendor" : "Raspberry Pi Foundation",  
"addr" : "B8:27:EB:59:A8:E1" , "ip" : "10.0.1.18", "ports" : [  "port" : "22", "state" :"open", "service" :
   --snip--
   > db.hosts.insert(ip:"10.0.1.18")
   WriteResult(
       "nInserted" : 0,
       "writeError" : 
           "code" : 11000,
         ❶ "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key
           error index: vulnmgt.hosts.$ip_1 dup key:  : \\"10.0.1.18\\" "
       
   )
   > db.vulnerabilities.find(oid:"1.3.6.1.4.1.25623.1.0.80091")
    "_id" : ObjectId("57fc2c891d41c805cf22111f"), "oid" : "1.3.6.1.4.1.25623.1.0.80091", "summary" :  
"The remote host implements TCP timestamps and therefore allows to compute\\nthe uptime.",  
"cvss" : 2.6,
   --snip--
   > db.vulnerabilities.insert(oid:"1.3.6.1.4.1.25623.1.0.80091")
   WriteResult(
       "nInserted" : 0,
       "writeError" : 
           "code" : 11000,
         ❷ "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate
           key error index: vulnmgt.vulnerabilities.$oid_1 dup key:  : 
           \\"1.3.6.1.4.1.25623.1.0.80091\\" "
       
   )

当试图使用现有文档的相同索引值去添加新文档时,MongoDB将返回duplicate key index错误(,❷)。这种错误可避免因不良代码导致的数据库集合中出现重复文档的问题。

10.1.3 定制

可将文档结构中的其他键设置为索引,例如,在第9章中,如果使用了散列值来唯一地标识漏洞结果,就应将该散列值(而不是OID)作为索引。

如果数据库已经很大或预期它可能变得更大,那么现在就可以去阅读第11章和第13章,看看将使用哪些查询,进而确定要将哪些键作为索引,以缩短查询时间。

10.2 确保数据是最新的

报告的表现不可能比它依据的数据更好,因此确保数据是最新的至关重要。

将来自Nmap和OpenVAS的主机信息导入到数据库的脚本中会更新主机信息,并在需要时插入新主机。但对于老主机,该怎么办呢?假设您在1月份扫描了一台服务器,并将其信息添加到了数据库中,但等到2月份时,这台服务器退役了。这时就需要将过时的信息从数据库中剔除,以确保报告依据的是实实在在的主机和漏洞数据。

可以让漏洞数据库保持原样,因为它包含的漏洞信息相对而言变化不大。但每次导入漏洞扫描报告时(参见9.4节),都必须清除并重新生成漏洞映射,只要定期地扫描并导入结果,漏洞映射就不会过时。

10.2.1 确定清理参数

如果将没有出现在最后一次扫描结果中的条目都删除,将有丢失重要数据的风险。因为最后一次扫描时,有些系统可能处于离线状态或不可达状态。如果永久地保留所有的数据,数据库中可能充斥着无关的数据,导致更难找到需要的动态信息。对于过时的数据,应保留多久呢?或者说对于特定的主机,在多少次扫描后其数据都没有更新时,可认为它已退役,并能将它删除呢?

答案取决于组织多久扫描一次以及组织的资产管理策略。在10.2.2节的脚本中,假设每周扫描一次,同时对于持续1个月(即4次扫描)都没有更新的信息,就认为它已过时,进而将其删除。

本书前面介绍插入所有数据库条目的脚本时,设置了时间戳,并在更新数据时更新时间戳。因此,可根据这些时间戳来找出并删除hosts数据库中至少4周没有更新的文档。

执行这种清理的方式有多种:在MongoDB命令行中手工执行命令;编写一个自动运行MongoDB命令的bash脚本;使用Python脚本执行这些操作。出于一致性考虑,本节将使用Python来执行清理操作。

10.2.2 使用Python清理数据库

程序清单10-3通过删除从特定日期到当前一直没变的数据来清理数据库。默认情况下,这个脚本假设28天前的数据都是过时的,可将其删除。

程序清单10-3 一个简单的数据库清理脚本(db-clean.py)

   #!/usr/bin/env python3

   # v0.2
   # Andrew Magnusson

   from pymongo import MongoClient
   import datetime, sys

   client = MongoClient('mongodb://localhost:27017')
   db = client['vulnmgt']

 olderThan = 28

   def main():

❷ date = datetime.datetime.utcnow()
❸ oldDate = date - datetime.timedelta(days=olderThan)
❹ hostsremoved = db.hosts.find('updated': '$lt': oldDate).count()
❺ db.hosts.remove('updated': '$lt': oldDate)
❻ print("Stale hosts removed:", hostsremoved)

   main()

这个脚本确定当前日期❷,再使用olderThan来确定要删除的文档的截止日期❸。然后,它查询数据库以获取一个列表,其中包含updated值早于截止日期的所有文档❹。最后,再让MongoDB将这些文档都删除❺并打印由被删除的文档组成的列表❻。

由于这个脚本设置删除间隔为28天,因此应至少每28天执行它一次,为此可使用cron来调度它。第12章将更详细地介绍调度操作。

10.2.3 定制

请根据执行Nmap和漏洞扫描器的时间间隔,在程序清单10-3所示的脚本中相应地定制“过时数据”变量(olderThan)。第12章将更详细地讨论扫描间隔。

还可使用MongoDB TTL索引(而不是脚本)。通过在主机文档集合中添加TTL索引,可让MongoDB自动删除至少28天未变的已更新字段的文档。请避免在数据分析期间自动执行这些删除操作。

10.3 小结

提高数据库的速度并改善数据的质量后,可想想这个数据库能够为您做什么了。

本文摘《漏洞管理实战:网络风险管理的策略方法》

漏洞管理实用教程,美亚4.5分好评图书,包含了较新的漏洞管理技术与实战项目代码,先理论后实战,内容由浅入深,帮助你打造一个有效的漏洞管理系统。

本书分别从概念和实战两个角度对漏洞管理进行了剖析,其中涉及与漏洞管理相关的概念和漏洞管理过程的步骤,以及构建免费或低成本漏洞管理系统的实用方法。
本书共 15 章,具体内容包括漏洞管理的基本概念、信息来源、漏洞扫描器、自动漏洞管理、处理漏洞、组织支持和办公室规则、搭建环境、使用数据收集工具、创建资产和漏洞数据库、维护数据库、生成资产和漏洞报告、自动执行扫描和生成报告、如何生成 html 形式的高级报告、与漏洞管理相关的高阶主题,以及未来的安全发展趋势及其对漏洞管理过程的影响。
本书先理论后实战,编排顺序合理,可帮助信息安全从业人员(尤其是漏洞研究人员和漏洞管理人员)打造一个可用、实用的漏洞管理系统,从而为所在的组织提供保护。

以上是关于如何改善数据库的结构,以及如何使用索引来缩短搜索时间的主要内容,如果未能解决你的问题,请参考以下文章

SAS:如何使用索引来挑选宏数组变量

应该针对不同的排序和过滤条件创建哪些MongoDB索引来提高性能?

如何根据名称而不是索引来订购选择?

sphinx增量索引和主索引来实现索引的实时更新

集合-ConcurrentSkipListMap 源码解析

集合-ConcurrentSkipListMap 源码解析