关闭

NoSQL数据库入门概述

标签: NoSql数据库入门memcachedRedis
2651人阅读 评论(0) 收藏 举报
分类:

关系型数据库与NoSql数据库

什么是NoSQL

Not Only SQL,其含义是:适合关系型数据库的时候就是用关系型数据库,不适用的时候也没必要非得使用关系型数据库不可,可以考虑使用更加合适的数据存储

弥补关系型数据库的不足,各种各样的NoSQL数据库运应而生。

数据库的分类

阶层型数据库

早期的数据库称为阶层型数据库,数据的关系都是以简单的树形结构来定义的。程序也通过树形结构对数据进行访问。这种结构,父记录(上层的记录)同时拥有多个子记录(下层记录),子记录只有唯一的父记录。正因为如此,这种非常简单的构造在碰到复杂数据的时候往往会造成数据的重复(同一数据在数据库内重复出现),出现数据冗余的问题。


阶层型数据库把数据通过阶层结构的方式表现出来,虽然这样的结构有利于提高查询效率,但与此相对应的是,不理解数据结构就无法进行高效的查询。当然,在阶层结构发生变更的时候,程序也需要进行相应的变更

网络型数据库
前所述,阶层型数据库会带来数据重复的问题。为了解决这个问题,就出现了网络型数据库。它拥有同阶层型数据库相近的数据结构,同时各种数据又如同网状交织在一起,因此而得名。

阶层型数据库只能通过父子关系来表现数据之间的关系。针对这一不足,网络型数据库可以使子记录同时拥有多个父记录,从而解决了数据冗余的问题。



但是,在网络型数据库中,数据间比较复杂的网络关系使得数据结构的更新变得比较困难。另外,与阶层型数据库一样,网络型数据库对数据结构有很强的依赖性,不理解数据结构就无法进行相应的数据访问。
关系型数据库
最后要向大家介绍的是以科德提出的关系数据模型为基础的关系型数据库。关系型数据库把所有的数据都通过行和列的二元表现形式表示出来,给人更容易理解的直观感受。网络型数据库存在着数据结构变更困难的问题,而关系型数据库可以使多条数据根据值来进行关联,这样就使数据可以独立存在,使得数据结构的变更变得简单易行
对于阶层型数据库和网络型数据库,如果不理解相应的数据结构,就无法对数据进行读取,它们对数据结构的依赖性很强。因此,它们往往需要专业的工程师使用特定的计算机程序进行操作处理。相反,关系型数据库将作为操作对象的数据和操作方法(数据之间的关联)分离开来,消除了对数据结构的依赖性,让数据和程序的分离成为可能。这使得数据库可以广泛应用于各个不同领域,进一步扩大了数据库的应用范围。

关系型数据库的优势

通用性及高性能
"关系型数据库的性能绝对不低,它具有非常好的通用性和非常高的性能"。毫无疑问,对于绝大多数的应用来说它都是最有效的解决方案。
突出的优势
关系型数据库作为应用广泛的通用型数据库,它的突出优势主要有以下几点:
1、保持数据的一致性(事务处理)
2、由于以标准化为前提,数据更新的开销很小(相同的字段基本上都只有一处)
3、可以进行JOIN等复杂查询
4、存在很多实际成果和专业技术信息(成熟的技术)。


这其中,能够保持数据的一致性是关系型数据库的最大优势。在需要严格保证数据一致性和处理完整性的情况下,用关系型数据库是肯定没有错的。但是有些情况不需要JOIN,对上述关系型数据库的优点也没有什么特别需要,这时似乎也就没有必要拘泥于关系型数据库了。

关系型数据库的不足

不擅长的处理

就像之前提到的那样,关系型数据库的性能非常高。但是它毕竟是一个通用型的数据库,并不能完全适应所有的用途。具体来说它并不擅长以下处理:
1、大量数据的写入处理
2、为有数据更新的表做索引或表结构(schema)变更
3、字段不固定的应用
4、对简单查询需要快速返回结果的处理

大量数据的写入处理

在数据读入方面,由复制产生的主从模式(数据的写入由主数据库负责,数据的读取由从数据库负责),可以比较简单地通过增加从数据库来实现规模化。但是,在数据的写入方面却完全没有简单的方法来解决规模化问题。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来实现读写分离,以提高读写性能和读库的可扩展性。

