【问题标题】:Database design for write-heavy web application编写繁重的 Web 应用程序的数据库设计
【发布时间】:2010-12-13 02:28:10
【问题描述】:

我们向客户提供的许多 LOB 应用程序都具有营销/促销性质(抽奖、活动注册等)。大多数应用程序虽然非常简单,但对数据库的要求很高。例如,想象一个“注册”类型的网站作为在超级碗期间播放的商业广告的后盾(是的,我们有几个)。

尽管我们已经非常擅长优化我们的网络应用程序代码,但数据库始终是一个问题,尽管应用程序相对简单。流程通常是这样的:

  1. 从数据库中读取以检测现有记录
  2. 如果记录是新的,则写入数据库

在许多情况下,这就是我们的应用程序需要执行的所有数据访问。但是,鉴于这是应用程序的唯一目的,对这个简单的过程进行大幅度优化是非常重要的。

就这个问题而言,我们有一个服务器运行一个raid 5 磁盘阵列来存储数据文件,另一个运行raid 5 阵列来存储日志。此时,操作系统为 Windows 2003 标准 32 位,服务器有 4 GB 内存。一些应用程序使用 SQL 2005 标准,而其他应用程序使用 MySQL 5.1。我非常清楚在这里可以进行某些操作系统和硬件优化,但我希望首先从软件方面解决我的需求。广泛的分析告诉我们,磁盘 IO 通常是主要瓶颈

说了这么多,并且知道缓存无济于事,因为大多数读取都是唯一的并且返回的数据很少(通常只有一点点指示记录是否存在),我正在考虑跳入内存数据库领域作为真实数据库的写入缓存层。考虑到我们的大部分高流量本质上是零星的,并且不会持续几个小时,这似乎很合适。此外,在大多数情况下,由于服务器崩溃可能会丢失几分钟的数据是可以接受的。

在最简单的形式中,我会修改一个典型的注册应用来执行以下操作:

  1. 在磁盘 DB 和内存 DB 中查询现有记录
  2. 如果没有,则将数据写入内存数据库并返回
  3. 定期将内存 DB 刷新到磁盘 DB

我的问题是:对于这个中间内存数据库,我有哪些选择?我已经尝试过内存中的哈希表、数据表等,但我正在寻找其他选项,甚至是针对完全不同方法的建议。

【问题讨论】:

  • 请提供记录的数量和大小的数量级,可能在特定活动之前和之后区分计数(即包括活动期间收集的额外记录计数的粗略概念)
  • 在一个由高流量驱动程序(如电视广告或广播广告)支持的典型应用程序中,我们可能会在广告播放后的 15-30 分钟内看到超过 200,000 次注册尝试。其中大部分通常发生在现场之后的 3-5 分钟内,因此存在争用问题。绝对数量不是问题,问题是并发性。对于此类性质的单一短期应用程序,我们有史以来最大的数据库在 2 个月内接近 1000 万条记录,其中大部分流量来自电视广告和电子邮件活动。
  • 另一种选择是将 UPSERT 逻辑封装在存储过程中,这样可以节省您的数据库访问(以及相关开销)。
  • +1 在 UPSERT 上(现在在 SQL2003 规范中 MERGE - 这是 SQL Server 所说的),也就是 MySQL 中的 REPLACE。这可能会有所帮助:msdn.microsoft.com/en-us/library/cc879317.aspx
  • 添加更多 RAM 以使您的工作集适合 RAM 远不止获得额外的硬盘。现代数据库也足够聪明,可以缓冲写入。对同一块的 1000 次写入可以转换为一次写入,前提是您有足够的 RAM 将所有内容的副本保存在 RAM 中,速度会快几个数量级。

标签: database-design data-modeling


【解决方案1】:

如果您不需要实时知道是否存在现有记录(即记录进入那里很重要,但您不需要向用户报告它是新的还是现有的) ,您可以以允许极快写入时间的方式构建数据库,而无需内存数据库,如果服务器出现故障或工作进程重新启动,内存数据库会带来很多潜在问题。

在您的数据库中为每个涉及此大量写入流程的表创建两个表。一个表应该是您的“实时”表,并且应该尽可能地进行写优化(即没有索引并且从不读取,除非移动到读取表时)。您的另一个表应该是您的读取优化表 - 根据任何报告注意事项等进行索引。

