0

我们正在开发拥有超过 10 万种产品的购物车网站,该网站基于流行的电子商务应用程序 - NopCommerce 2.3 版(只是为了向您介绍 NopCommerce - 它是最好和流行的开源电子商务应用程序之一建立在 ASP.net 版本 4 和 MVC3 之上。)。该网站以两种语言和单一货币发布。

它拥有大约 80 个类别和 30-40k 产品,效果相当好。我的意思是不是很糟糕。但这也不好。一旦添加了更多产品,性能问题就开始出现,例如响应时间长(加载超过 40-50 秒)和 CPU 使用率高(使用 90-100%),只有 10-20 个用户。

该服务器是配备 16 GB RAM 的四核 Xeon 处理器 - Windows Server 2008 R2,并且可以与另外一个电子商务网站正常工作,该网站有 50k 的定制开发代码产品 - 几乎不占用 4-8% 的 cpu。

我们使用缓存将主页特色产品和类别菜单存储在内存中,以避免数据库调用。它只改进了主页。

后来为了解决问题,我们分析并发现是目录列表导致从数据库中获取数据的延迟很大,这是精细标准化的。SQL 服务器似乎占用了 80-90% 的 CPU,而 w3wp 占用了 30-40% 的 cpu,这一直导致 100% 的 cpu 一直在不断地导致网站上只有少数访问者。我们咨询了一些专家,他们建议我们以二进制格式将非规范化数据存储在磁盘上,以绕过昂贵的数据库连接。我们做了一些研究并使用 Protobuff 将非规范化的序列化对象数据存储到磁盘中,该磁盘仅存储目录 - 产品列表页面所需的那些字段。但是由于维护了一些规范功能,我们过去创建了 3 个二进制文件。一个用于产品对象,另一个用于类别规范对象。这两个文件属于每个类别。还有一个用于产品和规格映射的文件 - 占用将近 5 mb。当请求到来时,它从序列化的二进制文件中读取数据并将数据返回给对象。只有当有人根据规范过滤产品时,它才会读入映射文件。

因此,现在每当对分类产品列表页面的请求时,它会检查是否为该分类创建了二进制文件,如果没有,则使用存储过程生成,并将对象保存为二进制以供以后使用。如果文件存在,则直接从二进制文件中读取。有了这个东西,我们在加载这个页面时避免了 90% 的 db 调用。只有少数用户(大约 30-40 人),它就像一个魅力。我们能够将每个页面加载的响应时间减少到 700-800 毫秒。如果我们查看加载时间,这是一个很大的改进,但 CPU 仍然较高。不同之处在于:现在 w3wp 使用 60-70% 的 cpu 和 20-30 个访问者,而 sql 几乎不使用 5-8%。

但是随着更多用户 appx 达到 100-120,服务器开始挂起,w3wp 的使用率持续超过 100%。请求不再以秒为单位提供服务,而是需要超过 20-25 秒才能加载。然后大多数请求永远不会被满足。当多个请求到达该站点时,我们注意到了这一点。

我们不是序列化和二进制格式器方面的专家。但是我们认为高cpu使用率是由文件读取操作引起的,或者可能是由于在每次目录页面加载时执行的反序列化操作。

我们现在正在寻找解决高 CPU 使用率的可能解决方案。可能是什么问题,我们应该在哪里解决它。您怎么看,是文件读取操作还是反序列化导致了这种情况?我们应该将非规范化对象存储在数据库中吗?我们有什么替代方案来解决这个问题?

等待您的专家意见。

提前致谢。

4

3 回答 3

1

由于您遇到 CPU 问题,我怀疑反序列化是罪魁祸首。ISerializable在这种情况下,您可以通过自己实现接口使序列化、反序列化快近 100 倍。我以前曾将这种技术用于大型对象图,并且改进非常显着。

假设您有这样的课程:

[Serializable]
public class TestObject : ISerializable {
  public long     id1;
  public long     id2;
  public long     id3;
  public string   s1;
  public string   s2;
  public string   s3;
  public string   s4;
  public DateTime dt1;
  public DateTime dt2;
  public bool     b1;
  public bool     b2;
  public bool     b3;
  public byte     e1;
  public IDictionary<string,object> d1;
}

实现 ISerializable 以便您可以进行自定义序列化和反序列化。

