postgresql PHP和pgbouncer处于事务模式:当前事务被中止

628mspwn  于 2023-11-18  发布在  PostgreSQL
关注(0)|答案(3)|浏览(142)

我运行了一个Drupal 7.2网站,嵌入了a flash game和一些用于播放器统计的自定义PHP脚本。使用CentOS 5.6/64位,PostgreSQL 8.4.8和PHP 5.3。它是一个具有4GB RAM的Quad-Opteron。
在高峰期(大约有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

字符串
并在postgresql. conf中增加了shared_buffers = 1024 MB,减少了max_connections = 50。
这很有帮助,但我经常遇到一个问题,即无法找到准备好的PDO语句:

SQLSTATE[26000]: Invalid sql statement name: 7 ERROR: prepared statement "pdo_stmt_00000016" does not exist

  • 可能是因为pgbouncer在prepare()和execute()之间切换连接。

我无法将pgbouncer切换到会话模式-我的网站将挂起。
我尝试添加PDO::ATTR_EMULATE_PREPARES => true-我的网站也挂起了。
我已经在每个prepare()和execute()调用周围添加了transaction()和commit()-但是我经常得到以下错误:

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


下面是我的代码的一个摘录失败的错误-它是非常简单的,只是调用五个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;
}


有没有人帮忙?亚历克斯

qc6wkl3g

qc6wkl3g1#

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

o2rvlv0m

o2rvlv0m2#

交易池
要使准备好的语句在这种模式下工作,需要PgBouncer在内部跟踪它们,而PgBouncer没有这样做。因此,在这种模式下继续使用PgBouncer的唯一方法是完全禁用准备好的语句。

mrfwxfqh

mrfwxfqh3#

1.配置pgbouncer以使用transaction pooling
1.编写一个PL函数来创建PREPARE艾德语句
1.让您的PL函数检查pg_prepared_statements系统视图,并生成所有准备好的语句(如果缺少)。
1.将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的艾德语句,查看VIEW或PL函数,因为它们会自动生成和缓存PREPARE的艾德语句。我建议更积极地使用PL/pgsql函数,因为这是最容易维护的方法。

相关问题