例如,要想将数据的写入规模化,可以考虑把主数据库从一台增加到两台,作为互相关联复制的二元主数据库来使用。确实这样似乎可以把每台主数据库的负荷减少一半,但是更新处理会发生冲突(同样的数据在两台服务器同时更新成其他值),可能会造成数据的不一致。为了避免这样的问题,就需要把对每个表的请求分别分配给合适的主数据库来处理,这就不那么简单了。

下图为两台主机问题:


另外,也可以考虑把数据库分割开来,分别放在不同的数据库服务器上,比如将这个表放在这个数据库服务器上,那个表放在那个数据库服务器上。数据库分割可以减少每台数据库服务器上的数据量,以便减少硬盘I/O(输入/输出)处理,实现内存上的高速处理,效果非常显著。但是,由于分别存储在不同服务器上的表之间无法进行JOIN处理,数据库分割的时候就需要预先考虑这些问题。数据库分割之后,如果一定要进行JOIN处理,就必须要在程序中进行关联,这是非常困难的。

下图为二元主数据库问题的解决办法:数据库分割


数据库分割:不能进行JOIN处理


为有数据更新的表做索引或表结构(schema)变更

在使用关系型数据库时,为了加快查询速度需要创建索引,为了增加必要的字段就一定需要改变表结构。
为了进行这些处理,需要对表进行共享锁定,这期间数据变更(更新、插入、删除等)是无法进行的。如果需要进行一些耗时操作(例如为数据量比较大的表创建索引或者是变更其表结构),就需要特别注意:长时间内数据可能无法进行更新。下表所示为共享锁和排他锁。


字段不固定的应用

如果字段不固定,利用关系型数据库也是比较困难的。有人会说"需要的时候,加个字段就可以了",这样的方法也不是不可以,但在实际运用中每次都进行反复的表结构变更是非常痛苦的。你也可以预先设定大量的预备字段,但这样的话,时间一长很容易弄不清楚字段和数据的对应状态(即哪个字段保存哪些数据),所以并不推荐使用。下图所示为使用预备字段的情况:


对简单查询需要快速返回结果的处理

最后还有一点,这点似乎称不上是缺点,但不管怎样,关系型数据库并不擅长对简单的查询快速返回结果。这里所说的"简单"指的是没有复杂的查询条件,而不是用JOIN的意思。因为关系型数据库是使用专门的SQL语言进行数据读取的,它需要对SQL语言进行解析,同时还有对表的锁定和解锁这样的额外开销。这里并不是说关系型数据库的速度太慢,而只是想告诉大家若希望对简单查询进行高速处理,则没有必要非用关系型数据库不可
在这种情况下,我想推荐大家使用NoSQL数据库。但是像MySQL提供了利用HandlerSocket这样的变通方法,也是可行的。虽然使用的是关系型数据库MySQL,但并没有利用SQL而是直接进行数据访问。这样的方法是非常快速的。

下图所示为HandlerSocket的概要:


NoSQL数据库

上节介绍了关系型数据库的不足之处。为了弥补这些不足(特别是最近几年),NoSQL数据库出现了。关系型数据库应用广泛,能进行事务处理和JOIN等复杂处理。相对地,NoSQL数据库只应用在特定领域,基本上不进行复杂的处理,但它恰恰弥补了之前所列举的关系型数据库的不足之处。

易于数据的分散

如前所述,关系型数据库并不擅长大量数据的写入处理。原本关系型数据库就是以JOIN为前提的,就是说,各个数据之间存在关联是关系型数据库得名的主要原因。为了进行JOIN处理,关系型数据库不得不把数据存储在同一个服务器内(集中),这不利于数据的分散。相反,NoSQL数据库原本就不支持JOIN处理,各个数据都是独立设计的,很容易把数据分散到多个服务器上。由于数据被分散到了多个服务器上,减少了每个服务器上的数据量,即使要进行大量数据的写入操作,处理起来也更加容易。同理,数据的读入操作当然也同样容易。

提升性能和增大规模

下面说一点题外话,如果想要使服务器能够轻松地处理更大量的数据,那么只有两个选择:一是提升性能,二是增大规模。下面我们来整理一下这两者的不同。
首先,提升性能指的就是通过提升现行服务器自身的性能来提高处理能力。这是非常简单的方法,程序方面也不需要进行变更,但需要一些费用。若要购买性能翻倍的服务器,需要花费的资金往往不只是原来的2倍,可能需要多达5~10倍。这种方法虽然简单,但是成本较高。下图所示为提升性能的费用与性能曲线:


另一方面,增大规模指的是使用多台廉价的服务器来提高处理能力。它需要对程序进行变更,但由于使用廉价的服务器,可以控制成本。另外,以后只要依葫芦画瓢增加廉价服务器的数量就可以了。下图所示为提升性能和增大规模:


不对大量数据进行处理的话就没有使用的必要吗?

NoSQL数据库基本上来说为了"使大量数据的写入处理更加容易(让增加服务器数量更容易)"而设计的。但如果不是对大量数据进行操作的话,NoSQL数据库的应用就没有意义吗?
答案是否定的。的确,它在处理大量数据方面很有优势。但实际上NoSQL数据库还有各种各样的特点,如果能够恰当地利用这些特点,它就会非常有用。具体的例子将会在第2章和第3章进行介绍,这些用途将会让你感受到利用NoSQL的好处。
1、希望顺畅地对数据进行缓存(Cache)处理
2、希望对数组类型的数据进行高速处理
3、希望进行全部保存

多样的NoSQL数据库

NoSQL数据库存在着"键值存储"、"文档型数据库"、"列存储数据库"等各种各样的种类,每种数据库又包含各自的特点。

NoSQL数据库是什么

NoSQL说起来简单,但实际上到底有多少种呢?我在提笔的时候,到NoSQL的官方网站上确认了一下,竟然已经有122种了。另外官方网站上也介绍了本书没有涉及到的图形数据库和对象数据库等各个类别。不知不觉间,原来已经出现了这么多的NoSQL数据库啊。 本节将为大家介绍具有代表性的NoSQL数据库。


key-value存储

这是最常见的NoSQL数据库,它的数据是以key-value的形式存储的。虽然它的处理速度非常快,但是基本上只能通过key的完全一致查询获取数据。根据数据的保存方式可以分为临时性、永久性和两者兼具三种。

临时性

memcached属于这种类型。所谓临时性就是 “数据有可能丢失”的意思。memcached把所有数据都保存在内存中,这样保存和读取的速度非常快,但是当memcached停止的时候,数据就不存在了。由于数据保存在内存中,所以无法操作超出内存容量的数据(旧数据会丢失)。
1、在内存中保存数据
2、可以进行非常快速的保存和读取处理
3、 数据有可能丢失

永久性

Tokyo Tyrant、Flare、ROMA等属于这种类型。和临时性相反,所谓永久性就是“数据不会丢失”的意思。这里的key-value存储不像memcached那样在内存中保存数据,而是把数据保存在硬盘上。与memcached在内存中处理数据比起来,由于必然要发生对硬盘的IO操作,所以性能上还是有差距的。但数据不会丢失是它最大的优势。
1、在硬盘上保存数据
2、可以进行非常快速的保存和读取处理(但无法与memcached相比)
3、数据不会丢失

两者兼具

Redis属于这种类型。Redis有些特殊,临时性和永久性兼具,且集合了临时性key-value存储和永久性key-value存储的优点。Redis首先把数据保存到内存中,在满足特定条件(默认是15分钟一次以上,5分钟内10个以上,1分钟内10000个以上的key发生变更)的时候将数据写入到硬盘中。这样既确保了内存中数据的处理速度,又可以通过写入硬盘来保证数据的永久性。这种类型的数据库特别适合于处理数组类型的数据。
1、同时在内存和硬盘上保存数据
2、可以进行非常快速的保存和读取处理
3、保存在硬盘上的数据不会消失(可以恢复)
4、适合于处理数组类型的数据

面向文档的数据库

MongoDB、CouchDB属于这种类型。它们属于NoSQL数据库,但与key-value存储相异。

不定义表结构

面向文档的数据库具有以下特征:即使不定义表结构,也可以像定义了表结构一样使用。关系型数据库在变更表结构时比较费事,而且为了保持一致性还需修改程序。然而NoSQL数据库则可省去这些麻烦(通常程序都是正确的),确实是方便快捷。

可以使用复杂的查询条件

