perf: V0.9.1-0.9.3 性能优化(count/featured 全表→聚合、请求内缓存、二级索引、PSI 未读铃铛)
- V0.9.1 (P0): Model::count() 改 SELECT COUNT(*);新增请求内 identity-map 缓存; Product::featured() 改 WHERE+ LIMIT 避免全表 - V0.9.2 (P1): schema.sql 内联二级索引 + install/upgrades/009_perf_indexes.sql 幂等迁移 - V0.9.3 (P1): psi_unread_events() 改 COUNT + JSON_CONTAINS,消除每页 all()+循环
This commit is contained in:
+70
-4
@@ -11,6 +11,28 @@ class Model
|
||||
protected $pk = 'id';
|
||||
protected $orderBy = 'id';
|
||||
|
||||
/* ---------- 请求内缓存(同一请求内重复读取同一查询只走一次 DB/文件) ---------- */
|
||||
private static $reqCache = [];
|
||||
|
||||
private function cacheKey(string $method, ...$args): string
|
||||
{
|
||||
return $this->table . '|' . $method . '|' . md5(serialize($args));
|
||||
}
|
||||
/** 读取缓存;未命中返回 null(用 array_key_exists 区分「未缓存」与「缓存了空数组」) */
|
||||
private function cacheGet(string $k)
|
||||
{
|
||||
return array_key_exists($k, self::$reqCache) ? self::$reqCache[$k] : null;
|
||||
}
|
||||
private function cachePut(string $k, $v): void
|
||||
{
|
||||
self::$reqCache[$k] = $v;
|
||||
}
|
||||
/** 任意写操作后清空请求内缓存,避免同请求内读到脏数据(CMS 读多写少,刷新成本可忽略) */
|
||||
private function cacheFlush(): void
|
||||
{
|
||||
self::$reqCache = [];
|
||||
}
|
||||
|
||||
/* ---------- 文件模式 ---------- */
|
||||
private function file(): string
|
||||
{
|
||||
@@ -71,10 +93,16 @@ class Model
|
||||
/* ---------- 通用 CRUD ---------- */
|
||||
public function all(): array
|
||||
{
|
||||
$k = $this->cacheKey('all');
|
||||
$hit = $this->cacheGet($k);
|
||||
if ($hit !== null) return $hit;
|
||||
if (Db::driver() === 'mysql') {
|
||||
return Db::query("SELECT * FROM `{$this->table}` ORDER BY `{$this->orderBy}` ASC")->fetchAll();
|
||||
$rows = Db::query("SELECT * FROM `{$this->table}` ORDER BY `{$this->orderBy}` ASC")->fetchAll();
|
||||
} else {
|
||||
$rows = $this->read(); $this->sort($rows);
|
||||
}
|
||||
$rows = $this->read(); $this->sort($rows); return $rows;
|
||||
$this->cachePut($k, $rows);
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function find($id)
|
||||
@@ -106,6 +134,7 @@ class Model
|
||||
|
||||
public function insert(array $data)
|
||||
{
|
||||
$this->cacheFlush();
|
||||
if (Db::driver() === 'mysql') {
|
||||
$cols = array_keys($data);
|
||||
$sql = "INSERT INTO `{$this->table}` (`" . implode('`,`', $cols) . "`) VALUES (" . implode(',', array_fill(0, count($cols), '?')) . ")";
|
||||
@@ -121,6 +150,7 @@ class Model
|
||||
|
||||
public function update($id, array $data): void
|
||||
{
|
||||
$this->cacheFlush();
|
||||
if (Db::driver() === 'mysql') {
|
||||
$cols = array_keys($data);
|
||||
$sql = "UPDATE `{$this->table}` SET `" . implode('`=?,`', $cols) . "`=? WHERE `{$this->pk}`=?";
|
||||
@@ -136,6 +166,7 @@ class Model
|
||||
|
||||
public function delete($id): void
|
||||
{
|
||||
$this->cacheFlush();
|
||||
if (Db::driver() === 'mysql') {
|
||||
Db::query("DELETE FROM `{$this->table}` WHERE `{$this->pk}`=?", [$id]);
|
||||
return;
|
||||
@@ -147,6 +178,7 @@ class Model
|
||||
/** 按任意列批量删除(用于主从表级联删除从表) */
|
||||
public function deleteRaw(string $col, $val): void
|
||||
{
|
||||
$this->cacheFlush();
|
||||
if (Db::driver() === 'mysql') {
|
||||
Db::query("DELETE FROM `{$this->table}` WHERE `{$col}`=?", [$val]);
|
||||
return;
|
||||
@@ -155,8 +187,42 @@ class Model
|
||||
$this->write(array_values($rows));
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
/**
|
||||
* 计数:MySQL 下走 COUNT(*) 聚合(避免 SELECT * 全表拉回再 count);
|
||||
* 可选按某列过滤。文件模式下回退为本地计数。
|
||||
* @param string|null $col 过滤列(null 表示全表计数)
|
||||
* @param mixed $val 过滤值
|
||||
*/
|
||||
public function count(string $col = null, $val = null): int
|
||||
{
|
||||
return count($this->all());
|
||||
if (Db::driver() === 'mysql') {
|
||||
if ($col === null) {
|
||||
return (int) Db::query("SELECT COUNT(*) FROM `{$this->table}`")->fetchColumn();
|
||||
}
|
||||
return (int) Db::query("SELECT COUNT(*) FROM `{$this->table}` WHERE `{$col}`=?", [$val])->fetchColumn();
|
||||
}
|
||||
$rows = $this->all();
|
||||
if ($col === null) return count($rows);
|
||||
$n = 0;
|
||||
foreach ($rows as $r) {
|
||||
if (($r[$col] ?? null) == $val) $n++;
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取某列等于 $val 的前 $limit 条。
|
||||
* MySQL 下带 LIMIT(索引友好);文件模式用 whereAll + array_slice 兜底。
|
||||
* 用于首页精选 / 列表分页等高频只读场景。
|
||||
*/
|
||||
public function whereLimit(string $col, $val, int $limit): array
|
||||
{
|
||||
if (Db::driver() === 'mysql') {
|
||||
return Db::query(
|
||||
"SELECT * FROM `{$this->table}` WHERE `{$col}`=? ORDER BY `{$this->orderBy}` ASC LIMIT ?",
|
||||
[$val, $limit]
|
||||
)->fetchAll();
|
||||
}
|
||||
return array_slice($this->whereAll($col, $val), 0, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user