对于需要高度可扩展性的网站(例如Facebook之类的社交网络),设计网站的最佳方法是什么?
我是否应该具有网站查询的Web服务以获取所需的数据?
要么
- 该网站应该直接查询数据库吗?(可以使用内置的语言构造来自动填充表格等)。
我认为Web服务是更好的设计,因为它提供了集中的数据访问,并且诸如缓存之类的东西变得更易于控制,但是其他人怎么看?
对于需要高度可扩展性的网站(例如Facebook之类的社交网络),设计网站的最佳方法是什么?
我是否应该具有网站查询的Web服务以获取所需的数据?
要么
我认为Web服务是更好的设计,因为它提供了集中的数据访问,并且诸如缓存之类的东西变得更易于控制,但是其他人怎么看?
Answers:
哇,这是一个简单的问题,有很多可能的答案。您问题的最明确的部分是询问它是否具有更大的可伸缩性以直接或通过Web服务与数据库交互。答案很简单:直接查询数据库。通过Web服务增加了一大堆延迟,这对于在防火墙后运行的代码(大体而言)完全是不必要的。例如,Web服务需要某些组件来接收请求,反序列化,查询数据库,序列化响应并返回请求。因此,如果您的代码都在防火墙后面运行,请省去麻烦,直接查询数据库。
但是,使网站具有可伸缩性远远超出了您最初提出的问题。因此,如果我在这里切线,请原谅我,但我认为考虑到您特别提到Facebook可能会很有用。
我建议您阅读Brad Fitzpatrick(LiveJournal的创始人,现在是Google)创建的工作和工具。当我与他在“六人公寓”工作时,这里有一些我从他那里学到的东西,以及有关LiveJournal使其可扩展的体系结构的知识。
使用窄数据库表而不是宽数据库表。令人着迷的是学习促使该体系结构发展的因素,该体系结构创建了一个轻松,快速的系统升级。如果使用宽表或表中每个字段或属性都是表中的一列的表,那么当需要升级数据库模式时(例如添加新列),系统将需要在表模式下锁定表更改已实施。大规模运行时,这意味着对数据库架构的简单更改可能会导致大型数据库中断。显然很烂。另一方面,窄表仅将与对象关联的每个单独属性存储为数据库中的一行。因此,当您要将新列添加到数据库时,您要做的就是将记录插入表中,这是非锁定操作。好的,这是一个背景知识,让我们看看该模型如何在像LiveJournal这样的工作系统中实际转换。
假设您要在一个人的博客上加载最近的10个日记条目,并且假设每个日记条目都有10个属性。在经典的宽表布局中,每个属性都将与表上的一列相关。然后,用户将查询该表一次以获取所需的所有数据。该查询将返回10行,每行将具有所需的所有数据(例如SELECT * FROM条目ORDER BY日期LIMIT 10)。在狭窄的表格布局中,情况有所不同。在此示例中,实际上有两个表:第一个表(表A)存储一个简单的条件,用户可以根据该条件进行搜索,例如,条目的ID,作者的ID,条目的日期等。第二个表(表B)然后存储与条目关联的所有属性。第二个表具有三列:entry_id,键和值。对于表A中的每一行,表B中将有10行(每个属性一行)。因此,为了获取并显示最后十个条目,您将需要11个查询。第一个查询为您提供条目ID列表,然后接下来的十个查询将获取与第一个查询中返回的每个条目关联的属性。
“天哪!” 您说:“这在地球上怎么可以扩展?!” 它完全违反直觉吗?在第一种情况下,我们只有一个数据库查询,但是在第二种“更具扩展性”的解决方案中,我们有11个数据库查询。这是没有意义的。该问题的答案完全取决于下一个项目符号。
自由使用内存缓存。如果您不知道,memcache是一个分布式,无状态,低延迟,基于网络的缓存系统。Facebook,Google,Yahoo以及地球上几乎所有流行且可扩展的网站都使用它。它是Brad Fitzpatrick发明的,部分目的是帮助抵消窄表数据库设计中固有的数据库开销。让我们看一下与上面#1中讨论的示例相同的示例,但是这次,我们介绍内存缓存。
让我们从用户首次访问页面而缓存中没有内容开始。首先,查询表A,该表返回要在页面上显示的10个条目的ID。然后,对于每个条目,您都查询数据库以检索与该条目关联的属性,然后使用这些属性构成一个您的代码可以与之交互的对象(例如,一个对象)。然后,您将该对象(或该对象的序列化形式)存储在内存缓存中。
某人第二次加载同一页面时,您将以相同的方式开始:通过在表A中查询要显示的条目ID列表。对于每个条目,您首先要进入内存缓存并说:“缓存中是否有条目#X?” 如果是,则memcache将入口对象返回给您。如果不是,则需要再次查询数据库以获取其属性,构成对象并将其存储在内存缓存中。在大多数情况下,第二次有人访问同一页面时,只有一个数据库查询,然后直接从内存缓存中提取所有其他数据。
实际上,大多数LiveJournal最终发生的事情是,系统的大多数数据(尤其是易失性数据)已缓存在内存缓存中,而支持窄表模式所需的数据库额外查询几乎完全被抵消了。
这种设计使解决与将与所有朋友相关联的帖子列表组装到信息流或“墙”中的问题变得非常容易。
接下来,考虑对数据库进行分区。上面讨论的模型还存在另一个问题,那就是您的窄表往往会很大/很长。这些表的行越多,其他管理任务就越难。为了弥补这一点,通过以某种方式对表进行分区来管理表的大小可能是有意义的,这样一个数据库可以为用户群集提供服务,而另一个数据库可以为另一个用户群集提供服务。这可以分配数据库负载,并保持查询效率。
最后,您需要很棒的索引。查询的速度将在很大程度上取决于数据库表的索引程度。我不会花太多时间来讨论什么是索引,只是要说它就像是一个巨大的卡片目录系统,可以使在大海捞针中的查找效率更高。如果使用mysql,则建议打开慢查询日志以监视需要很长时间才能完成的查询。当查询突然出现在您的雷达上时(例如,因为它很慢),请找出需要添加到表中的索引以加快查询速度。
“感谢您提供所有这些出色的背景知识,但是,圣洁的家伙,我将不得不编写很多代码。”
不必要。已经编写了许多库,这些库使与Memcache的接口变得非常容易。还有其他库将上述整个过程编入了代码;Perl中的Data :: ObjectDriver就是这样一个库。至于其他语言,则需要进行自己的研究。
希望这个答案对您有所帮助。我经常发现,系统的可伸缩性通常会越来越少地归因于代码,而越来越多地归因于可靠的数据存储和管理策略/技术设计。
对于诸如Facebook之类的社交网络之类的需要高度可扩展的网站,设计网站的最佳方法是什么?
测量。
我想...
错误的政策。
需要实际测量。
可扩展性不是特定实施策略的功能,而是设计您的应用程序体系结构,以便数据访问层可以在不进行大量重构和重写的情况下发展。
构建可扩展系统的一项重要技术是了解您的高级数据访问需求并围绕它们建立接口协定。例如,您可能需要获得一位用户或列出任何用户最近发布的50张照片。
您的应用程序业务逻辑和数据访问逻辑之间不一定需要网络通道。一个方法调用间接调用每个逻辑操作一个方法就可以很好地启动。
首先,使这些数据访问方法尽可能简单。在您的应用程序提供实际使用模式并且收集有关瓶颈所在的数据之前,很难预测性能问题的根源。
通过具有定义良好的数据访问界面,您可以改进数据访问实现,而无需对整个应用程序进行大范围更改。您还可以决定透明地切换到业务逻辑的Web服务体系结构。
上面的许多答案在发现性能瓶颈后就如何进行提供了一些很好的建议,但是如果过早应用这些性能瓶颈,您可能会在代码复杂性变得烦恼之前就知道是否需要复杂性。
开发一个简单的网站,使其达到一定的访问量水平。您将学习如何制作可扩展的网站。
在面对问题之前,您将无法想到解决方案。
一旦站点滚动并且面临扩展需求,请相信我,您一定会知道该怎么做。:-)
众所周知,默认情况下,Web应用程序应设计为三层:Web(表示)层,应用程序层和数据库层。这种划分是由于每个层的要求不同-通常是数据库的高质量磁盘访问/存储,应用程序层的CPU /内存高以及Web层的外部带宽/内存/地理位置分散高。应用程序/数据库层通常合并到一个应用程序中,直到应用程序生命周期中的更晚为止,因为数据库计算机通常倾向于大型服务器,这些服务器也可以构建为处理早期应用程序负载。
但是,您的应用程序的特定层数和适当的体系结构不必与此模型或任何其他模型匹配。
计划需要测量和监视系统中的所有活动。从两层或三层设计开始,并专注于在构建时看起来需要最多资源的部分。让运行中的应用程序在此级别指导您的设计。您收集的信息越多,信息越准确和详细,就可以随着应用程序的增长做出更好的决策。
选择一个框架和体系结构,以后将允许您尽可能快地,轻松地进行/更改所需的更改。即使在同一个可执行文件中执行数据访问/存储/处理和应用程序处理,但是如果将它们适当地考虑在内,那么以后将它们分成两层也不会那么困难。
连接数据库的任何其他步骤都只是开销。例如,在UI -> Business Facade -> Business -> Data Access -> Database
和之间UI -> Database
,第二种方法更快。但是,删除的步骤越多,系统的可维护性就越差,并且重复出现的次数也就越多。想象一下编写必要的代码来检索个人资料,主页,朋友管理页面等中的朋友列表。
因此,您应该在更高的性能(当然会直接影响更高的可伸缩性)和更好的可维护性之间取得平衡。
但是,当您考虑创建高度可扩展的网站时,不要局限于数据库连接主题。也请考虑以下事项: