hyperf/docs/zh-hk/db/model-cache.md
李铭昕 c224ba6922
v2.0.4 (#2185)
* v2.0.4

* Translate

Co-authored-by: huangzhhui <huangzhwork@gmail.com>
2020-07-27 15:52:51 +08:00

193 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 模型緩存
在高頻的業務場景下,我們可能會頻繁的查詢數據庫獲取業務數據,雖然有主鍵索引的加持,但也不可避免的對數據庫性能造成了極大的考驗。而對於這種 kv 的查詢方式,我們可以很方便的通過使用 `模型緩存` 來減緩數據庫的壓力。本組件實現了 Model 數據自動緩存的功能,且當刪除和修改模型數據時,自動刪除和修改對應的緩存。執行累加、累減操作時,緩存數據自動進行對應累加、累減變更。
> 模型緩存暫時只支持 `Redis` 存儲驅動,其他存儲引擎歡迎社區提交對應的實現。
## 安裝
```bash
composer require hyperf/model-cache
```
## 配置
模型緩存的配置默認存放在 `config/autoload/databases.php` 中。配置的屬性如下:
| 配置 | 類型 | 默認值 | 備註 |
|:-----------------:|:------:|:---------------------------------------------:|:---------------------------------------:|
| handler | string | Hyperf\ModelCache\Handler\RedisHandler::class | 無 |
| cache_key | string | `mc:%s:m:%s:%s:%s` | `mc:緩存前綴:m:表名:主鍵 KEY:主鍵值` |
| prefix | string | db connection name | 緩存前綴 |
| pool | string | default | 緩存池 |
| ttl | int | 3600 | 超時時間 |
| empty_model_ttl | int | 60 | 查詢不到數據時的超時時間 |
| load_script | bool | true | Redis 引擎下 是否使用 evalSha 代替 eval |
| use_default_value | bool | false | 是否使用數據庫默認值 |
```php
<?php
return [
'default' => [
'driver' => env('DB_DRIVER', 'mysql'),
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'hyperf'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
'prefix' => env('DB_PREFIX', ''),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => (float)env('DB_MAX_IDLE_TIME', 60),
],
'cache' => [
'handler' => \Hyperf\ModelCache\Handler\RedisHandler::class,
'cache_key' => 'mc:%s:m:%s:%s:%s',
'prefix' => 'default',
'ttl' => 3600 * 24,
'empty_model_ttl' => 3600,
'load_script' => true,
'use_default_value' => false,
]
],
];
```
## 使用
模型緩存的使用十分簡單,只需要在對應 Model 中實現 `Hyperf\ModelCache\CacheableInterface` 接口,當然,框架已經提供了對應實現,只需要引入 `Hyperf\ModelCache\Cacheable` Trait 即可。
```php
<?php
declare(strict_types=1);
namespace App\Models;
use Hyperf\DbConnection\Model\Model;
use Hyperf\ModelCache\Cacheable;
use Hyperf\ModelCache\CacheableInterface;
/**
* @property $id
* @property $name
* @property $gender
* @property $created_at
* @property $updated_at
*/
class User extends Model implements CacheableInterface
{
use Cacheable;
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'user';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['id', 'name', 'gender', 'created_at', 'updated_at'];
protected $casts = ['id' => 'integer', 'gender' => 'integer'];
}
// 查詢單個緩存
/** @var int|string $id */
$model = User::findFromCache($id);
// 批量查詢緩存,返回 Hyperf\Database\Model\Collection
/** @var array $ids */
$models = User::findManyFromCache($ids);
```
對應 Redis 數據如下,其中 `HF-DATA:DEFAULT` 作為佔位符存在於 `HASH` 中,*所以用户不要使用 `HF-DATA` 作為數據庫字段*。
```
127.0.0.1:6379> hgetall "mc:default:m:user:id:1"
1) "id"
2) "1"
3) "name"
4) "Hyperf"
5) "gender"
6) "1"
7) "created_at"
8) "2018-01-01 00:00:00"
9) "updated_at"
10) "2018-01-01 00:00:00"
11) "HF-DATA"
12) "DEFAULT"
```
另外一點需要注意的就是,緩存的更新機制,框架內實現了對應的 `Hyperf\ModelCache\Listener\DeleteCacheListener` 監聽器,每當數據修改時,框架會主動刪除對應的緩存數據。
如果您不希望由框架來自動刪除對應的緩存,可以通過主動覆寫 Model 的 `deleteCache` 方法,然後自行實現對應監聽即可。
### 批量修改或刪除
`Hyperf\ModelCache\Cacheable` 會自動接管 `Model::query` 方法,只需要用户通過以下方式進行數據的刪除,就可以自動清理對應的緩存數據。
```php
<?php
// 從數據庫刪除用户數據,框架會自動刪除對應的緩存數據
User::query(true)->where('gender', '>', 1)->delete();
```
### 使用默認值
當生產環境使用了模型緩存時,如果已經建立了對應緩存數據,但此時又因為邏輯變更,添加了新的字段,並且默認值不是 `0`、`空字符`、`null` 這類數據時,就會導致在數據查詢時,從緩存中查出來的數據與數據庫中的數據不一致。
對於這種情況,我們可以修改 `use_default_value``true`,並添加 `Hyperf\DbConnection\Listener\InitTableCollectorListener``listener.php` 配置中,使 Hyperf 應用在啟動時主動去獲取數據庫的字段信息,並在獲取緩存數據時與之比較並進行緩存數據修正。
### EagerLoad
當我們使用模型關係時,可以通過 `load` 解決 `N+1` 的問題,但仍然需要查一次數據庫。模型緩存通過重寫了 `ModelBuilder`,可以讓用户儘可能的從緩存中拿到對應的模型。
> 本功能不支持 `morphTo` 和不是隻有 `whereIn` 查詢的關係模型。
以下提供兩種方式:
1. 配置 EagerLoadListener直接使用 `loadCache` 方法。
修改 `listeners.php` 配置
```php
return [
Hyperf\ModelCache\Listener\EagerLoadListener::class,
];
```
通過 `loadCache` 方法,加載對應的模型關係。
```php
$books = Book::findManyFromCache([1,2,3]);
$books->loadCache(['user']);
foreach ($books as $book){
var_dump($book->user);
}
```
2. 使用 EagerLoader
```php
use Hyperf\ModelCache\EagerLoad\EagerLoader;
use Hyperf\Utils\ApplicationContext;
$books = Book::findManyFromCache([1,2,3]);
$loader = ApplicationContext::getContainer()->get(EagerLoader::class);
$loader->load($books, ['user']);
foreach ($books as $book){
var_dump($book->user);
}
```