跟key-value存储不同的是,面向文档的数据库可以通过复杂的查询条件来获取数据。虽然不具备事务处理和JOIN这些关系型数据库所具有的处理能力,但除此以外的其他处理基本上都能实现。这是非常容易使用的NoSQL数据库。
1、不需要定义表结构
2、可以利用复杂的查询条件

面向列的数据库

Cassandra、Hbase、HyperTable属于这种类型。由于近年来数据量出现爆发性增长,这种类型的NoSQL数据库尤其引人注目。

面向行的数据库和面向列的数据库

普通的关系型数据库都是以行为单位来存储数据的,擅长进行以行为单位的读入处理,比如特定条件数据的获取。因此,关系型数据库也被称为面向行的数据库。相反,面向列的数据库是以列为单位来存储数据的,擅长以列为单位读入数据。


高扩展性

面向列的数据库具有高扩展性,即使数据增加也不会降低相应的处理速度(特别是写入速度),所以它主要应用于需要处理大量数据的情况。另外,利用面向列的数据库的优势,把它作为批处理程序的存储器来对大量数据进行更新也是非常有用的。但由于面向列的数据库跟现行数据库存储的思维方式有很大不同,应用起来十分困难。
1、高扩展性(特别是写入处理)
2、应用十分困难

最近,像Twitter和Facebook这样需要对大量数据进行更新和查询的网络服务不断增加,面向列的数据库的优势对其中一些服务是非常有用的,但是由于这与本书所要介绍的内容关系不大,就不进行详细介绍了。

如何导入NoSQL数据库

并非对立而是互补的关系

关系型数据库和NoSQL数据库与其说是对立关系(替代关系),倒不如说是互补关系。笔者认为,与目前应用广泛的关系数据库相对应,在有些情况下使用特定的NoSQL数据库,将会使处理更加简单。

这并不是说“只使用NoSQL数据库”或者“只使用关系型数据库”,而是“通常情况下使用关系型数据库,在适合使用NoSQL的时候使用NoSQL数据库”,即让NoSQL数据库对关系型数据库的不足进行弥补。引入NoSQL数据库时的思维方法:


量材适用

当然,如果用错的话,可能会发生使用NoSQL数据库反而比使用关系型数据库效果更差的情况。NoSQL数据库只是对关系型数据库不擅长的某些特定处理进行了优化,做到量材适用是非常重要的。

例如,若想获得“更高的处理速度”和“更恰当的数据存储”,那么NoSQL数据库是最佳的选择。但一定不要在关系型数据库擅长的领域使用NoSQL数据库。

增加了数据存储的方式

原来一提到数据存储,就是关系型数据库,别无选择。现在NoSQL数据库给我们提供了另一种选择(当然要根据二者的优点和不足区别使用)。有些情况下,同样的处理若用NoSQL数据库来实现可以变得“更简单、更高速”。而且,NoSQL数据库的种类有很多,它们都拥有各自不同的优势。

在何种程度上依赖它

NoSQL数据库是一门新兴的技术,大家可能会觉得实际的操作经验还不多,还可能碰到新的程序错误,无法放心使用。

实际上,memcached已经相当的成熟了(错误和故障都已经被发现,且有明确的应对方法)。由于有丰富的事例和技术信息,所以不用担心会遇到上述问题。但是,在其他的NoSQL数据库的应用过程中遇到问题的可能性还是存在的。特别是实际应用的时候可以参考的经验、信息太少了。虽然NoSQL数据库能带来很多便利,但是在应用的时候也要考虑这些风险。

反过来说,如果不希望遇到此类问题,还是继续使用关系型数据库吧。它积累了很多成熟经验,更让人放心。

NoSQL数据库的种类和特征

memcached(临时性键值存储)

什么是memcached

memcached是由Danga Interactive公司开发的开源软件,属于临时性键值存储的NoSQL数据库。

为什么使用memcached

高速响应

大多数的Web应用程序通过关系型数据库来保存数据,并从中读取必要的数据显示在用户端浏览器上。当数据量较少时,应用程序可以很快读取结果并显示出来。但当数据量急剧增加,或者需要返回比较复杂的数据合计时,响应时间就会变长,用户也只能被迫等待结果的返回,这样将会降低用户体验。

作为高速缓存使用

那么怎么才能获得高速的响应呢?当然如果是简单处理的话,利用关系型数据库的索引也能获得高速响应。虽然memcached会更加快一些,但如果合理的使用索引,关系型数据库就足够快了。

但是,如果要对多个表的数据进行计算,情况会怎样呢?若使用关系型数据库,我们需要从每个表中取出数据然后进行最后的组合处理,或者每次都要使用JOIN等处理。虽然我们可以通过事前用批处理制作数据来解决这个问题,但是这样又会增加需要管理的表,花费我们更多的精力。

“由于准备数据本身需要关系型数据库花费几十秒到几分钟的时间才能计算出来,因此实时计算就会显得比较慢。”这个时候,memcached就可以大显身手了。因为memcached可以把从关系数据库中读取的数据保存到缓存中,所以即使是需要处理大量数据或者是访问非常集中的情况下,它也能非常快速地返回响应数据。这是因为memcached对于(第二次以后的)相同处理,只要它发现有数据保存在缓存里,就不用通过关系型数据库而直接进行处理。memcached存储的数据有可能丢失,但如果这些数据可以马上重新读取出来的话,那么即使因为memcached停止而导致数据丢失,也不会有什么问题。

特征

memcached是通过大家都很熟悉的散列表(关联数组)来存储各种格式数据的键值存储,所有的数据都被保存在内存中。

memcached利用简单的文本协议来进行数据通信,数据操作也只是类似于保存与键值相对应的值这样的简单处理。因此,通过telnet连接memcached,就可以进行数据的保存和读取。但是,由于它利用的是文本协议,所有无法对构造体类型的数据进行操作,而只能对字符串类型的值进行操作。

当然,通过利用为各种语言准备的程序库来使用memcached的时候,值并不仅仅是字符串和数值,数组和散列表这样的构造体一样可以进行保存和读取处理。但是,如前所述,因为使用了文本协议来进行通信,要完成这样的处理,就必须要把它们转换成序列化的字节数组,通常情况下这些处理是在程序库内部进行的,因此不需要特别在意,但是需要在保存数据的时候进行序列化处理,读取数据的时候进行反序列化处理。

序列化对开发语言的依赖

因为序列化依赖于开发语言,所以某种开发语言环境下进行的序列化结果是无法在其他开发语言环境下使用的。如果要进行这样的处理,就需要通过JSON这样不存在语义依赖关系的格式化方法来进行明确的序列化和反序列化处理。


优势

到memcached的优势,要数其极其快速的处理速度。由于数据全部存储在内存中,没有磁盘的IO处理发生,所以它能以比关系数据库高很多的速度进行处理(因为内存中对数据的访问速度是硬盘中的10~100万倍)。

另外一个容易被忽略的优势是她的简单易用性。由于它是通过键值这种散列表形式来操作数据,所以只要用过散列表的人就能很容易的使用它。由于memcached停止的时候所有的数据都会丢失,所以不论遇到什么奇怪的问题,只要重新启动memcached就可以恢复到初始状态。

另外,由于现在很多的Web服务器都在应用memcached,不但拥有了成熟的技术,而且有很多成功经验被公开,这样我们在心里和技术上遇到的困难就会小得多。

随着数据量的增加,当memcached的内存无法保存所有数据的时候,可能就需要多台服务器来进行memcached,实际上通过多台服务器来运行memcached也是非常简单的。在使用多台服务器来运行memcached的时候,会使用一致性散列算法来分散数据。这个算法已经实际应用到memcached的客户端程序库中了。

一致性散列(Consistent Hashing)

如果只是使用多台服务器,就可以利用键的散列值除以服务器台数,通过余数简单地决定哪台服务器处理哪条数据。

但是,这种方法在服务器数量发生变化时就会遇到问题,这时候就需要用到一致性散列这样的分散算法。

这个算法首先对“各个服务器对应的散列值”进行计算,把它们分配到一个圆周上。整个圆周代表键的取值范围,每个服务器承担一个特定取值范围内的键。

四台服务器的一致性散列分配方式:


新增一台5号服务器时的一致性散列分配方式:



新增5号服务器后,1、2、3号服务器负责处理的数据没有任何影响,也就是说,对于1、2、3号服务器,服务器数量变化后负责处理的数据与服务器数量变化前负责处理的数据一致,这也是一致性散列中一致性的含义。

