当前位置:首页 » 操作系统 » 元型数据库

元型数据库

发布时间: 2024-04-11 12:59:28

Ⅰ 如何用 Python 实现一个图数据库(Graph Database)

本文章是 重写 500 Lines or Less 系列的其中一篇,目标是重写 500 Lines or Less 系列的原有项目:Dagoba: an in-memory graph database。

Dagoba 是作者设计用来展示如何从零开始自己实现一个图数据库( Graph Database )。该名字似乎来源于作者喜欢的一个乐队,另一个原因是它的前缀 DAG 也正好是有向无环图 ( Directed Acyclic Graph ) 的缩写。本文也沿用了该名称。

图是一种常见的数据结构,它将信息描述为若干独立的节点( vertex ,为了和下文的边更加对称,本文中称为 node ),以及把节点关联起来的边( edge )。我们熟悉的链表以及多种树结构可以看作是符合特定规则的图。图在路径选择、推荐算法以及神经网络等方面都是重要的核心数据结构。

既然图的用途如此广泛,一个重要的问题就是如何存储它。如果在传统的关系数据库中存储图,很自然的做法就是为节点和边各自创建一张表,并用外键把它们关联起来。这样的话,要查找某人所有的子女,就可以写下类似下面的查询:

还好,不算太复杂。但是如果要查找孙辈呢?那恐怕就要使用子查询或者 CTE(Common Table Expression) 等特殊构造了。再往下想,曾孙辈又该怎么查询?孙媳妇呢?

这样我们会意识到,sql 作为查询语言,它只是对二维数据表这种结构而设计的,用它去查询图的话非常笨拙,很快会变得极其复杂,也难以扩展。针对图而言,我们希望有一种更为自然和直观的查询语法,类似这样:

为了高效地存储和查询图这种数据结构,图数据库( Graph Database )应运而生。因为和传统的关系型数据库存在极大的差异,所以它属于新型数据库也就是 NoSql 的一个分支(其他分支包括文档数据库、列数据库等)。图数据库的主要代表包括 Neo4J 等。本文介绍的 Dagoba 则是具备图数据库核心功能、主要用于教学和演示的一个简单的图数据库。

原文代码是使用 JavaScript 编写的,在定义调用接口时大量使用了原型( prototype )这种特有的语言构造。对于其他主流语言的用户来说,原型的用法多少显得有些别扭和不自然。

考虑到本系列其他数据库示例大多是用 Python 实现的,本文也按照传统,用 Python 重写了原文的代码。同样延续之前的惯例,为了让读者更好地理解程序是如何逐步完善的,我们用迭代式的方法完成程序的各个组成部分。

原文在 500lines 系列的 Github 仓库中只包含了实现代码,并未包含测试。按照代码注释说明,测试程序位于作者的另一个代码库中,不过和 500lines 版本的实现似乎略有不同。

本文实现的代码参考了原作者的测试内容,但跳过了北欧神话这个例子——我承认确实不熟悉这些神祇之间的亲缘关系,相信中文背景的读者们多数也未必了解,虽然作者很喜欢这个例子,想了想还是不要徒增困惑吧。因此本文在编写测试用例时只参考了原文关于家族亲属的例子,放弃了神话相关的部分,尽管会减少一些趣味性,相信对于入门级的代码来说这样也够用了。

本文实现程序位于代码库的 dagoba 目录下。按照本系列程序的同意规则,要想直接执行各个已完成的步骤,读者可以在根目录下的 main.py 找到相应的代码位置,取消注释并运行即可。

本程序的所有步骤只需要 Python3 ,测试则使用内置的 unittest , 不需要额外的第三方库。原则上 Python3.6 以上版本应该都可运行,但我只在 Python3.8.3 环境下完整测试过。

本文实现的程序从最简单的案例开始,通过每个步骤逐步扩展,最终形成一个完整的程序。这些步骤包括:

接下来依次介绍各个步骤。

回想一下,图数据库就是一些点( node )和边( edge )的集合。现在我们要做出的一个重大决策是如何对节点/边进行建模。对于边来说,必须指定它的关联关系,也就是从哪个节点指向哪个节点。大多数情况下边是有方向的——父子关系不指明方向可是要乱套的!

考虑到扩展性及通用性问题,我们可以把数据保存为字典( dict ),这样可以方便地添加用户需要的任何数据。某些数据是为数据库内部管理而保留的,为了明确区分,可以这样约定:以下划线开头的特殊字段由数据库内部维护,类似于私有成员,用户不应该自己去修改它们。这也是 Python 社区普遍遵循的约定。

此外,节点和边存在互相引用的关系。目前我们知道边会引用到两端的节点,后面还会看到,为了提高效率,节点也会引用到边。如果仅仅在内存中维护它们的关系,那么使用指针访问是很直观的,但数据库必须考虑到序列化到磁盘的问题,这时指针就不再好用了。

为此,最好按照数据库的一般要求,为每个节点维护一个主键( _id ),用主键来描述它们之间的关联关系。

我们第一步要把数据库的模型建立起来。为了测试目的,我们使用一个最简单的数据库模型,它只包含两个节点和一条边,如下所示:

按照 TDD 的原则,首先编写测试:

与原文一样,我们把数据库管理接口命名为 Dagoba 。目前,能够想到的最简单的测试是确认节点和边是否已经添加到数据库中:

assert_item 是一个辅助方法,用于检查字典是否包含预期的字段。相信大家都能想到该如何实现,这里就不再列出了,读者可参考 Github 上的完整源码

现在,测试是失败的。用最简单的办法实现数据库:

需要注意的是,不管添加节点还是查询,程序都使用了拷贝后的数据副本,而不是直接使用原始数据。为什么要这样做?因为字典是可变的,用户可以在任何时候修改其中的内容,如果数据库不知道数据已经变化,就很容易发生难以追踪的一致性问题,最糟糕的情况下会使得数据内容彻底混乱。

拷贝数据可以避免上述问题,代价则是需要占用更多内存和处理时间。对于数据库来说,通常查询次数要远远多于修改,所以这个代价是可以接受的。

现在测试应该正常通过了。为了让它更加完善,我们可以再测试一些边缘情况,看看数据库能否正确处理异常数据,比如:

例如,如果用户尝试添加重复主键,我们预期应抛出 ValueError 异常。因此编写测试如下:

为了满足以上测试,代码需要稍作修改。特别是按照 id 查找主键是个常用操作,通过遍历的方法效率太低了,最好是能够通过主键直接访问。因此在数据库中再增加一个字典:

完整代码请参考 Github 仓库。

在上个步骤,我们在初始化数据库时为节点明确指定了主键。按照数据库设计的一般原则,主键最好是不具有业务含义的代理主键( Surrogate key ),用户不应该关心它具体的值是什么,因此让数据库去管理主键通常是更为合理的。当然,在部分场景下——比如导入外部数据——明确指定主键仍然是有用的。

为了同时支持这些要求,我们这样约定:字段 _id 表示节点的主键,如果用户指定了该字段,则使用用户设置的值(当然,用户有责任保证它们不会重复);否则,由数据库自动为它分配一个主键。

如果主键是数据库生成的,事先无法预知它的值是什么,而边( edge )必须指定它所指向的节点,因此必须在主键生成后才能添加。由于这个原因,在动态生成主键的情况下,数据库的初始化会略微复杂一些。还是先写一个测试:

为支持此功能,我们在数据库中添加一个内部字段 _next_id 用于生成主键,并让 add_node 方法返回新生成的主键:

接下来,再确认一下边是否可以正常访问:

运行测试,一切正常。这个步骤很轻松地完成了,不过两个测试( DbModelTest 和 PrimaryKeyTest )出现了一些重复代码,比如 get_item 。我们可以把这些公用代码提取出来。由于 get_item 内部调用了 TestCase.assertXXX 等方法,看起来应该使用继承,但从 TestCase 派生基类容易引起一些潜在的问题,所以我转而使用另一个技巧 Mixin :

实现数据库模型之后,接下来就要考虑如何查询它了。

在设计查询时要考虑几个问题。对于图的访问来说,几乎总是由某个节点(或符合条件的某一类节点)开始,从与它相邻的边跳转到其他节点,依次类推。所以链式调用对查询来说是一种很自然的风格。举例来说,要知道 Tom 的孙子养了几只猫,可以使用类似这样的查询:

可以想象,以上每个方法都应该返回符合条件的节点集合。这种实现是很直观的,不过存在一个潜在的问题:很多时候用户只需要一小部分结果,如果它总是不计代价地给我们一个巨大的集合,会造成极大的浪费。比如以下查询:

为了避免不必要的浪费,我们需要另外一种机制,也就是通常所称的“懒式查询”或“延迟查询”。它的基本思想是,当我们调用查询方法时,它只是把查询条件记录下来,而并不立即返回结果,直到明确调用某些方法时才真正去查询数据库。

如果读者比较熟悉流行的 Python ORM,比如 SqlAlchemy 或者 Django ORM 的话,会知道它们几乎都是懒式查询的,要调用 list(result) 或者 result[0:10] 这样的方法才能得到具体的查询结果。

在 Dagoba 中把触发查询的方法定义为 run 。也就是说,以下查询执行到 run 时才真正去查找数据:

和懒式查询( Lazy Query )相对应的,直接返回结果的方法一般称作主动查询( Eager Query )。主动查询和懒式查询的内在查找逻辑基本上是相同的,区别只在于触发机制不同。由于主动查询实现起来更加简单,出错也更容易排查,因此我们先从主动查询开始实现。

还是从测试开始。前面测试所用的简单数据库数据太少,难以满足查询要求,所以这一步先来创建一个更复杂的数据模型:

此关系的复杂之处之一在于反向关联:如果 A 是 B 的哥哥,那么 B 就是 A 的弟弟/妹妹,为了查询到他们彼此之间的关系,正向关联和反向关联都需要存在,因此在初始化数据库时需要定义的边数量会很多。

当然,父子之间也存在反向关联的问题,为了让问题稍微简化一些,我们目前只需要向下(子孙辈)查找,可以稍微减少一些关联数量。

因此,我们定义数据模型如下。为了减少重复工作,我们通过 _backward 字段定义反向关联,而数据库内部为了查询方便,需要把它维护成两条边:

然后,测试一个最简单的查询,比如查找某人的所有孙辈:

这里 outcome/income 分别表示从某个节点出发、或到达它的节点集合。在原作者的代码中把上述方法称为 out/in 。当然这样看起来更加简洁,可惜的是 in 在 Python 中是个关键字,无法作为函数名。我也考虑过加个下划线比如 out_.in_ 这种形式,但看起来也有点怪异,权衡之后还是使用了稍微啰嗦一点的名称。

现在我们可以开始定义查询接口了。在前面已经说过,我们计划分别实现两种查询,包括主动查询( Eager Query )以及延迟查询( Lazy Query )。

它们的内在查询逻辑是相通的,看起来似乎可以使用继承。不过遵循 YAGNI 原则,目前先不这样做,而是只定义两个新类,在满足测试的基础上不断扩展。以后我们会看到,与继承相比,把共同的逻辑放到数据库本身其实是更为合理的。

接下来实现访问节点的方法。由于 EagerQuery 调用查询方法会立即返回结果,我们把结果记录在 _result 内部字段中。虽然 node 方法只返回单个结果,但考虑到其他查询方法几乎都是返回集合,为统一起见,让它也返回集合,这样可以避免同时支持集合与单结果的分支处理,让代码更加简洁、不容易出错。此外,如果查询对象不存在的话,我们只返回空集合,并不视为一个错误。

查询输入/输出节点的方法实现类似这样:

查找节点的核心逻辑在数据库本身定义:

以上使用了内部定义的一些辅助查询方法。用类似的逻辑再定义 income ,它们的实现都很简单,读者可以直接参考源码,此处不再赘述。

在此步骤的最后,我们再实现一个优化。当多次调用查询方法后,结果可能会返回重复的数据,很多时候这是不必要的。就像关系数据库通常支持 unique/distinct 一样,我们也希望 Dagoba 能够过滤重复的数据。

假设我们要查询某人所有孩子的祖父,显然不管有多少孩子,他们的祖父应该是同一个人。因此编写测试如下:

现在来实现 unique 。我们只要按照主键把重复数据去掉即可:

在上个步骤,初始化数据库指定了双向关联,但并未测试它们。因为我们还没有编写代码去支持它们,现在增加一个测试,它应该是失败的:

运行测试,的确失败了。我们看看要如何支持它。回想一下,当从边查找节点时,使用的是以下方法:

这里也有一个潜在的问题:调用 self.edges 意味着遍历所有边,当数据库内容较多时,这是巨大的浪费。为了提高性能,我们可以把与节点相关的边记录在节点本身,这样要查找边只要看节点本身即可。在初始化时定义出入边的集合:

在添加边时,我们要同时把它们对应的关系同时更新到节点,此外还要维护反向关联。这涉及对字典内容的部分复制,先编写一个辅助方法:

然后,将添加边的实现修改如下:

这里的代码同时添加正向关联和反向关联。有的朋友可能会注意到代码略有重复,是的,但是重复仅出现在该函数内部,本着“三则重构”的原则,暂时不去提取代码。

实现之后,前面的测试就可以正常通过了。

在这个步骤中,我们来实现延迟查询( Lazy Query )。

延迟查询的要求是,当调用查询方法时并不立即执行,而是推迟到调用特定方法,比如 run 时才执行整个查询,返回结果。

延迟查询的实现要比主动查询复杂一些。为了实现延迟查询,查询方法的实现不能直接返回结果,而是记录要执行的动作以及传入的参数,到调用 run 时再依次执行前面记录下来的内容。

如果你去看作者的实现,会发现他是用一个数据结构记录执行操作和参数,此外还有一部分逻辑用来分派对每种结构要执行的动作。这样当然是可行的,但数据处理和分派部分的实现会比较复杂,也容易出错。

本文的实现则选择了另外一种不同的方法:使用 Python 的内部函数机制,把一连串查询变换成一组函数,每个函数取上个函数的执行结果作为输入,最后一个函数的输出就是整个查询的结果。由于内部函数同时也是闭包,尽管每个查询的参数形式各不相同,但是它们都可以被闭包“捕获”而成为内部变量,所以这些内部函数可以采用统一的形式,无需再针对每种查询设计额外的数据结构,因而执行过程得到了很大程度的简化。

首先还是来编写测试。 LazyQueryTest 和 EagerQueryTest 测试用例几乎是完全相同的(是的,两种查询只在于内部实现机制不同,它们的调用接口几乎是完全一致的)。

因此我们可以把 EagerQueryTest 的测试原样不变拷贝到 LazyQueryTest 中。当然拷贝粘贴不是个好注意,对于比较冗长而固定的初始化部分,我们可以把它提取出来作为两个测试共享的公共函数。读者可参考代码中的 step04_lazy_query/tests/test_lazy_query.py 部分。

程序把查询函数的串行执行称为管道( pipeline ),用一个变量来记录它:

然后依次实现各个调用接口。每种接口的实现都是类似的:用内部函数执行真正的查询逻辑,再把这个函数添加到 pipeline 调用链中。比如 node 的实现类似下面:

其他接口的实现也与此类似。最后, run 函数负责执行所有查询,返回最终结果;

完成上述实现后执行测试,确保我们的实现是正确的。

在前面我们说过,延迟查询与主动查询相比,最大的优势是对于许多查询可以按需要访问,不需要每个步骤都返回完整结果,从而提高性能,节约查询时间。比如说,对于下面的查询:

以上查询的意思是从孙辈中找到一个符合条件的节点即可。对该查询而言,主动查询会在调用 outcome('son') 时就遍历所有节点,哪怕最后一步只需要第一个结果。而延迟查询为了提高效率,应在找到符合条件的结果后立即停止。

目前我们尚未实现 take 方法。老规矩,先添加测试:

主动查询的 take 实现比较简单,我们只要从结果中返回前 n 条记录:

延迟查询的实现要复杂一些。为了避免不必要的查找,返回结果不应该是完整的列表( list ),而应该是个按需返回的可迭代对象,我们用内置函数 next 来依次返回前 n 个结果:

写完后运行测试,确保它们是正确的。

从外部接口看,主动查询和延迟查询几乎是完全相同的,所以用单纯的数据测试很难确认后者的效率一定比前者高,用访问时间来测试也并不可靠。为了测试效率,我们引入一个节点访问次数的概念,如果延迟查询效率更高的话,那么它应该比主动查询访问节点的次数更少。

为此,编写如下测试:

我们为 Dagoba 类添加一个成员来记录总的节点访问次数,以及两个辅助方法,分别用于获取和重置访问次数:

然后浏览代码,查找修改点。增加计数主要在从边查找节点的时候,因此修改部分如下:

此外还有 income/outcome 方法,修改都很简单,这里就不再列出。

实现后再次运行测试。测试通过,表明延迟查询确实在效率上优于主动查询。

不像关系数据库的结构那样固定,图的形式可以千变万化,查询机制也必须足够灵活。从原理上讲,所有查询无非是从某个节点出发按照特定方向搜索,因此用 node/income/outcome 这三个方法几乎可以组合出任意所需的查询。

但对于复杂查询,写出的代码有时会显得较为琐碎和冗长,对于特定领域来说,往往存在更为简洁的名称,例如:母亲的兄弟可简称为舅舅。对于这些场景,如果能够类似 DSL (领域特定语言)那样允许用户根据专业要求自行扩展,从而简化查询,方便阅读,无疑会更为友好。

如果读者去看原作者的实现,会发现他是用一种特殊语法 addAlias 来定义自己想要的查询,调用方法时再进行查询以确定要执行的内容,其接口和内部实现都是相当复杂的。

而我希望有更简单的方法来实现这一点。所幸 Python 是一种高度动态的语言,允许在运行时向类中增加新的成员,因此做到这一点可能比预想的还要简单。

为了验证这一点,编写测试如下:

无需 Dagoba 的实现做任何改动,测试就可以通过了!其实我们要做的就是动态添加一个自定义的成员函数,按照 Python 对象机制的要求,成员函数的第一个成员应该是名为 self 的参数,但这里已经是在 UnitTest 的内部,为了和测试类本身的 self 相区分,新函数的参数增加了一个下划线。

此外,函数应返回其所属的对象,这是为了链式调用所要求的。我们看到,动态语言的灵活性使得添加新语法变得非常简单。

到此,一个初具规模的图数据库就形成了。

和原文相比,本文还缺少一些内容,比如如何将数据库序列化到磁盘。不过相信读者都看到了,我们的数据库内部结构基本上是简单的原生数据结构(列表+字典),因此序列化无论用 pickle 或是 JSON 之类方法都应该是相当简单的。有兴趣的读者可以自行完成它们。

我们的图数据库实现为了提高查询性能,在节点内部存储了边的指针(或者说引用)。这样做的好处是,无论数据库有多大,从一个节点到相邻节点的访问是常数时间,因此数据访问的效率非常高。

