4

我想知道在我的网络应用程序中使我的 Memcache 操作 atmoic 的最佳方法是什么。

考虑以下场景:

Client 1 connects and retrieves data from key 1
Client 2 connects a few microsecond after Client 1, requests the same data from key 1
Client 1 saves new data to key 1
Client 2 saves new (different data) to key 1, not taking into account that Client 1 modified the value already

在这种情况下,进程中没有原子性。

我的(潜在的)解决方案是在我的应用程序中设置、获取和释放键上的锁。

因此,在我实施之后,上述过程将像这样工作:

Client 1 connects, checks for an active lock on key 1, finds none, and gets the data
Client 2 connects a few microsecond after Client 1, requests the same data from key 1, but finds a lock
Client 2 enters a retry loop until Client 1 releases the lock
Client 1 saves new data to key 1, releases the lock
Client 2 gets the fresh data, sets a lock on key 1, and continues

想法?这种方法会奏效吗?我应该提防任何性能问题吗?

4

2 回答 2

7

考虑一下您要在这里解决的问题。你:

  1. 只是想避免丢失更新问题。(例如,使用递增一的计数器。)
  2. 或者您确实需要确保一个客户端检索到一个项目的值时,没有其他客户端可以使用该值?(这是价值代表有限资源的地方。)

大多数时候人们只想要(1)。如果这就是你想要的,你可以使用检查和设置语义Memcached::cas(),或者如果你有一个简单的整数值,你可以使用原子Memcached::increment()Memcached::decrement()操作。

但是,如果您需要使用密钥来表示有限资源 (2),请考虑使用一组不同的语义:

$keyname = 'key_with_known_name_representing_finite_resource';

// try to "acquire" the key with add().
// If the key exists already (resource taken), operation will fail
// otherwise, we use it then release it with delete()
// specify a timeout to avoid a deadlock.
// timeout should be <= php's max_execution_time
if ($mcache->add($keyname, '', 60)) {
   // resource acquired
   // ...do stuff....
   // now release
   $mcache->delete($keyname);
} else {
   // try again?
}

如果由于某种原因您无权访问cas(),您可以使用两个键和add()/delete

$key = 'lockable_key_name';
$lockkey = $key.'##LOCK';

if ($mcache->add($lockkey, '', 60)) { // acquire
    $storedvalue = $mcache->get($key);
    // do something with $storedvalue
    $mcache->set($key, $newvalue);
    // release
    $mcache->delete($lockkey);
}

与使用检查和设置方法相比,这种方法会导致更多的资源争用。

于 2012-05-16T21:49:05.557 回答
2

有一个内置功能可以为您解决这个问题。您正在寻找的是 CAS(检查并设置)。

“cas”是一个检查和设置操作,意思是“存储这些数据,但前提是自从我上次获取它以来没有其他人更新。” 1

当您尝试存储先前已由另一个进程更新的数据时,调用set将失败,因此您可以决定是否需要重新获取数据、无论如何存储或退出。

有关更多信息,请参阅Memcached::cas()

希望有帮助。

于 2012-05-16T21:30:31.110 回答