我想在PHP中实现无论事务与否都能进行排他控制

由於Web应用程序基本上是无状态的,每个HTTP请求都是独立的,所以即使程序同时并行运行,编写起来也很简单,几乎不需要考虑这一点。但是,当涉及到存储数据时就会稍微麻烦一些。如果用户只是更改自己专用的数据,那么就没有问题。但是,如果多个人同时对相同的共享数据进行写入操作,就会产生问题。

然而,随着这个时代网页普遍使用RDB(关系型数据库),其中包含高级且隐含的事务处理和排他控制。因此,现在很少有人担心并发写入的问题。以前,人们会利用文件系统锁来实现对访问的排他控制。

嘛,现在根本没有无法断开的SQL事务,所以可以说排他控制不再需要了… 但最近出现了一些无法这样说的情况。当同时使用NoSQL时,当后台有异步作业队列时,等等。已经有了需要这些的负载情况,应用服务器已经超过2台了吧。过时的文件系统锁定在许多情况下已经不够用了。

不过放心,在Yii框架中提供了Mutex组件,可以切换并使用多种排他锁实现。

如果仅需要使用应用程序服务器的文件系统进行独占处理,就可以这样做。

return [
    // ...
    'components' => [
        'mutex' => [
            'class' => 'yii\mutex\FileMutex'
        ],
    ],
]
$mutex = \Yii::$app->get('mutex');
$timeout = 10; // sec
if ($mutex->acquire('hoge-lock'), $timeout) {
    // なにかする
    $mutex->release();
} else {
    throw new HogeException('ロック獲得に失敗しました');
}

那么,如果等待了10秒仍无法成功,就会超时并抛出异常,以实现排他控制。

如果之后想要分散服务器,则无需更改应用程序代码,只需要切换组件。

return [
    // ...
    'components' => [
        'mutex' => [
            'class' => 'yii\mutex\MySqlMutex'
            'db' => [
                'class' => 'yii\db\Connection',
                'dsn' => 'mysql:...',
                'username' => '...',
                'password' => '...',
            ],
        ],
    ],
]

使用MySQL的SELECT GET_LOCK(…)来获取锁定。如果’db’ =>部分需要与主数据库连接相同,可以写成’db’ => ‘db’,写上组件名称也没问题。

还有PostgreSQL版本,但由于无法设置pg_try_advisory_lock的超时时间,所以并不理想。

除了Yii之外,PHP的独占控制库中还有ninja-mutex。据说在其他框架中,这个库被第三方库广泛集成。

在这里做的事情基本上和Yii相同,除了文件系统和MySQL之外,还有memcache和Redis的驱动程序。Yii的组件可以让用户继承并创建各种不同的组件,所以在Yii中也可以创建使用ninja-mutex的版本。

就算是像PHP这样的无状态语言,或者是在使用PHP适合的HTTP请求的上下文中,都需要重视并发控制,所以Yii在框架中一开始就具备这种抽象类型是非常了不起的。

广告
将在 10 秒后关闭
bannerAds