如何在PostgreSQL中快速插入BLOB?

时间:2009-07-23 22:30:58

标签: performance postgresql

您对配置PostgreSQL有何建议,以便可以快速编写BLOB?

我们正在使用PostgreSQL以高速率插入BLOB。

  1. 我们每秒大约220次调用lo_write()。
  2. 我们每个lo_write()写大约30KB的二进制数据。
  3. 相当于大约6 MB / s。
  4. 我们的计算机是RAID-5,因此写入速度接近200 MB / s。

    我们已经调整了postgresql.conf以获得以下内容:

    1. 我们更改了shared_buffers = 1GB
    2. 我们关闭了fsync
    3. logging_collector = off(几乎所有与日志记录相关的内容都已关闭)
    4. 我们确保如果我们不将BLOB存储为INSERT查询的一部分,那么PgSql保持良好状态。只有当我们将BLOB存储为查询的一部分时,它才会变慢。

      编辑:我正在使用Windows XP / Server。我正在使用Pgsql 8.3和PostGIS 1.3.6。我需要在数据库中存储BLOB的原因是因为我的应用程序要求我实时搜索这些BLOB。

      背景:我们的应用程序是高性能的实时信号处理,我们将信号作为BLOB存储到数据库中。

      编辑:这是我们用来执行基准测试的C ++代码。显然,我们的RAID配置大约需要16 MB / s。

      #include "libpq-fe.h"
      #include "libpq/libpq-fs.h"
      #include <sstream>
      #include <iostream>
      #include <tbb/tick_count.h>
      
      void test()
      {
        std::stringstream stst;
        stst << "user=postgres password=1234 dbname=test_db";
        PGconn* conn = PQconnectdb(stst.str().c_str());
        if (PQstatus(conn) != CONNECTION_OK)
        {
          throw std::exception("failed to connect To DB engine: ");
        }
      
        const int dataSize = 18512;
        char* data = (char*) malloc( dataSize );
      
      
        const int count = 10000;
      
        tbb::tick_count t0 = tbb::tick_count::now();
        for( int idx = 0; idx < count; idx++ )
        {
          // Insert acoustic binary into large object table "pg_largeobject" 
          Oid objectId;
          int lobj_fd;
          PGresult *res;
      
          res = PQexec(conn, "begin");
          PQclear(res);
      
          objectId = lo_creat(conn, INV_READ|INV_WRITE);
          if (objectId == 0)
          {
            throw std::exception("AddAcousticTable: Cannot create large object\n");
          }
      
          lobj_fd = lo_open(conn, objectId, INV_WRITE);
      
          const unsigned int writeBytes = lo_write(conn, lobj_fd, data, dataSize );
          if (writeBytes != dataSize )
          {
            std::stringstream msg;
            msg << "PsSasDataDB::AddToAcousticTable(): Incorrect number of bytes written for large object ";
            msg << writeBytes;
            throw std::exception(msg.str().c_str());
          }
      
          lo_close(conn, lobj_fd);
      
          res = PQexec(conn, "end");
          PQclear(res);
        }
        tbb::tick_count t1 = tbb::tick_count::now();
      
        std::cout << "Performance: " << (double(count*dataSize)/(t1-t0).seconds())/(1024.0*1024.0) << " MB/seconds";
      
        free( data );
      }
      int main()
      {
        try
        {
          test();
        }
        catch( std::exception e )
        {
          std::cerr << e.what();
        }
      
        return 0;
      }
      

5 个答案:

答案 0 :(得分:4)

RAID5:

它非常适合读取,写入大量数据和成本。 小桑多写道,这很糟糕。

由于你可能不使用你的数据库来存储大blob,你也会进行一些数据库写入/更新/插入,RAID5将是一个真正的PITA。

  • 您在表中插入或更新一行。 - &GT;需要编写表中的页面 - &GT;对于每个索引,至少需要更新一个索引页面 - &GT;在提交时,需要写入日志,刷新到磁盘并同步。