但一个潜在的问题是,如果数据库规模非常大,已经无法整个放在内存中,或者出于安全性等原因要实现分布式访问的话,那么指针就无法使用了,必须要考虑其他机制来解决这个问题。分布式数据库无论采用何种数据模型都是一个棘手的问题,在本文中我们没有涉及。有兴趣的读者也可以考虑 500lines 系列中关于分布式和集群算法的其他一些文章。

本文的实现和系列中其他数据库类似,采用 Python 作为实现语言,而原作者使用的是 JavaScript ,这应该和作者的背景有关。我相信对于大多数开发者来说, Python 的对象机制比 JavaScript 基于原型的语法应该是更容易阅读和理解的。

当然,原作者的版本比本文版本在实现上其实是更为完善的,灵活性也更好。如果想要更为优雅的实现,我们可以考虑使用 Python 元编程,那样会更接近于作者的实现,但也会让程序的复杂性大为增加。如果读者有兴趣,不妨对照着去读读原作者的版本。

Ⅱ 常用的数据库有哪些

1. IBM 的DB2

作为关系数据库领域的开拓者和领航人,IBM在1997年完成了System R系统的原型,1980年开始提供集成的数据库服务器—— System/38,随后是SQL/DSforVSE和VM,其初始版本与SystemR研究原型密切相关。DB2 forMVSV1 在1983年推出。该版本的目标是提供这一新方案所承诺的简单性,数据不相关性和用户生产率。1988年DB2 for MVS 提供了强大的在线事务处理(OLTP)支持,1989 年和1993 年分别以远程工作单元和分布式工作单元实现了分布式数据库支持。最近推出的DB2 Universal Database 6.1则是通用数据库的典范,是第一个具备网上功能的多媒体关系数据库管理系统,支持包括Linux在内的一系列平台。

2. Oracle

Oracle 前身叫SDL,由Larry Ellison 和另两个编程人员在1977创办,他们开发了自己的拳头产品,在市场上大量销售,1979 年,Oracle公司引入了第一个商用SQL 关系数据库管理系统。Oracle公司是最早开发关系数据库的厂商之一,其产品支持最广泛的操作系统平台。目前Oracle关系数据库产品的市场占有率名列前茅。

3. Informix

Informix在1980年成立,目的是为Unix等开放操作系统提供专业的关系型数据库产品。公司的名称Informix便是取自Information 和Unix的结合。Informix第一个真正支持SQL语言的关系数据库产品是Informix SE(StandardEngine)。InformixSE是在当时的微机Unix环境下主要的数据库产品。它也是第一个被移植到Linux上的商业数据库产品。

4. Sybase

Sybase公司成立于1984年,公司名称“Sybase”取自“system”和 “database” 相结合的含义。Sybase公司的创始人之一Bob Epstein 是Ingres 大学版(与System/R同时期的关系数据库模型产品)的主要设计人员。公司的第一个关系数据库产品是1987年5月推出的SybaseSQLServer1.0。Sybase首先提出Client/Server 数据库体系结构的思想,并率先在Sybase SQLServer 中实现。

5. SQL Server

1987 年,微软和 IBM合作开发完成OS/2,IBM 在其销售的OS/2 ExtendedEdition 系统中绑定了OS/2Database Manager,而微软产品线中尚缺少数据库产品。为此,微软将目光投向Sybase,同Sybase 签订了合作协议,使用Sybase的技术开发基于OS/2平台的关系型数据库。1989年,微软发布了SQL Server 1.0 版。

6.PostgreSQL

PostgreSQL 是一种特性非常齐全的自由软件的对象——关系性数据库管理系统(ORDBMS),它的很多特性是当今许多商业数据库的前身。PostgreSQL最早开始于BSD的Ingres项目。PostgreSQL 的特性覆盖了SQL-2/SQL-92和SQL-3。首先,它包括了可以说是目前世界上最丰富的数据类型的支持;其次,目前PostgreSQL 是唯一支持事务、子查询、多版本并行控制系统、数据完整性检查等特性的唯一的一种自由软件的数据库管理系统.

7.mySQL

mySQL是一个小型关系型数据库管理系统,开发者为瑞典MySQL AB公司。在2008年1月16号被Sun公司收购。目前MySQL被广泛地应用在Internet上的中小型网站中。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了MySQL作为网站数据库。MySQL的官方网站的网址是: www.mysql.com

Ⅲ 数据挖掘概念综述

数据挖掘概念综述
数据挖掘又称从数据库中发现知识(KDD)、数据分析、数据融合(Data Fusion)以及决策支持。KDD一词首次出现在1989年8月举行的第11届国际联合人工智能学术会议上。随后在1991年、1993年和1994年都举行KDD 专题讨论会,汇集来自各个领域的研究人员和应用开发者,集中讨论数据统计、海量数据分析算 法、知识表示、知识运用等问题。随着参与人员的不断增多,KDD国际会议发展成为年会。1998 年在美国纽约举行的第四届知识发现与数据 挖掘国际学术会议不仅进行了学术讨论,并且有30多家软件公司展示了他们的数据挖掘软件产品,不少软件已在北美、欧洲等国得到应用。
一、什么是数据挖掘
1.1、数据挖掘的历史
近十几年来,人们利用信息技术生产和搜集数据的能力大幅度提高,千万万个数据库被用于商业管理、政府办公、科学研究和工程开发等等,这一势头仍将持续发展下去。于是,一个新的挑战被提了出来:在这被称之为信息爆炸的时代,信息过量几乎成为人人需要面对的问题。如何才能不被信息的汪洋大海所淹没,从中及时发现有用的知识,提高信息利用率呢?要想使数据真正成为一个公司的资源,只有充分利用它为公司自身的业务决策和战略发展服务才行,否则大量的数据可能成为包袱,甚至成为垃圾。因此,面对”人们被数据淹没,人们却饥饿于知识”的挑战。另一方面计算机技术的另一领域——人工智能自1956年诞生之后取得了重大进展。经历了博弈时期、自然语言理解、知识工程等阶段,目前的研究 热点是机器学习。机器学习是用计算机模拟人类学习的一门科学,比较成熟的算法有神经网络、遗传算法等。用数据库管理系统来存储数据,用机器学习的方法来分析数据,挖掘大量数据背后的知识,这两者的结合促成了数据库中的知识发现(KDD:Knowledge Discovery in Databases)的产生,因此,数据挖掘和知识发现(DMKD)技术应运而生,并得以蓬勃发展,越来越显示出其强大的生命力。
数据挖掘又称从数据库中发现知识(KDD)、数据分析、数据融合(Data Fusion)以及决策支持。KDD一词首次出现在1989年8月举行的第11届国际联合人工智能学术会议上。随后在1991年、1993年和1994年都举行KDD 专题讨论会,汇集来自各个领域的研究人员和应用开发者,集中讨论数据统计、海量数据分析算 法、知识表示、知识运用等问题。随着参与人员的不断增多,KDD国际会议发展成为年会。1998 年在美国纽约举行的第四届知识发现与数据 挖掘国际学术会议不仅进行了学术讨论,并且有30多家软件公司展示了他们的数据挖掘软件产品,不少软件已在北美、欧洲等国得到应用。
2.2数据挖掘的概念
从1989年到现在,KDD的定义随着人们研究的不断深入也在不断完善,目前比较公认的定义是Fayyad 等给出的:KDD是从数据集中识别出有效的、新颖的、潜在有用的以及最终可理解模式的高级处理过程。从定义可以看出,数据挖掘(DataMining)就是从大量的、不完全的、有噪声的、模糊的、随机的数据中,提取隐含在其中的、人们事先不知道的、但又是潜在有用的信息和知识的过程。人们把原始数据看作是形成知识的源泉,就像从矿石中采矿一样。原始数据可以是结构化的,如关系数据库中的数据,也可以是半结构化的,如文本、图形、图像数据,甚至是分布在网络上的异构型数据。发现知识的方法可以是数学的,也可以是非数学的;可以是演绎的,也可以是归纳的。发现了的知识可以被用于信息管理、查询优化、决策支持、过程控制等,还可以用于数据自身的维护。因此,数据挖掘是一门很广义的交叉学科,它汇聚了不同领域的研究者,尤其是数据库、人工智能、数理统计、可视化、并行计算等方面的学者和工程技术人员。
特别要指出的是,数据挖掘技术从一开始就是面向应用的。它不仅是面向特定数据库的简单检索查询调用,而且要对这些数据进行微观、中观乃至宏观的统计、分析、综合和推理,以指导实际问题的求解,企图发现事件间的相互关联,甚至利用已有的数据对未来的活动进行预测。
一般来说在科研领域中称为KDD,而在工程领域则称为数据挖掘。
二、数据挖掘的步骤
KDD包括以下步骤:
1、数据准备
KDD的处理对象是大量的数据,这些数据一般存储在数据库系统中,是长期积累的结果。但往往不适合直接在这些数据上面进行知识挖 掘,需要做数据准备工作,一般包括数据的选择(选择相关的数据)、净化(消除噪音、冗余数据)、推测(推算缺失数据)、转换(离散值 数据与连续值数据之间的相互转换,数据值的分组分类,数据项之间的计算组合等)、数据缩减(减少数据量)。如果KDD的对象是数据仓 库,那么这些工作往往在生成数据仓库时已经准备妥当。数据准备是KDD 的第一个步骤,也是比较重要的一个步骤。数据准备是否做好将影 响到数据挖掘的效率和准确度以及最终模式的有效性。
2、数据挖掘
数据挖掘是KDD最关键的步骤,也是技术难点所在。研究KDD的人员中大部分都在研究数据挖掘技术,采用较多的技术有决策树、分类、 聚类、粗糙集、关联规则、神经网络、遗传算法等。数据挖掘根据KDD的目标,选取相应算法的参数,分析数据,得到可能形成知识的模式 模型。
3、评估、解释模式模型
上面得到的模式模型,有可能是没有实际意义或没有实用价值的,也有可能是其不能准确反映数据的真实意义,甚至在某些情况下是与事 实相反的,因此需要评估,确定哪些是有效的、有用的模式。评估可以根据用户多年的经验,有些模式也可以直接用数据来检验其准确性。 这个步骤还包括把模式以易于理解的方式呈现给用户。
4、巩固知识
用户理解的、并被认为是符合实际和有价值的模式模型形成了知识。同时还要注意对知识做一
致性检查,解决与以前得到的知识互相冲 突、矛盾的地方,使知识得到巩固。
5、运用知识
发现知识是为了运用,如何使知识能被运用也是KDD的步骤之一。运用知识有两种方法:一种是只需看知识本身所描述的关系或结果,就 可以对决策提供支持;另一种是要求对新的数据运用知识,由此可能产生新的问题,而需要对知识做进一步的优化
三、数据挖掘的特点及功能
3.1、数据挖掘的特点
数据挖掘具有如下几个特点,当然,这些特点与数据挖掘要处理的数据和目的是密切相关的。
1、处理的数据规模十分巨大。
2、查询一般是决策制定者(用户)提出的即时随机查询,往往不能形成精确的查询要求。
3、由于数据变化迅速并可能很快过时,因此需要对动态数据作出快速反应,以提供决策支持。
4、主要基于大样本的统计规律,其发现的规则不一定适用于所有数据
3.2、数据挖掘的功能
数据挖掘所能发现的知识有如下几种:
广义型知识,反映同类事物共同性质的知识;
特征型知识,反映事物各方面的特征知识;
差异型知识,反映不同事物之间属性差别的知识 ;关联型知识,反映事物之间依赖或关联的知识;
预测型知识,根据历史的和当前的数据推测未来数据;偏离型知识,揭示事物偏离常规的异常现象。
所有这些知识都可以在不同的概念层次上被发现,随着概念树的提升,从微观到中观再到宏观,以满足不同用户、不同层次决策的需要。例如,从一家超市的数据仓库中,可以发现的一条典型关联规则可能是”买面包和黄油的顾客十有八九也买牛奶”,也可能是”买食品的顾客几乎都用信用卡”,这种规则对于商家开发和实施客户化的销售计划和策略是非常有用的。至于发现工具和方法,常用的有分类、聚类、减维、模式识别、可视化、决策树、遗传算法、不确定性处理等。归纳起来,数据挖掘有如下几个功能:
预测/验证功能:预测/验证功能指用数据库的若干已知字段预测或验证其他未知字段值。预测方法有统计分析方法、关联规则和决策树预测方法、回归树预测方法等。
描述功能:描述功能指找到描述数据的可理解模式。描述方法包括以下几种:数据分类、回归分析、簇聚、概括、构造依赖模式、变化和偏差分析、模式发现、路径发现等。
四、数据挖掘的模式
数据挖掘的任务是从数据中发现模式。模式是一个用语言L来表示的一个表达式E,它可用来描述数据集F中数据的特性,E 所描述的数据是集 合F的一个子集FE。E作为一个模式要求它比列举数据子集FE中所有元素的描述方法简单。例如,“如果成绩在81 ~90之间,则成绩优良”可称 为一个模式,而“如果成绩为81、82、83、84、85、86、87、88、89 或90,则成绩优良”就不能称之为一个模式。
模式有很多种,按功能可分有两大类:预测型(Predictive)模式和描述型(Descriptive)模式。
预测型模式是可以根据数据项的值精确确定某种结果的模式。挖掘预测型模式所使用的数据也都是可以明确知道结果的。例如,根据各种 动物的资料,可以建立这样的模式:凡是胎生的动物都是哺乳类动物。当有新的动物资料时,就可以根据这个模式判别此动物是否是哺乳动物。
描述型模式是对数据中存在的规则做一种描述,或者根据数据的相似性把数据分组。描述型模式不能直接用于预测。例如,在地球上,70 %的表面被水覆盖,30 %是土地。
在实际应用中,往往根据模式的实际作用细分为以下6 种:
1、分类模式
分类模式是一个分类函数( 分 类 器),能够把数据集中的数据项映射到某个给定的类上。分类模式往往表现为一棵分类树,根据数据的 值从树根开始搜索,沿着数据满足的分支往上走,走到树叶就能确定类别。
2、回归模式
回归模式的函数定义与分类模式相似,它们的差别在于分类模式的预测值是离散的,回归模式的预测值是连续的。如给出某种动物的特征,可以用分类模式判定这种动物是哺乳动物还是鸟类;给出某个人的教育情况、工作经验,可以用回归模式判定这个人的年工资在哪个范围内,是在6000元以下,还是在6000元到1万元之间,还是在1万元以上。
3、时间序列模式
时间序列模式根据数据随时间变化的趋势预测将来的值。这里要考虑到时间的特殊性质,像一些周期性的时间定义如星期、月、季节、年 等,不同的日子如节假日可能造成的影响,日期本身的计算方法,还有一些需要特殊考虑的地方如时间前后的相关性(过去的事情对将来有 多大的影响力)等。只有充分考虑时间因素,利用现有数据随时间变化的一系列的值,才能更好地预测将来的值。
4、聚类模式
聚类模式把数据划分到不同的组中,组之间的差别尽可能大,组内的差别尽可能小。与分类模式不同,进行聚类前并不知道将要划分成几 个组和什么样的组,也不知道根据哪一(几)个数据项来定义组。一般来说,业务知识丰富的人应该可以理解这些组的含义,如果产生的模式无法理解或不可用,则该模式可能是无意义的,需要回到上阶段重新组织数据。
5、关联模式
关联模式是数据项之间的关联规则。关联规则是如下形式的一种规则:“在无力偿还贷款的人当中,60%的人的月收入在3000元以下。”
6、序列模式
序列模式与关联模式相仿,而把数据之间的关联性与时间联系起来。为了发现序列模式,不仅需要知道事件是否发生,而且需要确定事件 发生的时间。例如,在购买彩电的人们当中,60%的人会在3个月内购买影碟机
五、数据挖掘的发现任务
数据挖掘涉及的学科领域和方法很多,有多种分类法。根据挖掘任务分,可分为分类或预测模型发现、数据总结、聚类、关联规则发现、序列模式发现、依赖关系或依赖模型发现、异常和趋势发现等等;根据挖掘对象分,有关系数据库、面向对象数据库、空间数据库、时态数据库、文本数据源、多媒体数据库、异质数据库、遗产数据库以及环球网Web;根据挖掘方法分,可粗分为:机器学习方法、统计方法、神经网络方法和数据库方法。机器学习中,可细分为:归纳学习方法(决策树、规则归纳等)、基于范例学习、遗传算法等。统计方法中,可细分为:回归分析(多元回归、自回归等)、判别分析(贝叶斯判别、费歇尔判别、非参数判别等)、聚类分析(系统聚类、动态聚类等)、探索性分析(主元分析法、相关分析法等)等。神经网络方法中,可细分为:前向神经网络(BP算法等)、自组织神经网络(自组织特征映射、竞争学习等)等。数据库方法主要是多维数据分析或OLAP 方法,另外还有面向属性的归纳方法。
从挖掘任务和挖掘方法的角度而言有数据总结、分类发现、聚类和关联规则发现四种非常重要的发现任务。
5.1、数据总结
数据总结目的是对数据进行浓缩,给出它的紧凑描述。传统的也是最简单的数据总结方法是计算出数据库的各个字段上的求和值、平均值、方差值等统计值,或者用直方图、饼状图等图形方式表示。数据挖掘主要关心从数据泛化的角度来讨论数据总结。数据泛化是一种把数据库中的有关数据从低层次抽象到高层次上的过程。由于数据库上的数据或对象所包含的信息总是最原始、基本的信息(这是为了不遗漏任何可能有用的数据信息)。人们有时希望能从较高层次的视图上处理或浏览数据,因此需要对数据进行不同层次上的泛化以适应各种查询要求。数据泛化目前主要有两种技术:多维数据分析方法和面向属性的归纳方法。
1、多维数据分析方法是一种数据仓库技术,也称作联机分析处理(OLAP)。数据仓库是面向决策支持的、集成的、稳定的、不同时间的历史数据集合。决策的前提是数据分析。在数据分析中经常要用到诸如求和、总计、平均、最大、最小等汇集操作,这类操作的计算量特别大。因此一种很自然的想法是,把汇集操作结果预先计算并存储起来,以便于决策支持系统使用。存储汇集操作结果的地方称作多维数据库。多维数据分析技术已经在决策支持系统中获得了成功的应用,如着名的SAS数据分析软件包、Business Object公司的决策支持系统Business Object,以及IBM公司的决策分析工具都使用了多维数据分析技术。
采用多维数据分析方法进行数据总结,它针对的是数据仓库,数据仓库存储的是脱机的历史数据。
2、为了处理联机数据,研究人员提出了一种面向属性的归纳方法。它的思路是直接对用户感兴趣的数据视图(用一般的SQL查询语言即可获得)进行泛化,而不是像多维数据分析方法那样预先就存储好了泛化数据。方法的提出者对这种数据泛化技术称之为面向属性的归纳方法。原始关系经过泛化操作后得到的是一个泛化关系,它从较高的层次上总结了在低层次上的原始关系。有了泛化关系后,就可以对它进行各种深入的操作而生成满足用户需要的知识,如在泛化关系基础上生成特性规则、判别规则、分类规则,以及关联规则等。
5.2、分类发现
分类在数据挖掘中是一项非常重要的任务,目前在商业上应用最多。分类的目的是学会一个分类函数或分类模型(也常常称作分类器),该模型能把数据库中的数据项映射到给定类别中的某一个。分类和回归都可用于预测。预测的目的是从利用历史数据纪录中自动推导出对给定数据的推广描述,从而能对未来数据进行预测。和回归方法不同的是,分类的输出是离散的类别值,而回归的输出则是连续数值。
要构造分类器,需要有一个训练样本数据集作为输入。训练集由一组数据库记录或元组构成,每个元组是一个由有关字段(又称属性或特征)值组成的特征向量,此外,训练样本还有一个类别标记。一个具体样本的形式可为:( v1, v2, …, vn; c );其中vi表示字段值,c表示类别。
分类器的构造方法有统计方法、机器学习方法、神经网络方法等等。统计方法包括贝叶斯法和非参数法(近邻学习或基于事例的学习),对应的知识表示则为判别函数和原型事例。机器学习方法包括决策树法和规则归纳法,前者对应的表示为决策树或判别树,后者则一般为产生式规则。神经网络方法主要是BP算法,它的模型表示是前向反馈神经网络模型(由代表神经元的节点和代表联接权值的边组成的一种体系结构),BP算法本质上是一种非线性判别函数。另外,最近又兴起了一种新的方法:粗糙集(rough set),其知识表示是产生式规则。
不同的分类器有不同的特点。有三种分类器评价或比较尺度:1 预测准确度;2 计算复杂度;3 模型描述的简洁度。预测准确度是用得最多的一种比较尺度,特别是对于预测型分类任务,目前公认的方法是10番分层交叉验证法。计算复杂度依赖于具体的实现细节和硬件环境,在数据挖掘中,由于操作对象是巨量的数据库,因此空间和时间的复杂度问题将是非常重要的一个环节。对于描述型的分类任务,模型描述越简洁越受欢迎;例如,采用规则表示的分类器构造法就更有用,而神经网络方法产生的结果就难以理解。
另外要注意的是,分类的效果一般和数据的特点有关,有的数据噪声大,有的有缺值, 有的分布稀疏,有的字段或属性间相关性强,有的属性是离散的而有的是连续值或混合式的。目前普遍认为不存在某种方法能适合于各种特点的数据。
5.3、聚类
聚类是把一组个体按照相似性归成若干类别,即”物以类聚”。它的目的是使得属于同一类别的个体之间的距离尽可能的小,而不同类别上的个体间的距离尽可能的大。聚类方法包括统计方法、机器学习方法、神经网络方法和面向数据库的方法。
在统计方法中,聚类称聚类分析,它是多元数据分析的三大方法之一(其它两种是回归分析和判别分析)。它主要研究基于几何距离的聚类,如欧式距离、明考斯基距离等。传统的统计聚类分析方法包括系统聚类法、分解法、加入法、动态聚类法、有序样品聚类、有重叠聚类和模糊聚类等。这种聚类方法是一种基于全局比较的聚类,它需要考察所有的个体才能决定类的划分;因此它要求所有的数据必须预先给定,而不能动态增加新的数据对象。聚类分析方法不具有线性的计算复杂度,难以适用于数据库非常大的情况。
在机器学习中聚类称作无监督或无教师归纳;因为和分类学习相比,分类学习的例子或数据对象有类别标记,而要聚类的例子则没有标记,需要由聚类学习算法来自动确定。很多人工智能文献中,聚类也称概念聚类;因为这里的距离不再是统计方法中的几何距离 ,而是根据概念的描述来确定的。当聚类对象可以动态增加时,概念聚类则称是概念形成。
在神经网络中,有一类无监督学习方法:自组织神经网络方法;如Kohonen自组织特征映射网络、竞争学习网络等等。在数据挖掘领域里,见报道的神经网络聚类方法主要是自组织特征映射方法,IBM在其发布的数据挖掘白皮书中就特别提到了使用此方法进行数据库聚类分割。
5.4、关联规则发现
关联规则是形式如下的一种规则,”在购买面包和黄油的顾客中,有90%的人同时也买了牛奶”(面包+黄油 ( 牛奶 )。用于关联规则发现的主要对象是事务型数据库,其中针对的应用则是售货数据,也称货篮数据。一个事务一般由如下几个部分组成:事务处理时间 ,一组顾客购买的物品,有时也有顾客标识号(如信用卡号)。
由于条形码技术的发展,零售部门可以利用前端收款机收集存储大量的售货数据。因此,如果对这些历史事务数据进行分析,则可对顾客的购买行为提供极有价值的信息。例如,可以帮助如何摆放货架上的商品(如把顾客经常同时买的商品放在一起),帮助如何规划市场(怎样相互搭配进货)。由此可见,从事务数据中发现关联规则,对于改进零售业等商业活动的决策非常重要。
如果不考虑关联规则的支持度和可信度,那么在事务数据库中存在无穷多的关联规则。事实上,人们一般只对满足一定的支持度和可信度的关联规则感兴趣。在文献中,一般称满足一定要求的(如较大的支持度和可信度)的规则为强规则。因此,为了发现出有意义的关联规则,需要给定两个阈值:最小支持度和最小可信度。前者即用户规定的关联规则必须满足的最小支持度,它表示了一组物品集在统计意义上的需满足的最低程度;后者即用户规定的关联规则必须满足的最小可信度,它反应了关联规则的最低可靠度。
在实际情况下,一种更有用的关联规则是泛化关联规则。因为物品概念间存在一种层次关系,如夹克衫、滑雪衫属于外套类,外套、衬衣又属于衣服类。有了层次关系后,可以帮助发现一些更多的有意义的规则。例如,”买外套,买鞋子”(此处,外套和鞋子是较高层次上的物品或概念,因而该规则是一种泛化的关联规则)。由于商店或超市中有成千上万种物品,平均来讲,每种物品(如滑雪衫)的支持度很低,因此有时难以发现有用规则;但如果考虑到较高层次的物品(如外套),则其支持度就较高,从而可能发现有用的规则。另外,关联规则发现的思路还可以用于序列模式发现。用户在购买物品时,除了具有上述关联规律,还有时间上或序列上的规律,因为,很多时候顾客会这次买这些东西,下次买同上次有关的一些东西,接着又买有关的某些东西。

Ⅳ 数据库都有哪些

一、数据库种类有哪些
早期较为时兴的数据库种类有三种,分别是层次式数据库、网络式数据库和关系型数据库。而在如今的互联网中,最常见的数据库种类主要有2种,即关系型数据库和非关系型数据库。

二、层次数据库介绍
层次数据库是最开始研制的数据库系统软件,它把数据根据层次构造(树结构)的方法呈现。层次数据库以前是非常热门的数据库,但伴随着关系数据库的逐渐流行,如今早已非常少应用了。

较为具备象征性的层次数据库是IMS(Information Management System)数据库,由IBM企业研发。

三、关系型数据库详细介绍
网络数据库和层次数据库在数据独立性和抽象性级别上有所欠缺,用户开展存储时,需要声明数据的存储结构和相对路径。而关系数据库就可以较切实解决这种问题。

和Excel工作簿一样,关系型数据库也选用由列和行构成的二维表来管理数据,简单易懂。另外,它还利用SQL(Structured Query Language,结构化查询语言)对数据开展实际操作。

四、非关系型数据库详细介绍
伴随着互联网技术Web2.0的兴起,传统关系型数据库在应对大数据量,比如大规模和高并发的微博、微信或者SNS类型的web2.0动态网页时,已经有些力不从心,曝露了许多难以克服的难题。因此出现了针对大规模数据量场景,以性能卓越和应用便捷为目的的的数据库产品——NOSQL数据库。

Ⅳ 1. SQL Server 2000为用户提供模板和原型的数据库是( )。 A. master B. model C. msdb D. tempdb

BAADDBDCAB
1MB
master
ndf
日志
sp_renamedb

Ⅵ 数据库都有哪些

数据库是一组信息的集合,以便可以方便地访问、管理和更新,常用数据库有:1、关系型数据库;2、分布式数据库;3、云数据库;4、NoSQL数据库;5、面向对象的数据库;6、图形数据库。

计算机数据库通常包含数据记录或文件的聚合,例如销售事务、产品目录和库存以及客户配置文件。

通常,数据库管理器为用户提供了控制读写访问、指定报表生成和分析使用情况的能力。有些数据库提供ACID(原子性、一致性、隔离性和持久性)遵从性,以确保数据的一致性和事务的完整性。

数据库普遍存在于大型主机系统中,但也存在于较小的分布式工作站和中端系统中,如IBM的as /400和个人计算机。

数据库的演变

数据库从1960年代开始发展,从层次数据库和网络数据库开始,到1980年代的面向对象数据库,再到今天的SQL和NoSQL数据库和云数据库。

一种观点认为,数据库可以按照内容类型分类:书目、全文、数字和图像。在计算中,数据库有时根据其组织方法进行分类。有许多不同类型的数据库,从最流行的方法关系数据库到分布式数据库、云数据库或NoSQL数据库。

常用数据库:

1、关系型数据库

关系型数据库是由IBM的E.F. Codd于1970年发明的,它是一个表格数据库,其中定义了数据,因此可以以多种不同的方式对其进行重组和访问。

关系数据库由一组表组成,其中的数据属于预定义的类别。每个表在一个列中至少有一个数据类别,并且每一行对于列中定义的类别都有一个特定的数据实例。

结构化查询语言(SQL)是关系数据库的标准用户和应用程序接口。关系数据库易于扩展,并且可以在原始数据库创建之后添加新的数据类别,而不需要修改所有现有应用程序。

2、分布式数据库

分布式数据库是一种数据库,其中部分数据库存储在多个物理位置,处理在网络中的不同点之间分散或复制。

分布式数据库可以是同构的,也可以是异构的。同构分布式数据库系统中的所有物理位置都具有相同的底层硬件,并运行相同的操作系统和数据库应用程序。异构分布式数据库中的硬件、操作系统或数据库应用程序在每个位置上可能是不同的。

3、云数据库

云数据库是针对虚拟化环境(混合云、公共云或私有云)优化或构建的数据库。云数据库提供了一些好处,比如可以按每次使用支付存储容量和带宽的费用,还可以根据需要提供可伸缩性和高可用性。

云数据库还为企业提供了在软件即服务部署中支持业务应用程序的机会。

4、NoSQL数据库

NoSQL数据库对于大型分布式数据集非常有用。

NoSQL数据库对于关系数据库无法解决的大数据性能问题非常有效。当组织必须分析大量非结构化数据或存储在云中多个虚拟服务器上的数据时,它们是最有效的。

5、面向对象的数据库

使用面向对象编程语言创建的项通常存储在关系数据库中,但是面向对象数据库非常适合于这些项。

面向对象的数据库是围绕对象(而不是操作)和数据(而不是逻辑)组织的。例如,关系数据库中的多媒体记录可以是可定义的数据对象,而不是字母数字值。

6、图形数据库

面向图形的数据库是一种NoSQL数据库,它使用图形理论存储、映射和查询关系。图数据库基本上是节点和边的集合,其中每个节点表示一个实体,每个边表示节点之间的连接。

图形数据库在分析互连方面越来越受欢迎。例如,公司可以使用图形数据库从社交媒体中挖掘关于客户的数据。

访问数据库:DBMS和RDBMS

数据库管理系统(DBMS)是一种允许您定义、操作、检索和管理存储在数据库中的数据的软件。

关系数据库管理系统(RDBMS)是上世纪70年代开发的一种基于关系模型的数据库管理软件,目前仍然是最流行的数据库管理方法。

Microsoft SQL Server、Oracle数据库、IBM DB2和MySQL是企业用户最常用的RDBMS产品。DBMS技术始于20世纪60年代,支持分层数据库,包括IBM的信息管理系统和CA的集成数据库管理系统。一个关系数据库管理系统(RDBMS)是一种数据库管理软件是在20世纪70年代开发的,基于关系模式,仍然是管理数据库的最普遍的方式。

希望能帮助你还请及时采纳谢谢

热点内容
安卓系统怎么改序列号 发布:2024-05-18 23:28:16 浏览:781
c语言中实数 发布:2024-05-18 23:21:03 浏览:894
服务器搭建题目 发布:2024-05-18 23:01:29 浏览:27
下载武装突袭后怎么进服务器 发布:2024-05-18 22:56:17 浏览:825
c语言字符串大写变小写 发布:2024-05-18 22:56:16 浏览:438
重启删除的文件夹 发布:2024-05-18 22:34:11 浏览:638
视频软件源码 发布:2024-05-18 22:22:24 浏览:429
安卓换苹果sim卡去哪里换 发布:2024-05-18 22:21:29 浏览:637
顺丰薪资默认密码是多少 发布:2024-05-18 22:15:04 浏览:791
浏览器打不开服务器通信怎么办 发布:2024-05-18 21:32:22 浏览:961