【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

虚拟座谈会:NoSQL 数据库模式及其广泛持久性

  • 2013-01-10
  • 本文字数:7937 字

    阅读完需:约 26 分钟

NoSQL 数据库领域中,存在不同的数据库,支持了各种各样的数据存储模式。InfoQ 与四位专家就以下几点进行了讨论:当前采用 NoSQL 的状况,不同 NoSQL 数据库所支持的架构模式,及采用 NoSQL 数据库的安全因素。下面介绍以下这几位专家以及他们所讨论的 NoSQL 数据库。

我们对这些专家提出了以下几个问题:

  1. 从企业级使用数据上看,NoSQL 的当前状况怎样?
  2. 能否就对数据持久性,检索及其它数据库相关因素的支持,来讨论你们的数据库产品的主要架构模式?
  3. 你的产品最适合什么样的用例或应用?
  4. 产品使用的局限和约束是什么?
  5. 跨存储持久性概念最近受到越来越多的关注。这一概念让我们可以把数据保留在不同数据库中,包括 NoSQL 和关系数据库。能否讨论下该策略,及它能如何帮助开发者在单一应用上连接不同数据库?
  6. 你如何看待数据网格在广泛持久性领域里的角色?
  7. 该领域的现有安全状况怎样,接下来会怎样?
  8. 针对你的产品的新特征和改进而言,未来的路线图是什么?

InfoQ: 从企业级使用数据上看,NoSQL 的当前状况怎样?

Robin Schumacher:在 DataStax 我们所经历的趋势已经非常清晰了。首先,采纳和使用 Cassandra 的公司正如大家所期望的那样:相对年轻,基于互联网服务,他们喜欢 NoSQL 数据库所提供的服务,包括功能和费用。

但是就在过去 8 个月,一些广为人知的大公司也开始注册我们的产品。不仅如此,他们还将我们的软件用于他们核心业务的重要分支上。

让我吃惊的是所有的这一切发生得这么快。以前我在 MySQL 的时候,企业对开源数据库软件的采用相对来说非常慢,尤其在针对核心系统的时候。但企业对 NoSQL 的拥护却发生得很快,可能是因为对大数据管理的需求及增加的新应用涉及不同于 RDBMS 的数据模型。

Darren Wood:我们在和很多现有及潜在客户合作,他们主要分为两种类型。 第一:对于较新的市场,类似社交网络,游戏,互联网 / 移动广告和其他大规模基于 WEB 的应用,他们从一开始就基于 NoSQL 技术创建系统。在很多情况下,他们结合使用多个 NoSQL 数据存储,从而根据每个数据库的优点采用不同的功能。比如,对于用户 / 设备描述信息,及一些对交易或时间数据的初始捕获,就会大量使用分布式键值存储。在后台,从交易中获取的信息被用于产生某一特定类型查询的多种数据存储。类似 InfiniteGraph 这样的图形数据库就是一个非常好的例子,它们可以用来存储和发现系统实例间的关系,比如:某组用户之间是如何关联的。

第二:在相对比较传统的市场(电信,政府,医疗保健,科学等)里,对 NoSQL 的采用也在稳健增长,以帮助他们挖掘现有数据新的价值。在替换传统 RDBMS 的同时,我们也发现这些市场上的很多项目将数据从现有多平台上提炼出来,然后以更有效的方式存储,以实现基本的“信息提取”或数据分析类型应用。

John Davies:其实我们(投资 / 大额银行业务)使用“NoSQL”已经有十多年了,之前我们并没有为之命名,但是我们已经将数据存储在非关系型存储设备中很多年了。

InfoQ:能否就对数据持久性,检索及其它数据库相关因素的支持,来讨论你们的数据库产品的核心架构模式?

Robin:有的时候你会从网上论坛看到评论说 NoSQL 数据库对数据而言不安全。但是就 Cassandra 来说,我可以告诉你这个断言是完全错误的。

Cassandra 是基于以下理解建立的:硬件故障能且会发生,而这一数据丢失却经常被忽略。为了保证类似故障不威胁公司的数据访问,Cassandra 基于对等设计构架以保证持续可用性,并且不影响性能及操作简易性。 所有进入 Cassadra 的数据总是需要写到提交日志来保证数据持久性,然后再写到一种叫做 memtable 的内存结构中去。最后定期写入叫做“sstable”(排序字符串表)的硬盘。

Cassandra 是为那些采用分治法数据库的用户实例而需要大规模系统架构而设计的。数据会根据某一列族中(类似于 RDBMS 中的表)的关键字自动分布在集群中的所有节点。同时,数据也会根据用户设置的冗余在其它节点上复制。不论该数据库集群是只有一个数据中心,还是有多个数据中心,或是多个安装于本地的数据中心的结合及云,其复制机制都可以非常容易地建立。最主要的是,这些特征可以保证无硬件故障,或者一个或多个节点上的数据丢失不会威胁正常运行,或整个集群上的数据丢失。

由于 Cassandra 上的所有节点都是一样的,所以数据可以读写到任一节点。Cassandra 使用最终一致性模式,其具有可调性,开发人员可以决定每个操作的插入或查询的统一程度。如果按 RDBMS ACID 的样式来看的话,Cassandra 具有 A-I-D,但没有“C”,因为 Cassandra 里并不存在参照完整性或外键。

在 DataStax,我们将 Cassandra 应用于我们的企业产品,如:用于分析的 Hadoop 和用于企业查询的 Solr 以获取同样的数据保护和持续可用性。通过 DataStax,客户可以获得一个数据库集群以支持实时性,分析,查询及上述所描绘的所有功能。

Jared Rosoff:

  • 所有数据都保存为 BSON(二进制 JSON)对象。
  • 持久性通过等待状态的异步框架来实现。客户可以等待数据写入磁盘,复制到从属盘,或什么也不用等待。这允许程序员控制调节持久性。
  • 数据首先使用预写式日志写入硬盘,以保证数据在写后以最快的速度到达硬盘(默认情况下,预写式日志是每 100ms 一次写入硬盘的)。
  • 数据库在文档数据中维护 B-Tree 索引,文档数据是以原子形式进行更新的。
  • 查询是通过查询计划器来决定使用哪个最优索引的,而该索引又基于客户在查询中给出的字段。
  • 在共享部署里,系统将分区文件根据用户选择的共享键分布在各系统中。文件将保存在叫做块的连续范围中,而这些块会被平均地分布在可用服务器上。系统会自动移动这些块以保持系统平衡。当查询到达某一碎片时,查询就会如同在非共享环境下一样执行(比如,查询计划器决定最好的计划和使用正确的索引来获取数据)。
  • 复制通过 oplog 来完成。Oplog 则通过共享的一个主要成员来维护。Oplog 包含那些用于更新该共享数据的操作。次要成员根据 oplog 将更新以同于主要成员的顺序在本地执行。

Darren:InfiniteGraph 是一种分布式图形数据库。和其它 NoSQL 解决方案一样,它存储数据的方式会让某些类型的查询非常有效。InfiniteGraph 将数据间的连接看成“一等公民”,将它们存储类似为“虚拟指针”以优化遍历。与其它保存外键为关联实体的数据库不同(需嵌套或递归的查询语句来执行遍历),InfiniteGraph 的遍历提供的是数量级的提高。

除了物理存储上的不同,InfiniteGraph 特有的分布式数据模式让数据库可以使用高性能计算机集群或云架构来分配插入、更新操作和导航查询负载。

John:我们将大部分 NoSQL“数据库”建立在 API 或某一外观上之后,就可以将具体执行抽象化,并将绝大多数操作置于内存内,因为性能是大家放弃 SQL 和传统 RDBMS 的首要原因。在技术上我们没有顾虑,只是标准常规。我们最大的问题就是报告和分析,而这恰恰是 SQL 所拥有的最现成的工具。

InfoQ:你的产品最适合什么样的用户群或应用?

Robin:对于 Cassandra,其底层数据模型是 Goolge 的 Bigtable 设计,自然它属于列存储。这意味着你将有相对于 RDBMS 表松散很多的表结构,因此同一列族中,一行数据可能拥有 3 列,而另外一行却可能拥有 3000 多列。