这些小的1页(8kB)写入中的每一个都小于您的RAID5条带大小,因此RAID控制器将需要寻找几个(或所有)RAID磁盘,读取几个条带,并重新写入更新的条带和平价。对于同步,您必须等待所有磁盘同步,在此期间它们不会为任何其他请求提供服务......某些RAID控制器在执行此操作时也特别糟糕,这取决于您的硬件。

- &GT;对于高随机写入吞吐量,最好将RAID 1(或10或01)用于数据,并为2个独立磁盘上的日志使用额外的RAID1卷。

因此,如果您的RAID5中有3个磁盘,请删除一个磁盘,将数据放入磁盘并登录另一个磁盘,然后进行基准测试。如果它是好的,请加入额外的磁盘,并制作2x RAID1卷(日志,数据)。

如果您的负载是读取负载而不是写入量大,对于相同的预算(磁盘数量),最好将日志放在与数据相同的卷上,而将RAID10放在同一个卷上。


  

我需要在数据库中存储BLOB的原因是因为   我的应用程序要求我实时搜索这些BLOB。

如果你想使用数据库运行良好的东西(比如事务,安全性,或者从一个地方放置1台服务器中的所有东西,连贯的备份,没有头痛等),你就把BLOB放在数据库中。

如果要进行搜索,则不使用BLOB进行搜索,而是使用简单的数据库表和SQL查询来获取对所需BLOB的引用。然后,您获得了BLOB,但您可以轻松获取文件系统文件。因此,在您的应用程序中,您也可以使用文件系统文件,这样会快得多。

答案 1 :(得分:2)

在数据库端提高写入性能的最大胜利是它将checkpoint_segments设置为更高的数字。默认值为3,您至少需要30或更多。如果您查看系统上的数据库日志,可能会收到有关此问题的警告。

除此之外,获得良好的写入性能会变成更多的磁盘硬件问题。你使用什么RAID控制器?它是否具有最佳设置,具有可靠的电池备份写入缓存?这是最重要的事情 - 如果你这样做,RAID5可能会在这种情况下容忍,因为控制器缓存将合并一些随机写入。

P.S。不要关闭fsync,这很危险。相反,你应该使用asynchronous commit,它可以完成相同的基本功能(减少磁盘刷新次数),但是更加安全。

答案 2 :(得分:1)

不要放弃RAID5。现代RAID系统可以在RAID-5或RAID-6中为您提供300-350 MB / s的速度。

我之前也使用过pg_largeobject来存储文件。它只会导致备份和OID问题,尽管那是PostgreSQL 7.2 / 7.3。

我同意上面的评论,虽然将文件存储在文件系统上,如果必须的话,在其上放置一个函数视图前端会更有效。用于存储获取文件的函数视图组合的另一个优点可能是不同的存储类型,例如本地文件系统,网格文件系统,云等。

答案 3 :(得分:1)

硬件答案多于软件答案,但是:

对于RAM磁盘或SSD而言,这听起来非常棒,可以缓解RAID5上的随机访问压力。只有当你可以容忍丢失数据(或电池回来)时才有RAM磁盘,当然,使用SSD的分层SAN比普通SAN更昂贵,但在Solaris中它可以在ZFS中完成,并且有一些不成熟Linux克隆。您正在研究加速与直接随机写入相比的数量级。

还要考虑查看SAN控制器的固件升级,和/或升级其内部的电池备份内存。如果它足够大,它通常可以更有效地进行写入 - 组合,从而提高吞吐量。

答案 4 :(得分:1)

我遇到了同样的问题并在两个选项之间执行了基准测试:将blob数据存储在文件中或数据库中的bytea中。

我将列配置为使用EXTERNAL存储模式(TOAST模式)。

测试包括一个客户端发送1000张灰色图像,每张图像326 * 244像素(总共636 Mbit),并由服务器存储。

每次测试重复5次。 结果显示每种方法处理的平均数据速率为Mbit / sec。

档案:119.9 DB:164.8

令人惊讶的是,在数据库中存储图像比在文件中存储快37%! 我想在使用标准文件系统时打开和关闭数百个文件是一个瓶颈。

我在运行PostgreSQL 9.2的x86 Windows 7 32位,4GB服务器(即我的笔记本电脑......)上执行了基准测试