public void GetObjectData (SerializationInfo info, StreamingContext ctxt) {
  SerializationWriter sw = SerializationWriter.GetWriter ();
  sw.Write (id1);
  sw.Write (id2);
  sw.Write (id3);
  sw.Write (s1);
  sw.Write (s2);
  sw.Write (s3);
  sw.Write (s4);
  sw.Write (dt1);
  sw.Write (dt2);
  sw.Write (b1);
  sw.Write (b2);
  sw.Write (b3);
  sw.Write (e1);
  sw.Write<string,object> (d1);
  sw.AddToInfo (info);
}

public TestObject (SerializationInfo info, StreamingContext ctxt) {
  SerializationReader sr = SerializationReader.GetReader (info);
  id1 = sr.ReadInt64 ();
  id2 = sr.ReadInt64 ();
  id3 = sr.ReadInt64 ();
  s1  = sr.ReadString ();
  s2  = sr.ReadString ();
  s3  = sr.ReadString ();
  s4  = sr.ReadString ();
  dt1 = sr.ReadDateTime ();
  dt2 = sr.ReadDateTime ();
  b1  = sr.ReadBoolean ();
  b2  = sr.ReadBoolean ();
  b3  = sr.ReadBoolean ();
  e1  = sr.ReadByte ();
  d1  = sr.ReadDictionary<string,object> ();
}

这不仅会使有效载荷缩小 10 到 100 倍,而且还可以将性能提高 10 倍,有时甚至提高 100 倍。

另一件事,看看你是否有任何大型循环可以循环数千个对象。也许你有次优的 linq 查询。这些有时会占用 CPU。

最后,我将推荐我见过的开发人员犯的 10 大缓存错误,尤其是在使用分布式缓存时。

http://www.codeproject.com/Articles/115107/Ten-Caching-Mistakes-that-Break-your-App

于 2012-02-12T12:30:46.970 回答
0

问题1:这个盒子上运行的是什么?如果我没看错的话,你有一个网站有 50,000 种产品(没有提到用户或点击量),而另一个网站有更多。当您堆叠站点时,您会看到一些退化,即使您的代码非常紧凑。

问题 2:您是否将所有图层都放在一个盒子上?您现在有竞争问题,并且可能会由于 I/O 操作而阻塞一些 CPU 绑定线程。

问题 3:您是否审查过代码以确保正确的开发概念和方法(SOLID 等)?如果没有,您可能会持有比需要更长的资源并导致问题。

问题4:你有没有介绍过?我的意思是 SQL Server 和 Web 应用程序。如果没有,您不知道问题可能出在哪里,我怀疑任何人都可以在这个论坛上帮助您。

即使有数以百万计的“产品”,正确设计的数据库和站点也应该相当快。但是,不同的因素共同代表性能。所有层上的所有部分都会影响应用程序。

举个例子,我曾经咨询过一家公司,该公司已经构建了一个正在消亡的高性能电子商务应用程序。在代码审查中,所有部分似乎都很好。在测试中,页面和数据库都运行良好。但他们从来没有强调过这个系统。如果他们有,他们会抓住这一点精神错乱。

 //let's not focus on the magic string, okay? Think about static
 private static SqlConnection connection = new SqlConnection("{conn string here}");

整个站点都通过单个 SQL 连接进行过滤,因为一位开发人员不了解底层连接池的概念,并认为对象初始化比通过静态“始终在线”连接过滤更受欢迎。

在您对应用程序进行概要分析之前,您在这里没有可以回答的问题。一旦你发现问题并提出问题,有人可以站出来说“这就是你解决问题的方法”。您可以向此问题添加更多信息,但在确定问题之前,您将无处可去。

于 2012-02-10T17:03:44.323 回答
0

数据库“问题”的答案是修复设计不佳的数据库。数据库“问题”并不是数据库能力的根本问题。你的设计有问题。

修复有多种形式,但它始终是答案。数据库“问题”总是与许多不同类型的问题相同。

这个故事的寓意是,永远不要从一个对修复数据库问题一无所知并建议你使用胶带的人那里获得数据库建议。所有数据库问题的答案是将数据和计算移动到尽可能靠近数据库的位置。

从数据库中移动数据越远,问题就越严重,解决方案的可扩展性会线性降低。不要听非数据库开发人员试图“修复”您的数据库。

于 2013-05-22T04:51:52.053 回答