但是,原本由4号服务器负责处理的数据,现在由4号服务器和5号服务器共同处理。服务器数量变化后分配给4号服务器处理的数据,之前本来就是由4号服务器处理,只是部分数据被划分给5号服务器负责处理,这样只有一部分数据会受到影响。所谓的一致性散列,并非变化前后服务器的分配保持完全一致。而是将服务器增减带来的缓存错误的影响减小到非常低的水平。

不足

memcached有什么不足之处呢?其实就是大家非常在意的数据临时性(数据有可能丢失)问题。

由于memcached把数据都保存在内存中,当memcached由于故障等原因停止的时候,所有的数据都会丢失。也正因为如此,用它来处理那些重要数据是非常危险的,绝对不要使用。最好的处理方式还是把原始数据保存在其他地方,而只是用memcached来处理原始数据的复制或者是通过原始数据计算出来的结果。

另外,它还存在只能通过键来读取数据这样的局限。不能支持像LIKE这样的模糊查询。



memcached的应用

例如:分布式Session



Tokyo Tyrant

属于NoSQL分类中的永久性键值存储。Tokyo Tyrant和memcached一样,通过键值这样的散列表结构保存数据。但是,数据的保存地点却不一样,memcached是把数据保存在内存中,而Tokyo Tyrant则是把数据保存在磁盘上。

另外,Tokyo Tyrant还引入了数据库类型的概念。可以根据选择的数据库类型,在缓存数据库、散列数据库、B-tree数据库和表数据库等数据保存方式间进行切换。


优势

由于数据存储在硬盘上,Tokyo Tyrant的最大优势就是在它停止的时候数据也不会丢失。当然,关系数据库也不存在数据丢失的问题,所有从某种程度上说,也算不上什么优势。Tokyo Tyrant的另一个优势是:它在保存和读取数据的时候,与磁盘的IO处理无关,可以实现对数据的高速访问。它可以获得比关系型数据库快得多的处理速度。用户在获得高速反应响应的同时,又不必担心数据丢失的烦恼,真的是非常方便。

关于数据的读取方式,memcached只能通过与键完全一致的条件进行查询,而Tokyo Tyrant存在不同的数据库类型,可以进行范围查询(B-tree数据库)或者像关系型数据库那样进行复杂条件的查询(表格数据库)。同一个产品,能够根据用途的不同而在数据库类型间进行切换实在是方便极了。

不足

虽然既不会发生数据丢失,访问的速度也非常快。但是,使用起来难度大。

Redis(临时性/持久性键值存储)

什么事Redis

它兼具临时性和永久性,所以它是NoSQL数据库中介于memcached和Tokyo Tyrant之间的键值存储。

为什么要使用Redis

处理数组形式的数据

确实,如果要处理字符串数据和标准的散列数据,memcached和Tokyo Tyrant这样的键值存储可能已经足够了。但是,根据用途的不同,也不乏对快速处理数值和数组类型数据的要求

Redis是键值存储的一种,但是它对链表集合等数组类型的数据进行了优化处理,可以对数组类型的数据进行高速的插入和读取处理,另外,Redis包含很多可以把这些处理原子化的命令,所以可以非常容易的保证数据的一致性

特征

由于Redis通常是把数据保存在内存中,所以是处理速度非常快的键值存储。虽然已经存在了同样是内存中保存数据的键值存储memcached,但是这两者的用途却大不相同。memcached主要用作关系型数据库的缓存,与关系型数据结合使用,对简单的操作进行优化处理。与之相对,Redis本身就是作为数据存储而设计出来的,它的操作指令非常多,其中很多很多都支持原子操作。

优势

Redis可以处理字符串、链表、集合、有序集合、散列表等各种类型的数据,但需要注意的是所有数据都会被当作字符串处理(数值的保存也是一样)。

由于数据通常保存在内存中,所以处理速度非常快。Redis会定期对数据进行快照处理,除了一部分当前的更新之外,数据都不会丢失。虽然进行数据快照的时候会增加负荷(需要对所有的数据进行IO处理),但是由于数据快照的IO处理通常都是连续IO,所以非常高效。

不足

Redis最大的问题就是这项新技术的使用实例相对较少。

内容源自:

《NoSql数据库入门》

1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:107483次
    • 积分:1696
    • 等级:
    • 排名:千里之外
    • 原创:62篇
    • 转载:5篇
    • 译文:0篇
    • 评论:27条