我们正在考虑将我们的 php 会话数据移动到 redis。
设置看起来很简单。只需在 php.ini 中设置以下内容并重新启动 apache。应该都设置好了:
session.save_handler = redis
session.save_path = "tcp://host1:6379"
如果可能的话,我希望我们的用户不要注意到迁移。是否可以在不丢失任何现有会话数据的情况下将会话数据移动到 redis?
目前没有针对您所要求的开箱即用的解决方案,但为此任务编写自定义脚本实际上相当简单。
本质上,phpredis 将会话数据以字符串形式存储在 redis 中,键名格式如下:PHPREDIS_SESSION:$sessionid
,其中$sessionid
是会话的 php id,可通过 检索session_id()
。会话数据被“编码”为 php-session 序列化变量(与常见的 php 序列化/反序列化格式略有不同,请参阅session_encode)。
既然我们知道了这一点,那么迁移存储在文件中的会话数据有两种可能性:
遍历每个会话文件(实际路径session.save_path
在您的 php.ini 中设置),读取数据并将其写回 redis。文件本身存储会话数据的 php-session 序列化表示,这意味着内容可以直接复制到 redis,并且文件名具有以下模式:sess_$sessionid
$sessionid 是,你猜对了,id 你'将要用于您的 redis 密钥。
通过暂时保留基于文件的会话来逐步迁移数据,但在使用会话数据时实时填充 redis,直到存储在 redis 中的会话数量看起来足以进行切换。这可以通过执行以下操作来实现:
$redis->set("PHPREDIS_SESSION:".session_id(), session_encode());
就在每个脚本结束之前。此方法可能会增加一点开销,具体取决于会话中的数据量以及 session_encode 的工作方式。
刚刚在 bash 中创建了这样一个脚本并添加到我的仓库中。
可能,是的,容易,不是那么多。
AFAIK,phpredis 没有迁移脚本,因此您必须自己编写一个。您可能想查看Cm_RedisSession 的脚本,该脚本对该 redis 模块执行类似操作。
如果你使用 symfony,你可以使用如下命令:
yml 配置:
parameters:
redis_address: "localhost"
project_name : "ACME_"
snc_redis:
clients:
default:
type: predis
alias: default
dsn: redis://%redis_address%
logging: '%kernel.debug%'
session:
type: predis
alias: session
dsn: redis://%redis_address%/1
logging: true
session:
client: session
prefix: '%project_name%PHPREDIS_SESSION'
ttl: 7776000 # 90 days
symfony 命令:
<?php
// Command: app/console acme:migrate:session:files:to:redis --env=dev
namespace Acme\AppBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Finder\Finder;
class MigrateSessionFilesToRedisCommand extends ContainerAwareCommand {
protected $env;
protected function configure() {
$this->setName('acme:migrate:session:files:to:redis')
->setDescription("Migrate Session Files To Redis")
->setHelp("Migrate Session Files To Redis");
}
protected function execute(InputInterface $input, OutputInterface $output) {
$sessionPath = realpath( sprintf('%s%s', $this->getContainer()->getParameter('kernel.root_dir'), '/sessions') );
$prefix = 'ACME_PHPREDIS_SESSION';
$redis = $this->getContainer()->get('snc_redis.session');
$finder = new Finder();
$finder->files()->in($sessionPath);
foreach ($finder as $file) {
$realPath = $file->getRealpath();
$sessionId = str_replace( 'sess_', '', $file->getRelativePathname() );
$redis->append( sprintf('%s:%s', $prefix, $sessionId) , file_get_contents( $realPath ) );
}
}
}
注意:将“ACME”替换为您的项目 ID/名称,并设置存储文件的正确会话路径。
这是我将会话数据导入 redis 的超级简单脚本:
#!/bin/bash
export REDISCLI_AUTH=my-supper-strong-password-4-redis-server
TTL=$(( 24 * 3600 ))
for i in sess_*; do
ex=$(( $(date +%s) - $(stat -c %Y "$i") + $TTL ))
k="PHPREDIS_SESSION:${i:5}"
v=$(cat "$i")
echo "SET $k of len:${#v} EX $ex"
redis-cli SET "$k" "$v" EX $ex
done
我没有彻底测试它,所以请谨慎使用。