【问题标题】:Working around 1 MB max_allowed_packet when reading MEDIUMBLOB in PDO MySQL在 PDO MySQL 中读取 MEDIUMBLOB 时工作大约 1 MB max_allowed_pa​​cket
【发布时间】:2015-04-20 16:36:51
【问题描述】:

我正在使用 PDO 检索 MySQL 数据库中表的 MEDIUMBLOB 列中的 5 MB 值。 MEDIUMBLOB 最多可以存储 16 MB,但由于 max_allowed_packet,PDO 将其截断为 1 MB。我尝试了bindColumn 中提到的Large Objects,但是PDO 的MySQL 驱动程序产生一个字符串,而不是一个流(bug 40913,报告为“仍然存在于PHP-5.6.5 中”)。实际上,它将PDO::PARAM_LOB 视为PDO::PARAM_STR 的同义词。对BLOB Download Truncated at 1 MB... 的回答建议在服务器上增加my.cnf 中的max_allowed_packet 变量,但我无权更改my.cnf,这可能会影响服务器的其他用户。我知道可以解决这个问题,因为 phpMyAdmin 可以从同一服务器下载如此大的BLOB

表格是这样定义的:

CREATE TABLE IF NOT EXISTS cache_items (
  `cache_id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  `name` VARBINARY(63),
  `value` MEDIUMBLOB NOT NULL,
  `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  `expires` DATETIME NOT NULL,
  UNIQUE (`name`),
  INDEX (`expires`)
) ENGINE=INNODB;

PHP 代码:

<?php
require_once("dbsettings.php");
$db = new PDO($pdo_dsn, $pdo_username, $pdo_password, $pdo_options);
$name = 'hello';
$read_stmt = $db->prepare("
SELECT `value` FROM `cache_items`
WHERE `name` = :n AND `expires` > CURRENT_TIMESTAMP
ORDER BY `cache_id` DESC LIMIT 1
");
$read_stmt->execute([':n' => $name]);
$read_stmt->bindColumn(1, $value_fp, PDO::PARAM_LOB);
$ok = $read_stmt->fetch(PDO::FETCH_BOUND);
echo gettype($bodyfp);  // string, not resource, because of bug 40913
echo strlen($bodyfp);   // 1048576, not 5xxxxxx, because of max_allowed_packet

那么程序应该如何检索一个大的BLOB?或者将每个value 存储在一个目录中的文件中,然后执行一个定期任务,从目录中删除与cache_items 中未过期条目不对应的任何文件,这样会更实用吗?

【问题讨论】:

    标签: php mysql pdo


    【解决方案1】:

    我通过创建一个循环来解决这个问题,该循环使用 MySQL 的 SUBSTRING 函数在循环中读取较小的值块,其中每个块都小于一个数据包。

    <?php
    // [connection setup omitted]
    $stat_stmt = $db->prepare("
    SELECT `cache_id`, LENGTH(`value`) FROM `cache_items`
    WHERE `name` = :n AND `expires` > CURRENT_TIMESTAMP
    ORDER BY `cache_id` DESC LIMIT 1
    ");
    $read_stmt = $db->prepare("
    SELECT SUBSTRING(`value` FROM :start + 1 FOR 250000)
    FROM `cache_items`
    WHERE `cache_id` = :id
    ");
    
    // Find the ID and length of the cache entry
    $stat_stmt->execute($stat_stmt, [':n'=>$name]);
    $files = $stat_stmt->fetchAll(PDO::FETCH_NUM);
    if (!$files) {
        exit;
    }
    list($cache_id, $length) = $files[0];
    
    // Read in a loop to work around servers with small MySQL max_allowed_packet
    // as well as the fact that PDO::PARAM_LOB on MySQL produces a string instead
    // of a stream
    // https://bugs.php.net/bug.php?id=40913
    $length_so_far = 0;
    $body = [];
    while ($length_so_far < $length) {
        $read_stmt->execute([':id'=>$cache_id, ':start'=>$length_so_far]);
        $piece = $read_stmt->fetchAll(PDO::FETCH_COLUMN, 0);
        if (!$piece) {
            exit;
        }
        $piece = $piece[0];
        if (strlen($piece) < 1) {
            exit;
        }
        $length_so_far += strlen($piece);
        $body[] = $piece;
    }
    echo implode('', $body);
    

    【讨论】:

      猜你喜欢
      • 2017-01-15
      • 2015-08-31
      • 2018-09-13
      • 1970-01-01
      • 2018-10-15
      • 2011-12-25
      • 1970-01-01
      • 1970-01-01
      • 2015-06-18
      相关资源
      最近更新 更多