2011-06-23 109 views
2

我运行一个Drupal 7.2网站,嵌入a flash game,其中包含几个用于玩家统计的自定义PHP脚本。使用CentOS 5.6/64位,PostgreSQL 8.4.8和PHP 5.3。这是一款配备4GB RAM的Quad-Opteron。PHP和pgbouncer处于交易模式:当前交易已中止

在高峰时期(当网上有大约500名玩家时),我的网站曾经因为postmaster程序过多而倒闭。对pgsql-general mailing list建议我安装pgbouncer 1.3.4具有以下/etc/pgbouncer.ini:

[databases] 
pref = host=/tmp user=pref password=XXX dbname=pref 

[pgbouncer] 
logfile = /var/log/pgbouncer.log 
pidfile = /var/run/pgbouncer/pgbouncer.pid 
listen_port = 6432 
unix_socket_dir = /tmp 

auth_type = md5 
auth_file = /var/lib/pgsql/data/global/pg_auth 

pool_mode = transaction 
;pool_mode = session 

server_check_delay = 10 

max_client_conn = 200 
default_pool_size = 16 

log_connections = 0 
log_disconnections = 0 
log_pooler_errors = 1 

,并增加的shared_buffers = 1024MB,并在postgresql.conf中下降的max_connections = 50。

这帮助,但我经常一个准备PDO语句不会被发现了一个问题:

SQLSTATE[26000]: Invalid sql statement name: 7 ERROR: prepared statement "pdo_stmt_00000016" does not exist 
  • 可能是因为pgbouncer来切换准备()之间的连接和执行()。

我无法将pgbouncer切换到会话模式 - 我的网站将挂起。

我试过添加PDO :: ATTR_EMULATE_PREPARES => true - 我的网站也挂起。

我添加的BeginTransaction()和commit()围绕每个准备()和execute()调用 - 但后来我得到了很常出现以下错误:

SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block 

下面是我的代码的摘录由于错误而失败 - 这非常简单,只需要调用5条SELECT语句:

function fetch_top() { 
     $table  = ''; 
     $top   = ''; 

     try { 
       # throw exception on any errors 
       $options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); 
       $db = new PDO(sprintf('pgsql:host=%s port=%u; dbname=%s', 
         DBHOST, DBPORT, DBNAME), DBUSER, DBPASS, $options); 

       # last week's winner 
       $db->beginTransaction(); 
       $sth = $db->prepare(" 
select u.id, 
     u.first_name, 
     u.avatar, 
     u.female, 
     u.city, 
     m.money, 
     u.login > u.logout as online 
from pref_users u, pref_money m where 
     m.yw=to_char(current_timestamp - interval '1 week', 'IYYY-IW') and 
     u.id=m.id 
order by m.money desc 
limit 1 
"); 
       $sth->execute(); 
       $winner = $sth->fetch(PDO::FETCH_OBJ); 
       $db->commit(); 

       $db->beginTransaction(); 
       $sth = $db->prepare(' 
select count(id) from (
    select id, 
      row_number() over(partition by yw order by money desc) as ranking 
    from pref_money 
) x 
where x.ranking = 1 and id=? 
'); 
       $sth->execute(array($winner->id)); 
       $winner_medals = $sth->fetchColumn(); 
       $db->commit(); 

       # current week leader 
       $db->beginTransaction(); 
       $sth = $db->prepare(" 
select u.id, 
     u.first_name, 
     u.avatar, 
     u.female, 
     u.city, 
     m.money, 
     u.login > u.logout as online 
from pref_users u, pref_money m where 
     m.yw=to_char(current_timestamp, 'IYYY-IW') and 
     u.id=m.id 
order by m.money desc 
limit 1 
"); 
       $sth->execute(); 
       $leader = $sth->fetch(PDO::FETCH_OBJ); 
       $db->commit(); 

       $db->beginTransaction(); 
       $sth = $db->prepare(' 
select count(id) from (
    select id, 
      row_number() over(partition by yw order by money desc) as ranking 
    from pref_money 
) x 
where x.ranking = 1 and id=? 
'); 
       $sth->execute(array($leader->id)); 
       $leader_medals = $sth->fetchColumn(); 
       $db->commit(); 

       # fetch top players 
       $db->beginTransaction(); 
       $sth = $db->prepare(" 
select u.id, 
     u.first_name, 
     u.female, 
     u.city, 
     m.money, 
     u.login > u.logout as online 
from pref_users u, pref_money m where 
     m.yw=to_char(current_timestamp, 'IYYY-IW') and 
     u.id=m.id 
order by m.money desc 
limit 7 
"); 
       $sth->execute(); 
       $i = 0; 
       while ($player = $sth->fetch(PDO::FETCH_OBJ)) { 
         $top .= user_link($player) . ($i++ > 0 ? '' : '&nbsp;&raquo;') . '<br />'; 
       } 
       $db->commit(); 

       # create the HTML table 
       $table = sprintf('.... skipped for brevity ....'); 
     } catch (Exception $e) { 
       exit('Database problem: ' . $e->getMessage()); 
     } 

     return $table; 
} 

请帮忙吗? 亚历

回答

0

我不使用PDO,但在会话模式下使用pgBouncer的预准备语句对我来说确实有效。我只需要设置“server_reset_query = DISCARD ALL”就可以使preapred语句正常工作。你可以设置pool_mode会话,并设置上述变量?

+0

你好,这正是我昨天所做的,似乎工作得很好(用PDO,但没有beginTransaction/commit) –

1

交易池

为了准备语句在此模式下工作,需要PgBouncer到 跟踪它们的内部,它不这样做。 所以只有这样 保持使用PgBouncer在这种模式下是完全禁用准备好的语句 。

+0

事务池文档的URL:http://pgbouncer.projects.postgresql.org/doc/usage.html#_description @jordani是绝对正确的,但是使用事务池。 – Sean

+0

如何禁用脚本中的预准备语句? –

+0

@Sean,对不起 - 但我不明白你的评论“但是,使用交易池”。我已经在做。 –

1
  1. 配置pgbouncer使用transaction pooling
  2. 写PL函数创建你的PREPARE编报表
  3. 让你的PL功能检查pg_prepared_statements系统视图,并生成所有准备好的声明,如果有任何遗漏。
  4. 更改SQL命令的执行是:
    1. BEGIN
    2. SELECT create_prepared_statements();
    3. /* Do whatever it is that you would normally do */
    4. COMMIT

需要尚未将是 - 调用这个原因编写create_prepared_statements() PL函数是因为你不知道后端是你的连接正在发送到,还是你正在与之通话的后端是新产生的,并且没有PREPARE ed语句。

根据您使用PREPARE'ed语句的方式,查看VIEW的PL函数,因为它们自动生成并缓存PREPARE'ed语句。我建议更积极地使用PL/pgsql函数,但是因为这是最简单的维护方法。