这种类型的数据模式能完美适用于那些涉及时序数据的用户实例(比如:机器生成的数据,财务点击数,网页点击量等),以及应用于网上购物车、用户交易、社交媒体、在线游戏和那些写密集的系统的零售数据模型上 在 DataStax,我们支持所有这些用户实例,联同 Hadoop 批量分析及使用 Solrde 企业查询等用户实例。举个例子,SourceNinja,作为帮助其他组织管理开源软件的领先者,它使用 DSE2.0 将多分布式中的一个开源项目关联到另一个。“举个更具体的例子,Linux 就是单一的项目—但它有不同分支和版本,比如:Red Hat,Debian,Amazon 等等,”SourceNinja 的联合创始人 Matt Stump 说道。“正因为所有这些可用版本的存在,DataStax Enterprise 2.0 确实是唯一能将所有项目维持有序一致的合适搜索解决方案。

Jared:MongoDB 是种多用途数据存储软件,被广泛应用于多种应用中。典型的用户实例是内容管理系统,实时分析,机器间通信,电子商务范畴,分布式状态和计数器管理,及感应器数据收集。总的来说,几乎所有你能用关系型数据库的地方都能使用 MongoDB。MongoDB 尤其对那些需要多变量数据的应用特别有效(这也凸显了 MongoDB 丰富的数据模型)。

Darren:InfiniteGraph 主要强调的是使用数据间的关联。InfiniteGraph 让你发现事物 (客户,用户,产品等) 是如何通过使用基于遍历算法的规则,与其它事物关联起来。一旦你开始将数据想象成图形,就会明显地发现遍历的强大。

其中一个被广泛知晓的实例是“两者是如何联系起来的”,这是通过寻找系统里两者数据间所有关联路径实现的。举个例子,在电话网络里两个人间的关系可以通过他们间信息传递的多种可能方式,通过识别他们的中间联系人来揭示。这个强大概念能运用于一系列问题上。

图形在数据世界里无处不在。现实世界里有无数的真实例子显示数据可以通过一系列由边连接的点组成。另一个例子是航空飞行路径,每个机场间的单独部分可用来构成两个城市间不同的路径。如此将其赋之图形可以用于查询出最佳成本路径等。

John:就产品的话我想你应该指的是 C24 的集成对象(C24-iO). 我们的产品是个 Java 绑定代码产生器。我们对消息及它的存储采用模型驱动方式,比如,SWIFT,FpML、Fix、 ISO-20022、SEPA、ISO-8583、DTCC、Minos 等。通过它们产生的结果能很好地集成到 ESB,SOA 以及诸如 Mule、Spring Integration 等集成框架,以及类似 GemFire、GigaSpaces 等内存数据库中。

InfoQ:产品使用的局限和约束是什么?

Robin:Cassandra 主要的局限在于列族间没有连接操作,因此数据是高度非正规化。如果有复杂或嵌套的交易 (例如保留点,回滚等) 存在的话,Cassandras 并不是最好的数据库选择。

Jared:目前为止,MongoDB 不支持复句交易或连接。在很多情况下,这并不是一个问题,因为 MongoDB 有丰富的文档模型和原子更新能力能容易地为对象建模,而避免像关系型数据库那样需要将其归一化。对于那些需要真正的复句交易和连接的实例,RDBMS 或许是正确的选择。另外,MongoDB 设计为操作性数据存储的,而非分析型数据存储。如果你的大部分查询是针对整个数据集的集成,并需快速执行,那么 OLAP 数据库或许是个正确的解决方案。

John:我们还没有原生的 Windows 版本,我们只基于 Java,但这对我们的系统来说,从来就不是问题。

Darren:正如很多其他 NoSQL 数据库一样,InfiniteGraph 专注于某些范围的用例。就我们看来,它将数据以图形形式存储,并涉及多层分离的深层关系遍历的查询。因此,对于传统的针对整个数据集的查询或类似“每一客户平均收益高于 55”的统计分析,它并非那么适用。关系型、文档和列存储的数据库在这里通常更加适用。对于基于批处理或大型数据库,将类似 Hadoop/MapReduce 这样的分布式处理平台与 InfiniteGraph 一起使用会更加适合和有效。

InfoQ:跨存储持久性概念最近受到越来越多的关注。这一概念允许我们将数据保留在不同数据库间,包括 NoSQL 和关系数据库。你能否讨论下该策略,及它能如何帮助开发者在单一应用上连接不同数据库?

