缓存时PHPer优化所必备的一个知识点
配置
首先,修改.env
文件配置缓存的驱动方式
1 2
| // 缓存驱动方式:apc, array, database, file, memcached, redis CACHE_DRIVER=memcached
|
apc
: APC驱动,APC是PHP的一个扩展,其目标是为缓存和优化PHP中间码(opcode)提供一个免费、开源、健壮的框架
array
: 数组驱动,往往仅仅用于测试,好处是不会持久化,只会在一次PHP脚本执行的生命周期内有效
database
: 数据库驱动,需参考下面代码在数据库中建立缓存表,并设置CACHE_DATABASE_TABLE
和CACHE_DATABASE_CONNECTION
1 2 3 4 5
| Schema::create('cache', function($table) { $table->string('key')->unique(); $table->text('value'); $table->integer('expiration'); });
|
file
: 文件驱动,系统将生成缓存文件储存在/storage/framework/cache
文件夹里,所以,777权限是必须的
memcached
: Memcached
驱动,使用之前需要先在系统中安装Memcached
,并设置MEMCACHED_HOST和MEMCACHED_PORT
redis
: Redis
驱动,使用之前需要先在系统中安装Redis
,并设置CACHE_REDIS_CONNECTION
众多驱动方式中,array
不是持久缓存,database
需要读取数据库,file
需要读取文件,apc/memcached/redis
是基于内存的,所以…选择适合自己的
使用
缓存需要Facade
的支持,否则会提示Class 'Cache' not found
错误,所以,请检查/bootstrap/app.php
,确认$app->withFacades();
的注释已去掉
Lumen已经默认去掉注释
无论选用任何驱动方式,缓存的使用方式是统一的,请看以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| use Cache; ...
$id = 5;
$result = Cache::get('UserData_'.$id); if($result){ return $result; }
$result = User::findOrFail($id);
Cache::put('UserData_'.$id, $result, 60);
|
接下来具体说一下Memcached
和Redis
Memcached
Memcache
与 Memcached
Memcache:
是一个自由和开放源代码、高性能、分配的内存对象缓存系统。用于加速动态web应用程序,减轻数据库负载。它可以应对任意多个连接,使用非阻塞的网络IO。由于它的工作机制是在内存中开辟一块空间,然后建立一个Hash表,Memcached自管理这些Hash表。
Memcached:
Memcache是该系统的项目名称,Memcached是该系统的主程序文件(字母d可以理解为daemon),以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,使用共享内存存取数据。
PHP 有两个 Memcached 客户端:“PHP Memcache 扩展” 和 “PHP Memcached 扩展”,这就是是我们搞混的地方。
必须同时安装服务端和客户端
如果安装了 Memcached
服务端不安装扩展,那么 PHP
无法操控 Memcached
。
同样如果安装了 PHP Memcached
扩展(PHP Memcache 和 PHP memcached 两者选择一个),但是没有安装 Memcached
服务端,那么这个就无法使用。
只有同时安装了 Memcached
服务端和 PHP 客户端扩展才可以提高动态网站性能。
具体应用
- 确保
PHP
安装并引入了 memcached
扩展(可能要重启 php-fpm);确保 Memcached
已正常运行(略)
本地测试
windows下安装PHP7.0/7.1的Memcache扩展: nono303/PHP7-memcache-dll
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php
$memcache = new Memcache; $memcache->connect('localhost', 11211) or die ("Could not connect");
$version = $memcache->getVersion(); echo "Memcache Server's version: ".$version."<br/>\n";
$tmp_object = new stdClass; $tmp_object->str_attr = 'test'; $tmp_object->int_attr = 123;
$memcache->set('key', $tmp_object, false, 10) or die ("Failed to save data at the server");
echo "Store data in the cache (data will expire in 10 seconds)<br/>\n";
$get_result = $memcache->get('key'); echo "Data from the cache:<br/>\n";
var_dump($get_result);
|
在Lumen
中应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
return [ 'default' => 'memcached',
'stores' => [ 'memcached' => [ 'driver' => 'memcached', 'servers' => [ [ 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 'port' => env('MEMCACHED_PORT', 11211), 'weight' => 100, ], ], ], ], ];
|
1 2
| CACHE_DRIVER=memcached MEMCACHED_HOST=172.17.0.11
|
bootstrap/app.php
中引入配置和 memcached.connector
:
1 2 3 4 5 6
| $app->configure('cache');
// !!! 不配会坑 $app->singleton( 'memcached.connector', Illuminate\Cache\MemcachedConnector::class );
|
在Laravel
中重新封装
app/Contract/Service/Cachable.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php
namespace App\Contract\Service;
interface Cachable { public function get(string $key) : string;
public function set(string $key, string $value, int $seconds) : Cachable;
public function flush(string $key) : string;
public function delete(string $key) : bool;
public function incrementToday(string $key, int $step = 1) : int; }
|
app/Service/Cache/Laravel.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| <?php
namespace App\Services\Cache;
use Illuminate\Support\Facades\Cache; use App\Contract\Service\Cachable;
class Laravel implements Cachable { public function get(string $key): string { return (string) Cache::get($key); }
public function set(string $key, string $value, int $seconds): Cachable { Cache::put($key, $value, ($seconds/60));
return $this; }
public function flush(string $key): string { $value = null;
if (Cache::has($key)) { $value = Cache::get($key); Cache::forget($key); }
return (string) $value; }
public function delete(string $key): bool { if (Cache::has($key)) { return Cache::forget($key); }
return true; } public function incrementToday(string $key, int $step = 1): int { $times = intval(Cache::get($key)); $times += $step; $minutes = ($this->seconds_left_today() / 60);
Cache::put($key, $times, $minutes);
return $times; }
private function seconds_left_today() : int { $tomorrow = strtotime(date('Y-m-d', strtotime('+1 day')));
return $tomorrow - time(); } }
|
注意:这里如果不事先加载单例 MemcachedConnector
,则会报如下错误:
lumen.ERROR: ReflectionException: Class memcached.connector does not exist in /data/www/vendor/illuminate/container/Container.php:729`
生产环境一般开启了 autoload 等缓存了的,为确保正确加载,部署时要清理下缓存先:
1 2 3 4 5 6
| php artisan config:clear composer dump-autoload
# Lumen 默认没有以下命令 # php artisan optimize # php artisan clear-compiled
|
memcached
默认的过期时间则 不能大于 30
天!
Redis
1 2
| composer require illuminate/redis composer require predis/predis
|
1 2 3 4 5 6
| "require": { "php": ">=7.1.3", "illuminate/redis": "5.5.*", "laravel/lumen-framework": "5.5.*", "predis/predis": "^1.1", },
|
1
| $app->register(Illuminate\Redis\RedisServiceProvider::class);
|
1
| use Illuminate\Support\Facades\Redis;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <?php
namespace App\Traits;
class Redis { public function set($key, $val) { Redis::set($key, $val); }
public function setex($key, $exp, $val) { Redis::setex($key, $exp, $val); }
public function get($key) { return Redis::get($key); }
public function expire($key, $exp = 180) { Redis::expire($key, $exp); }
public function incr($key) { Redis::incr($key); }
public function del($key) { Redis::del($key); }
public function exists($key) { return Redis::exists($key); } }
|
参考