本文目录导读:

在PHP项目中,权限校验往往是性能瓶颈之一,尤其是当权限规则复杂、用户量大时,缓存是提升权限校验速度的核心手段,以下是几种高效的缓存策略及实现方案:
缓存粒度与结构设计
1 按用户缓存(推荐)
// 缓存结构:user_id => [permissions]
$cacheKey = "user_permissions:{$userId}";
$permissions = $redis->get($cacheKey);
if ($permissions === null) {
// 从数据库加载权限
$permissions = $db->query("
SELECT p.code
FROM user_roles ur
JOIN role_permissions rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.id
WHERE ur.user_id = ?
", [$userId]);
// 缓存7200秒,并根据数据变动设置失效标记
$redis->setex($cacheKey, 7200, serialize($permissions));
}
2 位图缓存(极高性能)
// 预定义权限位图映射
$permissionMap = [
'create_user' => 1, // 第1位
'edit_user' => 2, // 第2位
'delete_user' => 4, // 第3位
// ... 按2的幂分配
];
// 缓存用户权限位图值
$bitmapKey = "user_bitmap:{$userId}";
$userBitmap = $redis->get($bitmapKey);
if ($userBitmap === null) {
$permissions = loadUserPermissions($userId);
$bitmap = 0;
foreach ($permissions as $perm) {
if (isset($permissionMap[$perm['code']])) {
$bitmap |= $permissionMap[$perm['code']];
}
}
$redis->setex($bitmapKey, 3600, $bitmap);
}
// 校验权限:O(1)复杂度
function checkPermission($userId, $permissionCode) {
$bitmap = $redis->get("user_bitmap:{$userId}");
return ($bitmap & $permissionMap[$permissionCode]) !== 0;
}
多级缓存架构
1 本地内存 + Redis 二级缓存
class PermissionCache {
private static $localCache = [];
public function check($userId, $permission) {
// 第一级:本地内存缓存(毫秒级)
if (isset(self::$localCache[$userId])) {
return in_array($permission, self::$localCache[$userId]);
}
// 第二级:Redis缓存
$key = "user_perms:{$userId}";
$cached = $this->redis->get($key);
if ($cached !== null) {
self::$localCache[$userId] = unserialize($cached);
return in_array($permission, self::$localCache[$userId]);
}
// 第三级:数据库查询
$permissions = $this->loadFromDB($userId);
$this->redis->setex($key, 3600, serialize($permissions));
self::$localCache[$userId] = $permissions;
return in_array($permission, $permissions);
}
}
2 使用共享内存(APCu)
// 适用于单机部署,速度极快(纳秒级)
function checkPermissionAPCu($userId, $permissionCode) {
$cacheKey = "perm:{$userId}";
if (apcu_exists($cacheKey)) {
$bitmap = apcu_fetch($cacheKey);
} else {
$bitmap = buildUserBitmap($userId);
apcu_store($cacheKey, $bitmap, 300); // 5分钟过期
}
return ($bitmap & $permissionCode) !== 0;
}
缓存失效策略
1 基于版本号的缓存失效
class VersionedPermissionCache {
// 全局版本号,权限变更时递增
private $globalVersionKey = "perm_global_version";
public function check($userId, $permission) {
$globalVersion = $this->redis->get($this->globalVersionKey);
$userCacheKey = "user_perm:{$userId}:v{$globalVersion}";
$permissions = $this->redis->get($userCacheKey);
if ($permissions === null) {
$permissions = $this->loadPermissions($userId);
$this->redis->setex($userCacheKey, 3600, serialize($permissions));
}
return in_array($permission, unserialize($permissions));
}
// 权限变更时调用
public function invalidateAll() {
$this->redis->incr($this->globalVersionKey);
}
}
2 延迟失效 + 消息队列
// 权限变更时,发送异步失效消息
class PermissionInvalidator {
public function invalidate($userId) {
// 1.立即清除Redis缓存(同步)
$this->redis->del("user_perms:{$userId}");
// 2.发送异步消息清除本地缓存(可选)
$this->messageQueue->send('clear_permission_cache', [
'user_id' => $userId,
'timestamp' => time()
]);
}
}
// 消费者进程
class CacheClearWorker {
public function process($message) {
// 清除所有服务器节点的本地缓存
apcu_delete("perm:{$message['user_id']}");
}
}
批量预加载优化
1 请求入口处批量加载
// 在中间件或框架启动时
class PermissionMiddleware {
public function handle($request, $next) {
$userId = $request->user()->id;
// 一次性加载所有权限到本地缓存
$permissions = $this->loadUserPermissions($userId);
// 存储到请求上下文
$request->attributes->set('_permissions', $permissions);
return $next($request);
}
}
// 后续校验直接使用本地数组
function checkPermission($request, $code) {
$permissions = $request->attributes->get('_permissions');
return in_array($code, $permissions);
}
2 按模块分组缓存
class ModulePermissionCache {
// 只缓存常访问的模块
private $hotModules = ['dashboard', 'user', 'order'];
public function checkModule($userId, $module) {
$key = "perm_module:{$userId}:{$module}";
// 热点模块使用长时间缓存
$ttl = in_array($module, $this->hotModules) ? 7200 : 600;
return $this->cache->remember($key, $ttl, function() use ($userId, $module) {
return $this->db->checkModulePermission($userId, $module);
});
}
}
基准测试数据
| 方案 | 平均耗时 | 每秒请求数 | 适用场景 |
|---|---|---|---|
| 无缓存(直接查库) | 50-200ms | 50-200 | 极小规模 |
| Redis(字符串) | 1-5ms | 2000-10000 | 中大规模 |
| Redis(位图) | 5-2ms | 5000-20000 | 大规模,权限固定 |
| APCu(位图) | 01-0.1ms | 50000+ | 单机部署 |
| 本地内存+Redis | 1-1ms | 10000-50000 | 高并发场景 |
生产环境最佳实践
class OptimizedPermissionService {
private $redis;
private $localCache = [];
private $useBitMap;
public function __construct() {
$this->useBitMap = count(PERMISSION_DEFINITIONS) <= 64; // 64位以内用位图
}
public function check($userId, $permissionCode) {
// 1. 本地缓存(当前请求周期)
if (isset($this->localCache[$userId])) {
return $this->checkInCache($this->localCache[$userId], $permissionCode);
}
// 2. Redis缓存
$cacheKey = $this->useBitMap
? "perm_bitmap:{$userId}"
: "perm_list:{$userId}";
$cached = $this->redis->get($cacheKey);
if ($cached !== null) {
$data = $this->useBitMap ? (int)$cached : unserialize($cached);
$this->localCache[$userId] = $data;
return $this->checkInCache($data, $permissionCode);
}
// 3. 数据库加载
$permissions = $this->loadFromDatabase($userId);
$cacheData = $this->useBitMap
? $this->buildBitmap($permissions)
: $permissions;
// 缓存并设置失效监听
$this->redis->setex($cacheKey, 3600,
$this->useBitMap ? $cacheData : serialize($cacheData));
$this->localCache[$userId] = $cacheData;
return $this->checkInCache($cacheData, $permissionCode);
}
private function checkInCache($cacheData, $permissionCode) {
if ($this->useBitMap) {
return ($cacheData & PERMISSION_MAP[$permissionCode]) !== 0;
}
return in_array($permissionCode, $cacheData);
}
}
总结建议
- 优先选择位图缓存:如果权限数量小于64个,位图是性能最优方案
- 使用多级缓存:本地内存 + Redis 组合能覆盖大部分场景
- 合理设置TTL:热点权限7200秒,普通权限3600秒,非核心模块600秒
- 异步失效:权限变更时使用消息队列异步清除缓存
- 避免缓存穿透:对空结果也进行短暂缓存(比如60秒)
- 监控与告警:监控缓存命中率,低于90%时检查配置
根据实际业务场景选择合适的方案,通常位图缓存+Redis+本地内存的组合能满足99%的性能需求。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播