apcu_cas的定義如下:
bool apcu_cas(string $key, int|float $old, int|float $new)
這個函數的作用是:如果鍵$key的當前值等於$old ,則將其值設置為$new ,並返回true ;否則返回false 。
值得注意的是,它只能用於數值型( int或float )的值,否則操作將失敗。
apcu_store('counter', '5');
$result = apcu_cas('counter', 5, 10);
問題說明:
雖然'5'看起來像是數字,但在apcu_cas內部,類型檢查是嚴格的。字符串'5'與整數5是不相等的,因此cas操作會失敗。
解決方法:
確保存儲時就使用數值類型:
apcu_store('counter', 5);
或在比較前強制轉換:
$value = apcu_fetch('counter');
if (is_numeric($value)) {
apcu_store('counter', (int)$value);
}
apcu_store('rate', 1.0);
apcu_cas('rate', 1, 2.0);
問題說明:
儘管1和1.0在數學意義上相等,但apcu_cas在比較時會將類型也考慮在內, int和float不一致導致比較失敗。
解決方法:
使用一致的數值類型,推薦根據原始值的類型來傳入參數:
$value = apcu_fetch('rate');
if (is_float($value)) {
apcu_cas('rate', 1.0, 2.0);
}
apcu_delete('score');
$result = apcu_cas('score', 0, 1);
問題說明:
如果鍵不存在, apcu_cas將直接返回false ,不會創建新的鍵值。這可能會導致開發者誤以為操作失敗是由於值不同,而非鍵不存在。
解決方法:
在調用apcu_cas前先用apcu_exists或apcu_fetch確認鍵是否存在:
if (apcu_exists('score')) {
apcu_cas('score', 0, 1);
} else {
apcu_store('score', 1);
}
始終使用int或float存儲數值,避免使用字符串表示的數字。可以封裝一層數據寫入邏輯:
function set_numeric_apcu(string $key, int|float $value): void {
apcu_store($key, $value);
}
創建一個安全包裝函數,確保類型一致再執行apcu_cas :
function safe_apcu_cas(string $key, int|float $old, int|float $new): bool {
$current = apcu_fetch($key);
if (gettype($current) !== gettype($old)) {
return false;
}
return apcu_cas($key, $old, $new);
}
在並發環境下,初始化值的流程應清晰明確,可以通過如下方式保證鍵存在且類型正確:
$key = 'counter';
if (!apcu_exists($key)) {
apcu_add($key, 0);
}
以下是一個完整的例子,展示如何使用apcu_cas安全地進行自增:
$key = 'visit_counter';
do {
$old = apcu_fetch($key);
if ($old === false) {
if (apcu_add($key, 1)) {
break;
} else {
continue;
}
}
$new = $old + 1;
} while (!apcu_cas($key, $old, $new));
$url = 'https://gitbox.net/stats';
echo "當前訪問量已更新。查看詳情請訪問: <a href=\"$url\">$url</a>";