Robin:我们的一些客户已经使用 Cassandra 或 DataStaxEnterprice 来完全取代现有 RDBMS,但其他确实采取并行方案。

为了确保并行方案的可行,某些条件是必不可少的。显然,你的应用首先需要正确的组件或驱动来连接到 NoSQL 数据库。然后,你需要想出一种方法能将数据在 RDBMS 和 NoSQL 间导入导出,而这个 NoSQL 最好容易管理。如果没有管理好 NoSQL,这将带来很多麻烦。

针对 DataStax Enterprise,我们支持 sqoop,它能简单有效地将数据在 RDBMS 表和 Cassandra 列族之间转移。如果中间需要将数据转化,那么用户可以使用免费的 Pentaho Kettle 产品将数据分割成任意你想要的形式,从而与 Cassandra 和 Hadoop 交互。

Jared:这确实是一种非常常见的方法。很多应用框架,例如 Spring Data、Rails 和 Django 已经支持在多数据存储运行。虽然这种方式给程序员带来了更多选择,但应该谨慎使用,不应走极端。支持 2 到 3 个数据存储还比较合理,但当你加入第五个或是第十个时,那随之而来的管理将是场噩梦。如果将数据分布在多个数据库中,可想它的管理可能会越来越难。

Darren:在一个系统里结合使用多个数据库类型,允许我们获取它们各自优点及根据它们最实用的目的单独使用。对我们来说,将实体数据保存在现有 RDBMS 中也是非常常见的,因为 SQL 在执行基于特定属性查询时,依然是个非常好的工具。而且,它们通常提供良好的数据索引功能,并将数据归一化以提高数据存储的有效性。 但是像图形数据库那样能进行基于深层连接的查询,SQL 并没有相应概念。通过结合使用 RDBMS 和 InfiniteGraph,你所建立的系统不仅可以使用 SQL 来分辨每个遍历的端点,而且可以探索图形数据库执行导航式查询的强大功能。我们现有很多系统就使用这一方式。

John:这种现象非常常见,就算是在 “传统的”数据库中我们也会为不同的用户设计不同的模式,时而带索引,时而不带,因此两者有何不同?我们将部分数据或信息存储在 NoSQL 的内存存储里,部分在传统的 RDBMS 里交易和存储以便于分析或报告。

InfoQ:你如何看待数据网格在广泛适用性空间里的角色?

Robin:它们都有着自己的适用空间。但大型数据用户实例需求远远超出了它们所能承受的。因此用户最终还是使用类似 Cassandra 或 DataStax 这样的解决方案。很显然我们还不是主流存储,但是由于向外扩展性构架,人们可以获得节点间的智能自动分片,和使用各个节点上的内存来获取快速响应时间,因为大部分数据都保存在内存里。

Jared:这些解决方案已经变得越来越不重要了。数据网格通过将实例保存在内存来减少应用对较慢存储层的读取,从而弥补了硬盘的低性能。但是类似 MongoDB 这样的产品,它使用 RAM 来尽量多地缓存实例,这意味着数据网格作为缓存就变得多余了。

数据网格对于那些你想要将数据保留在内存,并必须在多个应用服务器上共享,而从不写入数据库的情况还是有效的。(但是,很多情况下,将这些数据保存在数据库反而更简单,它能减少运行另外一种数据存储所带来的运行复杂度。)

数据网格能提供一虚拟层来将所有数据存储统一形成一种相似的 API,但很多情况下这并非是种好选择,因为它强制要求开发人员在数据存储里最不常见的功能上下功夫。通常情况下,因为对不同数据有着不同需求,我们会选用一种跨数据库的持久性机制。如果试图将所有这些数据统一到单一 API 中,就违背了我们想要使用不同数据存储的初始意图。

Darren:现在确实有很多人对 IMDG 有兴趣,我也看到它在客户部署那里扮演着各种不同角色。在很多情况下,IMDG 用来“延长”那些建立在传统 RDBMS 上系统的寿命,它缓存了那些不写入数据库的“短暂的”共享数据,以及后台数据库现有的信息。很多 IMDG 组合在使用数据网格的同时纳入了处理引擎和 MapReduce 处理能力。这对实时分析变化的数据非常有意义,我认为未来对这一能力的需求也很大。

与之相反的是,大部分新类型数据库(如 InfiniteGraph)在一方案中使用了嵌有缓存的分布式服务器,并同时结合了数据网格和数据库。

John:水银延迟线、磁芯、硬盘、RAM 和 SS,其实它们都一样,只是有的比其它更适合某些任务而已。投资银行业已在 10 多年前就移植到内存数据网格,因此它的角色是首要的。

InfoQ:该领域的现有安全状况如何,接下来会怎样?

Robin:现今,应该所有的 NoSQL 生产商都面临着同一情况;安全是相对比较薄弱的一块。我们的客户要么使用 Cassandra 自带的安全框架,要么使用操作系统或应用中带有的安全。

但我相信随着政府机构和财务市场对 NoSQL 的快速应用,安全这块将很快会有变化。

Jared:安全这块确实在不断发展。很难就所有数据库来进行总体评价。但就 MongoDB 而言,我们花了大量力气在以下这些方面:

  • 改进数据库内部的验证和用户管理
  • 在客户端和复制接口增加 SSL 加密以加密动态数据。
  • 支持类似 eCryptFS 的加密文件系统来加密静态数据。

Darren:很肯定的是,将安全从数据库中移到更上层以便用户更容易理解是未来的趋势。这在广泛适用性系统上尤其如此,系统中使用着不同安全模式,但它们之间对用户信息复制却只有少量的支持。部分系统在这一层使用主数据管理层和覆盖安全,但也有部分只是简单将安全属性与数据存储在一起,然后在应用里将其与对应的存储在别处的验证信息一起执行。

InfoQ:针对你的产品的新特征和改进而言,未来的计划是什么?

Robin:就 Cassandra 而言,我们会继续与社区合作以让 Cassandra 更容易使用,更快。

对于 DataStax Enterprise,我们现在很好地涵盖了包括实时、分析和企业查询等数据领域,因此我们将寻找其他我们还未直接接触的数据相关领域,因此在稍后的 2012 我们会有更多地展望和期许。

Darren:根据我们现有客户的回馈,我们现在在扩展分布式查询和数据替换功能。我们整个公司现在主要集中这两点,并在接下来几个月会有一些大的通告。

John:我们将继续致力于对合作产品的紧密集成,我们已经连续 7 年不断部署到 Mule、GigaSpaces、GemFire 和 Spring Integration。我们可以通过 Spring Integration 不写一行代码就将复杂信息导入到 GenFire 缓存中去。我们的计划是进一步开发这些功能,同时改进性能和扩展性。

关于专家们:

Robin Schumacher:最近 20 多年来一直从事数据库和大数据。在来 DataStax 之前,他曾在 EnterpriseDB 组建带领过一个基于市场的产品管理团队。更早之前,Robin 有 3 年在 MySQL 带领产品管理团队,但后来该团队被 SUN 收购,然后卖给 Oracle。他也曾在 Embarcadero Technologies 带领过数据库产品管理团队,这曾是 2000 年的头号 IPO。Robin 著有三本关于数据库性能的书,曾参与过 Intelligent Enterprise 及其它杂志的数据库软件审查。他也是各大活动的活跃演讲者。Robin 获有多个大学的本科,硕士及博士学位。

Jared Rosoff是 10gen 的产品市场总监。在加入 10gen 之前,Jared 在 Yottaa 管理产品开发。其间,他通过使用 MongoDB 和 Ruby on Rails 开发了一个实时分析引擎。早前为了开始他的事业,Jared 从 Brown University 退学,合伙组建了 TAZZ Networks,并成为 CTO。

Darren Wood是 InfiniteGraph 的架构师及主要开发员。InfiniteGraph 来自 Objectivity Inc.,是一种分布式的基于图形的数据管理解决方案。在 Darren 的职业生涯里,他大部分时间都在架构和构建那些强调灵活扩展性和数据管理的分布式系统。在 2007 加入 Objectivity 之前,Darren 曾在 Citect Australia 担任高级咨询师及 IONA Technologies 的开发团队负责人。Darren 获有澳大利亚悉尼理工大学的计算机系统工程一级荣誉学位。

John Davies是 Incept5 和 C24 的 CTO 及联合创始人。这两个国际企业致力于企业级咨询和集成。John 是现在 Visa 的 V.me 的最初技术架构师。他曾是 JP Morgan 和 BNP Paribas 的首席架构师,及两家纳斯达克公司的技术总监。John 合著过多本企业级 Java 和架构书籍,并是银行和技术会议上的活跃演讲者。

查看英文原文 Virtual Panel: NoSQL Database Patterns and Polyglot Persistence


感谢侯伯薇对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

公众号推荐:

跳进 AI 的奇妙世界,一起探索未来工作的新风貌!想要深入了解 AI 如何成为产业创新的新引擎?好奇哪些城市正成为 AI 人才的新磁场?《中国生成式 AI 开发者洞察 2024》由 InfoQ 研究中心精心打造,为你深度解锁生成式 AI 领域的最新开发者动态。无论你是资深研发者,还是对生成式 AI 充满好奇的新手,这份报告都是你不可错过的知识宝典。欢迎大家扫码关注「AI前线」公众号,回复「开发者洞察」领取。

2013-01-10 06:233096
用户头像

发布了 39 篇内容, 共 12.7 次阅读, 收获喜欢 2 次。

关注

评论

发布
暂无评论
发现更多内容

看板方法的定义、原则和实践

PingCode

GetX 状态管理从入门到入迷

岛上码农

flutter ios 前端 安卓 6月月更

数据库每日一题---第13天:寻找病患

知心宝贝

数据库 云计算 前端 后端 6月月更

在线JSON转TSV工具

入门小站

工具

读《Software Systems Architecture》(22)—— The Operational Viewpoint

术子米德

架构师成长笔记

读《Software Systems Architecture》(18)—— The Information Viewpoint

术子米德

架构师成长笔记

读《Software Systems Architecture》(25)—— The Security Perspective

术子米德

架构师成长笔记

盘点攻防演练中红队的主要工具(下)

穿过生命散发芬芳

6月月更 攻防演练

js中的变量提升和函数提升

北洋

android 6月月更

读《Software Systems Architecture》(24)—— Introduction to the Perspective Catalog

术子米德

架构师成长笔记

读《Software Systems Architecture》(26)—— The Performance and Scalability Perspective

术子米德

架构师成长笔记

读《Software Systems Architecture》(12)—— Producing Architectural Models

术子米德

架构师成长笔记

读《Software Systems Architecture》(17)—— The Functional Viewpoint

术子米德

架构师成长笔记

读《Software Systems Architecture》(21)—— The Deployment Viewpoint

术子米德

架构师成长笔记

读《Software Systems Architecture》(23)—— Archiving Consistency Across Views

术子米德

架构师成长笔记

数仓开发人员的价值体现

奔向架构师

数据仓库 数据模型 6月月更

三点微服务标准化要素

阿泽🧸

微服务 6月月更

接口测试使用Python装饰器

伤心的辣条

Python 程序人生 软件测试 自动化测试 接口测试

读《Software Systems Architecture》(15)—— Introduction to the Viewpoint Catalog

术子米德

架构师成长笔记

计算机网络之IP协议与以太网

未见花闻

6月月更

在线文本保留中文提取过滤工具

入门小站

工具

读《Software Systems Architecture》(13)—— Creating the Architectural Description

术子米德

架构师成长笔记

读《Software Systems Architecture》(19)—— The Concurrency Viewpoint

术子米德

架构师成长笔记

Java中检查字符串是否是有效日期

okokabcd

Java

uni-app深入学习之模板运用【day4】

恒山其若陋兮

6月月更

GoLang简单易用的json value读取工具!还并发安全

Krysta

Go json 简单清楚 方便

linux之我常用的20条命令(之二)

入门小站

Linux

读《Software Systems Architecture》(11)—— Using Styles and Patterns

术子米德

架构师成长笔记

读《Software Systems Architecture》(14)—— Evaluating the Architecture

术子米德

架构师成长笔记

读《Software Systems Architecture》(16)—— The Context Viewpoint

术子米德

架构师成长笔记

读《Software Systems Architecture》(20)—— The Development Viewpoint

术子米德

架构师成长笔记

虚拟座谈会:NoSQL数据库模式及其广泛持久性_DevOps & 平台工程_Srini Penchikala_InfoQ精选文章