每当您写入实时表时,请忽略与记录是新记录还是现有记录有关的任何事情,或者除了尽快将该数据放入表中并从数据库中取出之外的任何事情。设置将记录从活动表移动到读取优化表的计划作业,并担心匹配那里的现有记录。理想情况下,这将在非高峰时间完成,否则您可能需要考虑使用第三个临时表,以便在任何时候都不会在实时表上发生争用。

【讨论】:

  • 如果实时表中的数据可以相对快速地读取怎么办? (即不能等到计划的作业将新数据传输到读取表)
【解决方案2】:

接受“一切都是消息,数据库是备份”的新概念。当您有东西要存储时,创建一条消息并使用 XMPP 将其发送到黑盒(如 eJabberD)。让黑盒按照自己的时间表更新您的数据库。这就是 Twitter 等网站的工作方式。

看看这个幻灯片: http://www.slideshare.net/kellan/beyond-rest

【讨论】:

    【解决方案3】:

    这是一个奇怪的想法:不要使用数据库进行初始捕获。设计两三个速度极快的索引文件,它们的格式不需要经常更改。捕获这些文件中的数据。

    编写一些适当触发的软件,将捕获的数据复制到数据库中,但不会延迟交互式用户。标记复制的数据以防止重复复制,并回收文件中的空间。

    现在,您可以根据在多种用途之间共享数据的想法来设计数据库,而不是保持跟上捕获过程的想法。毕竟,共享数据才是 databased 真正的亮点。

    【讨论】:

    • 这是 Google 的 Dapper 分布式跟踪工具工作原理背后的基本理念。应用程序写入 lcoal 文件,然后收集器更懒惰地将这些文件复制到 BigTable。
    【解决方案4】:

    与编程无关,但肯定会有所帮助:获取一些较新的固态磁盘。

    是的,它们的大小相对昂贵,但由于磁盘 IO 是瓶颈,只需将当前的 HDD 换成一些 SSD 就可以大大提高性能。

    【讨论】:

      【解决方案5】:

      SQLite 有一个in memory 操作模式。如果您的页面点击处理程序后面有一个持久的服务器进程,这将起作用。

      否则,基于常规文件的 DB 可能会被欺骗,将其文件写入内存文件系统,例如 tmpfs

      【讨论】:

        【解决方案6】:

        我不知道你提到的数据库,但是如果数据库的内容(或者至少是重要的表)适合内存,oracle 能够将它固定在缓存中,所以它基本上表现得像一个 in内存数据库。

        我还会检查您的数据库的隔离级别设置。如果您能够放松那些,您可能可以减少锁定。

        最后考虑移除独特的约束,或者在高峰期禁用它们。

        【讨论】:

          【解决方案7】:

          在我看来,您应该能够使用具有用户大小缓存的 RDBMS 来适应您的工作负载。我看到每秒大约有 10000 个索引记录,使用简单的 C++ 可调用 RDBMS 和普通硬件。这包括提交到磁盘。此外,由于您可能只查看记录中的一个小字段,因此请寻找面向列的数据库——将数据存储在列中的数据库。如果您只对一个领域感兴趣,那么阅读整行是没有意义的。

          【讨论】:

            【解决方案8】:

            正如许多其他人所提到的,优化您的数据库架构以进行写入而不是读取,这是您的首要任务,尽管我猜您已经去过那里

            在研究内存数据库之前,您可能想了解一些可用的 ORM,尤其是 NHibernate。

            NHibernate 将一些数据保存在内存中,并允许您控制何时从内存中“刷新”数据更新并与数据库同步。

            您可能会觉得值得一看。

            【讨论】:

              【解决方案9】:

              编辑:严格专注于磁盘 I/O...

              1. 尽可能多地删除不必要的索引。索引不是免费的——空间或时间。
              2. 删除任何您不需要的特殊触发器或约束。
              3. 剔除任何并非绝对关键的实体关系/关系完整性运算符。
              4. 如果您当前的 DBMS 支持,请将事务表分离到多个磁盘中(例如,循环)。
              5. 考虑添加更多彼此独立的数据库服务器(即不涉及复制);为此,您需要一个调度程序来决定哪个服务器将接受交易,以及一个整合交易的方案/单独的进程。

              最大限度地减少数据库逻辑量并横向添加服务器(与尖端服务器技术相反)基本上是 ebay 采用的方法。

              【讨论】:

                猜你喜欢
                • 2011-12-07
                • 2018-01-31
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-12-19
                • 1970-01-01
                相关资源
                最近更新 更多