2026-08-31

PHP / Laravel / 后端 / MySQL / Redis / API / 老项目维护,我建议不要装一大堆 Skills,精选 6~10 个最实用的就够了

Cursor 现在原生支持 Agent Skills,Skill 本质上是 SKILL.md + 可选 references/scripts,Agent 会根据任务自动加载;也可以在 Agent 里通过 / 调用。(Cursor)

最推荐你的 PHP 技能组合

① Laravel Best Practices —— 必装 ⭐⭐⭐⭐⭐

这个最适合你。

目前有一个比较成熟的 Laravel Skill:

agent-laravel-skills

它主要解决:

  • Controller / Service / Model 分层
  • Eloquent
  • FormRequest
  • Policy / Authorization
  • Validation
  • Migration
  • Queue
  • Cache
  • N+1
  • SQL 性能
  • Security
  • Laravel 架构
  • 重构

而且明确支持 Cursor / Codex / Claude Code。(GitHub)

安装:

npx skills add PauloFelipeM/agent-laravel-skills -a cursor

② Laravel/PHP Troubleshooting —— 非常适合你 ⭐⭐⭐⭐⭐

你平时经常遇到:

PHP-FPM
Nginx
Laravel
MySQL
Redis
Docker
权限
队列
数据库
线上异常

我反而非常推荐这种 troubleshooting skill

有一个 Laravel/PHP Skills 项目专门包含:

troubleshooting
troubleshooting-backend
unexpected-situations

其中 troubleshooting-backend 就是针对 Laravel/PHP 后端错误排查。(GitHub)

Laravel/PHP Agent Skills


③ TDD / Pest PHP ⭐⭐⭐⭐

如果你让 Cursor 给 Laravel 项目补测试,这个非常有用。

尤其是:

Service
Repository
Controller
Job
Command
API

可以让 Agent 按:

先写测试
↓
实现
↓
运行 Pest
↓
修复
↓
再次测试

而不是直接给你生成一堆看起来正确、实际上没跑过的代码。

上面的 ceilidhboy/skills 就包含:

tdd-laravel

专门针对 Pest PHP。(GitHub)


④ Laravel Queue ⭐⭐⭐⭐⭐

这个我特别推荐你

因为你做后端,经常会遇到:

Redis Queue
Job
队列并发
失败重试
延迟任务
Batch
Horizon

现在有一个专门的 Laravel AI Skills 项目,把:

Laravel Queues
Laravel Horizon

单独做成 Skills,并且针对 Laravel 11~13。(GitHub)

安装对应 Skill 后,你可以直接告诉 Cursor:

把这个任务改成 Laravel Queue Job,
要求支持失败重试、超时、幂等。

Agent 会更容易按照 Laravel 的正确方式实现。


⑤ Laravel Horizon ⭐⭐⭐⭐⭐

如果你的项目使用:

Redis
Laravel Queue
Horizon

建议和 Queue 一起装。

尤其是生产环境:

supervisor
balance
worker
timeout
retry
failed_jobs

这些东西让普通 AI 很容易胡写。

专门的 Horizon Skill 会比单纯问 Cursor:

“帮我配置 Horizon”

靠谱很多。(GitHub)


⑥ Laravel Data / DTO ⭐⭐⭐⭐

如果你的项目 API 比较多,我推荐。

例如:

UserData
OrderData
ProductData
CreateOrderData
UpdateOrderData

让 Cursor 不要所有东西都:

$request->all()

然后 Controller 里面写一大堆业务逻辑。

目前有专门针对 spatie/laravel-data 的 Skill。(GitHub)


⑦ Laravel Security ⭐⭐⭐⭐⭐

这个我建议你一定加

特别是你这种后端项目,AI 很容易写出:

Model::where('id', $request->id)->first();

然后直接返回。

或者:

$request->all()

批量赋值。

或者:

DB::raw(...)

等等。

Security Skill 可以让 Agent 在:

Authentication
Authorization
Validation
Mass Assignment
SQL Injection
IDOR
权限
敏感数据
API

这些方面多一层检查。

目前也有 Laravel Skills 项目专门把 Security、Testing、Performance、API 等作为不同 preset。(GitHub)


⑧ Laravel Performance ⭐⭐⭐⭐⭐

这个对你尤其有价值。

你以前经常处理:

MySQL
Redis
高并发
SQL
ETL
批处理

所以建议增加:

N+1
Eloquent
SQL
Index
Cache
Queue
Memory

这一类 Skill。

例如官方开源项目里的 Laravel Best Practices Skill,就明确包含:

N+1、query performance、caching、authorization、validation、queue 等检查。(GitHub)

⑨ Git / Code Review ⭐⭐⭐⭐⭐

这个虽然不是 PHP 专属,但是我认为比很多 PHP Skill 都重要

Cursor 官方 Marketplace 目前已经提供类似:

/code-review
/test
/fix-merge-conflicts
/pr

这样的技能/命令。(Cursor)

你的工作流可以变成:

Cursor
  ↓
写代码
  ↓
/test
  ↓
/code-review
  ↓
修复
  ↓
/pr

这个比单纯:

“帮我写 PHP”

强很多。


我给你配一套「PHP 后端开发套餐」

如果是你的开发场景,我会这样装:

                    Cursor
                       │
       ┌───────────────┼───────────────┐
       ↓               ↓               ↓
 Laravel Best      Troubleshooting    Code Review
 Practices
       │
       ├── Security
       ├── Performance
       ├── Testing / Pest
       ├── Queue
       ├── Horizon
       └── DTO / Laravel Data

第一批先装这 6 个

Skill推荐度用途
Laravel Best Practices⭐⭐⭐⭐⭐Laravel 日常开发
Troubleshooting Backend⭐⭐⭐⭐⭐排错
Security⭐⭐⭐⭐⭐安全
Performance⭐⭐⭐⭐⭐SQL/Eloquent/缓存
TDD / Pest⭐⭐⭐⭐自动化测试
Queue / Horizon⭐⭐⭐⭐⭐Redis 队列

还有一个我特别建议你装

Superpowers ⭐⭐⭐⭐⭐

Cursor 官方 Marketplace 已经有经过 Cursor 验证的 Superpowers Skill 集合。里面包括:

brainstorming
dispatching-parallel-agents
executing-plans
finishing-a-development-branch
receiving-code-review

等等。(Cursor)

这个不是 PHP 专用,但是对于你这种经常让 AI 改后端项目的人非常有用。

例如你说:

给这个 Laravel 项目增加订单批量导入。

普通 AI 很可能:

直接开始改代码

Superpowers 更倾向于:

分析需求
   ↓
理解项目
   ↓
制定方案
   ↓
拆任务
   ↓
实现
   ↓
测试
   ↓
Review

对于老 PHP 项目尤其有价值。


如果是我帮你配置 Cursor

我不会安装几十个 Skill。

我会给你配置:

① Laravel Best Practices
② PHP/Laravel Troubleshooting
③ Security
④ Performance
⑤ Pest/TDD
⑥ Queue
⑦ Horizon
⑧ Superpowers
⑨ Code Review

然后再给你的项目写一个:

.cursor/rules/php-backend.mdc

专门规定:

PHP 8.x
Laravel
MySQL
Redis
Nginx
PHP-FPM

Controller 不写业务
Service 处理业务
Repository 是否使用由项目决定
禁止无意义 Repository
所有 SQL 注意 N+1
大数据量必须考虑 chunk/cursor
Redis Key 必须统一规范
Queue 必须考虑幂等
API 必须验证参数
禁止 $request->all() 直接入库

这一层实际上比装 20 个 Skill 更重要。

2026-08-31

这套配置的目标是让 Cursor 不只是“会写 Yii2”,而是按后端工程师的方式分析、修改、测试和 Review Yii2 项目。

Cursor + PHP + Yii2 Backend Skill

目录结构

.cursor/
├── rules/
│   ├── php.mdc
│   ├── yii2.mdc
│   ├── mysql.mdc
│   ├── redis.mdc
│   ├── api.mdc
│   ├── security.mdc
│   └── testing.mdc
│
└── skills/
    └── yii2-backend/
        ├── SKILL.md
        └── references/
            ├── architecture.md
            ├── database.md
            └── review.md

1. .cursor/rules/php.mdc

---
description: PHP backend development standards
globs:
  - "**/*.php"
alwaysApply: true
---

# PHP Development Rules

## PHP Version

Before writing new PHP code:

1. Inspect composer.json.
2. Determine the project's PHP version.
3. Follow the project's existing PHP syntax and conventions.
4. Do not introduce syntax unsupported by the project's PHP version.

## Type Safety

Prefer:

- strict parameter types
- return types
- nullable types
- typed properties
- enums when supported by the project

Avoid unnecessary mixed values.

Example:

GOOD:

```php
public function getUser(int $id): ?User
{
    return User::findOne($id);
}

Avoid:

public function getUser($id)
{
    return User::findOne($id);
}

unless the existing project intentionally follows weak typing.

Error Handling

Do not silently swallow exceptions.

Bad:

try {
    // ...
} catch (\Throwable $e) {
}

If an exception must be caught:

  • log it
  • add meaningful context
  • either recover or rethrow

Null Handling

Do not assume database queries always return records.

Bad:

$user = User::findOne($id);

return $user->name;

Prefer:

$user = User::findOne($id);

if ($user === null) {
    throw new NotFoundHttpException('User not found.');
}

Code Duplication

Before adding a new helper:

  1. Search the project.
  2. Check whether the functionality already exists.
  3. Reuse existing abstractions when appropriate.

Do not create unnecessary utility classes.

Backward Compatibility

When modifying existing code:

  • preserve existing public method signatures when possible
  • preserve API response formats
  • preserve database behavior
  • avoid unnecessary refactoring
  • avoid changing unrelated files

Dependency Policy

Do not add Composer packages automatically.

Before adding a dependency:

  1. Check whether the project already has equivalent functionality.
  2. Check composer.json.
  3. Explain why the dependency is necessary.
  4. Prefer Yii2/PHP standard functionality when sufficient.

---

# 2. `.cursor/rules/yii2.mdc`

```md
---
description: Yii2 framework architecture and development rules
globs:
  - "**/*.php"
alwaysApply: true
---

# Yii2 Development Rules

## Architecture

Prefer:

Controller
    ↓
Service
    ↓
Model / ActiveRecord
    ↓
Database

Controllers should remain thin.

Controllers are responsible for:

- receiving HTTP parameters
- authentication
- authorization
- request validation
- calling services
- formatting responses

Controllers should NOT contain complex business logic.

## Controller

Avoid:

```php
public function actionCreate()
{
    $model = new Order();

    if ($model->load(Yii::$app->request->post())) {
        // hundreds of lines of business logic
    }
}

Prefer:

public function actionCreate()
{
    $params = Yii::$app->request->post();

    $order = $this->orderService->create($params);

    return $order;
}

Service

Use services for:

  • business workflows
  • transactions
  • cross-model operations
  • external API calls
  • complex business rules
  • queue dispatch
  • multi-step operations

Example:

final class OrderService
{
    public function create(array $params): Order
    {
        return Yii::$app->db->transaction(function () use ($params) {
            // business logic
        });
    }
}

ActiveRecord

Use ActiveRecord for:

  • entity persistence
  • simple queries
  • relationships
  • validation

Avoid putting large business workflows inside ActiveRecord.

ActiveQuery

Prefer readable queries:

Order::find()
    ->where(['status' => Order::STATUS_PENDING])
    ->orderBy(['id' => SORT_DESC])
    ->all();

N+1 Prevention

Always check for N+1 when using relations.

Potentially dangerous:

$orders = Order::find()->all();

foreach ($orders as $order) {
    echo $order->user->name;
}

Prefer:

$orders = Order::find()
    ->with('user')
    ->all();

When modifying relationship-heavy code, inspect the generated SQL if necessary.

Large Dataset

Never blindly use:

Model::find()->all();

for potentially large datasets.

Prefer:

foreach (
    Model::find()->batch(500) as $models
) {
    foreach ($models as $model) {
        // ...
    }
}

or:

foreach (
    Model::find()->each(500) as $model
) {
    // ...
}

updateAll/deleteAll

For large bulk operations consider:

Model::updateAll(
    ['status' => Model::STATUS_DONE],
    ['status' => Model::STATUS_PENDING]
);

and:

Model::deleteAll([
    'status' => Model::STATUS_DELETED,
]);

But check:

  • events
  • behaviors
  • timestamps
  • business rules

because bulk operations bypass normal ActiveRecord lifecycle behavior.

Transactions

Use transactions when multiple writes must succeed or fail together.

Yii::$app->db->transaction(function () {
    // write A
    // write B
});

Do not create unnecessary long transactions.

Do not hold database transactions while waiting for:

  • HTTP requests
  • external APIs
  • long-running jobs
  • user input

Validation

Never trust HTTP input.

Use:

  • rules()
  • scenarios
  • explicit validation
  • typed DTOs when appropriate

Do not blindly persist:

$model->load($request->post());
$model->save();

without checking validation result.

Dependency Injection

For new code prefer dependency injection where practical.

Avoid unnecessary:

Yii::$app->someComponent

inside deeply nested business logic.

Use constructor injection for stable dependencies when compatible with the project architecture.

Existing Project Conventions

Before creating a new class:

  1. Search similar classes.
  2. Follow existing namespace conventions.
  3. Follow existing directory structure.
  4. Follow existing naming conventions.
  5. Reuse existing base classes.

---

# 3. `.cursor/rules/mysql.mdc`

```md
---
description: MySQL database development and performance rules
globs:
  - "**/*.php"
  - "**/*.sql"
alwaysApply: true
---

# MySQL Rules

## Query Safety

Never concatenate user input into SQL.

Bad:

```php
$sql = "SELECT * FROM user WHERE name = '" . $name . "'";

Use parameterized queries.

Query Performance

When modifying database code, consider:

  1. indexes
  2. WHERE conditions
  3. ORDER BY
  4. JOIN
  5. GROUP BY
  6. LIMIT
  7. result size
  8. N+1 queries

Indexes

Before adding an index:

  • inspect existing indexes
  • check query patterns
  • avoid duplicate indexes
  • consider composite index ordering

Do not add indexes blindly.

N+1

Always inspect loops containing database queries.

Bad:

foreach ($orders as $order) {
    User::findOne($order->user_id);
}

Prefer eager loading or batch querying.

Large Data

Avoid:

->all()

for millions of records.

Prefer:

  • batch()
  • each()
  • pagination
  • cursor-like processing where appropriate

COUNT

Be careful with expensive COUNT queries on large tables.

Transactions

Keep transactions short.

Never perform slow external HTTP requests inside database transactions unless absolutely necessary.

SQL Review

For complex queries, consider using:

EXPLAIN

before recommending a performance optimization.

Never claim a query is optimized without examining its execution characteristics when performance is the actual task.


---

# 4. `.cursor/rules/redis.mdc`

```md
---
description: Redis caching, locking and queue rules
globs:
  - "**/*.php"
alwaysApply: true
---

# Redis Rules

## Key Naming

Redis keys must have a consistent namespace.

Prefer:

```text
project:user:{id}
project:order:{id}
project:lock:order:{id}

Avoid random key formats.

Cache

Every cache entry must consider:

  • key
  • TTL
  • invalidation
  • serialization
  • stampede protection

Do not cache everything.

Cache Stampede

For hot data consider:

  • locking
  • early refresh
  • stale-while-revalidate
  • randomized TTL

Distributed Lock

When using Redis locks:

  • always define TTL
  • always release the lock
  • avoid infinite locks
  • make operations idempotent

Example conceptual pattern:

acquire lock
    ↓
execute
    ↓
finally release lock

Queue

Queue jobs must be:

  • idempotent
  • retry-safe
  • timeout-aware
  • failure-aware

Do not assume a job executes exactly once.

Retry

When implementing retries consider:

  • transient errors
  • permanent errors
  • retry count
  • backoff
  • dead-letter/failed queue

Redis Memory

Do not store unlimited collections.

Consider:

  • TTL
  • maximum list size
  • maximum set size
  • memory usage

---

# 5. `.cursor/rules/api.mdc`

```md
---
description: Yii2 REST API development rules
globs:
  - "**/controllers/**/*.php"
  - "**/modules/**/*.php"
alwaysApply: true
---

# API Rules

## Input

Never trust request input.

Validate:

- required fields
- type
- length
- range
- enum
- authorization

## Response

API responses should follow the project's existing response format.

Do not introduce a new response structure without checking existing APIs.

## HTTP Status

Use appropriate status codes when the project supports them:

200 - success
201 - created
204 - no content
400 - invalid request
401 - unauthenticated
403 - unauthorized
404 - not found
409 - conflict
422 - validation error
500 - server error

## Authentication

Authentication and authorization are different.

Always check authorization for resources.

Example:

User A must not be able to access User B's resource simply by changing:

```text
?id=123

Pagination

For potentially large API results use pagination.

Do not return unlimited database records.

Serialization

Do not blindly return ActiveRecord objects if that may expose:

  • password hashes
  • tokens
  • internal IDs
  • internal status
  • sensitive fields

Use explicit fields when appropriate.

API Compatibility

Before changing:

  • field names
  • response structure
  • HTTP status
  • error format

search existing frontend/client usage.


---

# 6. `.cursor/rules/security.mdc`

```md
---
description: PHP Yii2 security rules
globs:
  - "**/*.php"
alwaysApply: true
---

# Security Rules

Every new backend feature must consider:

- authentication
- authorization
- input validation
- SQL injection
- XSS
- CSRF
- IDOR
- mass assignment
- file upload
- SSRF
- command injection
- sensitive information exposure
- logging of secrets

## SQL Injection

Never concatenate user-controlled SQL.

## Mass Assignment

Do not blindly assign:

```php
$model->attributes = $request->post();

without checking Yii2 safe attributes and validation.

IDOR

Never assume an authenticated user can access a resource.

Bad:

$order = Order::findOne($id);

if authorization is not checked.

Prefer checking ownership or RBAC.

Passwords

Never:

  • log passwords
  • return password hashes
  • put secrets into source code

Tokens

Never log:

  • JWT
  • access token
  • refresh token
  • API key
  • session cookie

File Upload

Validate:

  • extension
  • MIME type
  • file size
  • filename
  • storage location

Do not trust the uploaded filename.

Command Execution

Be extremely careful with:

exec()
shell_exec()
system()
passthru()

Never pass untrusted user input directly into shell commands.

External URL

Validate external URLs before making server-side HTTP requests.

Consider SSRF.

Logging

Logs should contain enough information for debugging but must not contain secrets or sensitive user data.


---

# 7. `.cursor/rules/testing.mdc`

```md
---
description: PHP Yii2 testing rules
globs:
  - "**/*.php"
alwaysApply: true
---

# Testing Rules

When modifying business logic:

1. Identify existing tests.
2. Add or update tests when practical.
3. Run the relevant test suite.
4. Do not claim tests pass unless they were actually executed.

## Unit Tests

Prioritize:

- business rules
- services
- complex calculations
- permission checks
- edge cases

## API Tests

Test:

- successful request
- invalid parameters
- unauthorized request
- nonexistent resource
- boundary conditions

## Database Tests

When database behavior matters, test:

- insert
- update
- transaction rollback
- unique constraints
- relations

## Regression

When fixing a bug:

1. reproduce the bug
2. create a regression test
3. implement the fix
4. run the test
5. run related tests

## Test Claims

Never say:

"All tests pass."

unless tests were actually executed.

8. .cursor/skills/yii2-backend/SKILL.md

---
name: yii2-backend
description: Expert PHP Yii2 backend development skill. Use for Yii2 architecture, ActiveRecord, ActiveQuery, REST APIs, services, database operations, Redis, performance optimization, security, debugging, refactoring and backend code review.
---

# Yii2 Backend Expert

You are an experienced PHP/Yii2 backend engineer.

Your job is not merely to generate code.

Before changing code, understand the existing project architecture and conventions.

## Workflow

For every non-trivial task:

### Step 1: Understand the project

Inspect:

- composer.json
- config/
- common/
- frontend/
- backend/
- console/
- api/
- modules/
- models/
- services/
- components/
- behaviors/

Do not assume the project follows the standard Yii2 structure.

### Step 2: Search existing implementation

Before creating new code search for:

- similar Controller
- similar Model
- similar Service
- existing helper
- existing component
- existing Redis wrapper
- existing HTTP client
- existing response formatter
- existing validation rules

Prefer reuse over duplication.

### Step 3: Understand data flow

Determine:

```text
Request
 ↓
Controller
 ↓
Validation
 ↓
Service
 ↓
Model
 ↓
Database

or the actual architecture used by the project.

Step 4: Implement minimum necessary changes

Do not refactor unrelated code.

Avoid changing:

  • unrelated formatting
  • unrelated classes
  • dependency versions
  • database schema

unless required.


Yii2 ActiveRecord

Before modifying ActiveRecord code check:

  • rules()
  • scenarios()
  • relations()
  • behaviors()
  • events
  • beforeSave()
  • afterSave()
  • beforeDelete()
  • afterDelete()

Remember:

updateAll()
deleteAll()

do not behave exactly like individual ActiveRecord save/delete operations.


Query Optimization

When the task involves performance:

  1. inspect SQL
  2. identify query count
  3. identify N+1
  4. inspect indexes
  5. consider eager loading
  6. consider batch processing
  7. consider caching
  8. consider database execution plans

Do not optimize based solely on intuition.


Large Dataset

For large tables prefer:

->batch(500)

or:

->each(500)

instead of:

->all()

Do not load millions of records into PHP memory.


Business Transactions

If a business operation modifies multiple related records:

Yii::$app->db->transaction(function () {
    // operation
});

Keep transaction scope small.

Do not perform external network calls inside transactions unless necessary.


API Development

When creating an API:

  1. inspect existing API conventions
  2. validate input
  3. authenticate
  4. authorize
  5. execute business logic
  6. return consistent response
  7. avoid exposing sensitive fields

Debugging

When debugging:

  1. reproduce
  2. inspect stack trace
  3. identify first meaningful error
  4. inspect surrounding code
  5. check configuration
  6. check database/Redis/external dependencies
  7. propose root cause
  8. implement minimal fix
  9. test

Do not blindly patch the last exception line.


Performance

When asked to optimize:

First establish the bottleneck.

Possible bottlenecks:

  • MySQL
  • Redis
  • PHP CPU
  • PHP memory
  • network
  • external API
  • serialization
  • filesystem
  • queue
  • locking

Do not automatically add Redis caching.


Security

Every change must consider:

  • SQL injection
  • XSS
  • CSRF
  • IDOR
  • RBAC
  • mass assignment
  • file upload
  • SSRF
  • command injection
  • secret leakage

Code Review

When reviewing code, check:

Correctness

  • business logic
  • edge cases
  • null handling
  • concurrency

Database

  • N+1
  • indexes
  • transaction scope
  • unnecessary queries
  • large dataset handling

Performance

  • loops
  • memory
  • caching
  • Redis
  • external requests

Security

  • authorization
  • validation
  • injection
  • sensitive information

Maintainability

  • naming
  • duplication
  • complexity
  • coupling

Compatibility

  • existing API
  • existing database schema
  • existing callers

Response Format

For implementation tasks:

  1. Explain the root cause or approach briefly.
  2. Show the changed files.
  3. Provide complete relevant code.
  4. Explain important design decisions.
  5. Provide test commands.
  6. Clearly state what was actually tested.

Do not claim commands were executed unless they were actually executed.


Important Rule

Do not blindly follow the user's proposed implementation if it introduces:

  • security vulnerabilities
  • data corruption risk
  • obvious performance problems
  • broken Yii2 conventions

Instead explain the problem and provide a safer implementation.


---

# 9. `.cursor/skills/yii2-backend/references/architecture.md`

```md
# Yii2 Architecture Reference

Recommended default architecture:

Controller
    ↓
Request/DTO
    ↓
Service
    ↓
Domain/Model
    ↓
Repository/ActiveRecord
    ↓
Database

However, do not force this architecture onto an existing project.

Existing architecture always takes priority.

## Controller

Responsibilities:

- HTTP
- authentication
- authorization
- parameter extraction
- validation
- response

Avoid business logic.

## Service

Responsibilities:

- business workflow
- transaction
- multiple model coordination
- external service interaction
- queue dispatch

## Model

Responsibilities:

- data representation
- validation
- relationships
- persistence

Avoid putting huge business workflows into models.

## Repository

Do not introduce Repository classes automatically.

Use them when:

- query complexity is high
- data access must be abstracted
- multiple data sources exist
- existing project architecture already uses repositories

Do not create:

Controller → Repository → ActiveRecord

just for the sake of patterns.

## Transaction

Transaction should surround the atomic business operation.

Avoid:

transaction {
    HTTP request
    database operation
}

Prefer:

HTTP request

↓

validate

↓

external preparation if needed

↓

transaction {
    database writes
}

↓

response

10. .cursor/skills/yii2-backend/references/database.md

# Yii2 Database Reference

## Query Checklist

When reviewing a query ask:

1. How many SQL statements execute?
2. Is there N+1?
3. Are indexes available?
4. Is ORDER BY indexed?
5. Is the result set bounded?
6. Is the query executed inside a loop?
7. Is the same query repeated?
8. Can eager loading help?
9. Can batch processing help?
10. Is caching appropriate?

## Common Bad Pattern

```php
foreach ($users as $user) {
    $orders = Order::find()
        ->where(['user_id' => $user->id])
        ->all();
}

Potential N+1.

Better

Use eager loading:

$users = User::find()
    ->with('orders')
    ->all();

or fetch data in batches depending on the use case.

Large Data

Bad:

$rows = Order::find()->all();

Potentially dangerous for large tables.

Better:

foreach (Order::find()->batch(1000) as $orders) {
    // process
}

Bulk Update

Order::updateAll(
    ['status' => Order::STATUS_DONE],
    ['status' => Order::STATUS_PENDING]
);

Remember that ActiveRecord events and behaviors may not run as with individual save() calls.

EXPLAIN

For slow SQL:

EXPLAIN SELECT ...

Inspect:

  • type
  • possible_keys
  • key
  • rows
  • Extra

---

# 11. `.cursor/skills/yii2-backend/references/review.md`

```md
# Yii2 Code Review Checklist

## PHP

- [ ] PHP version compatible
- [ ] types appropriate
- [ ] null handling
- [ ] exception handling
- [ ] no unnecessary duplication

## Yii2

- [ ] Controller is thin
- [ ] validation exists
- [ ] ActiveRecord relations checked
- [ ] N+1 checked
- [ ] transaction scope correct
- [ ] behaviors/events considered

## MySQL

- [ ] indexes checked
- [ ] unnecessary SQL avoided
- [ ] large result set controlled
- [ ] batch processing considered
- [ ] transaction performance checked

## Redis

- [ ] key naming consistent
- [ ] TTL exists where appropriate
- [ ] cache invalidation considered
- [ ] distributed lock has timeout
- [ ] queue job is idempotent

## Security

- [ ] authentication
- [ ] authorization
- [ ] SQL injection
- [ ] XSS
- [ ] CSRF
- [ ] IDOR
- [ ] mass assignment
- [ ] file upload
- [ ] SSRF
- [ ] command injection
- [ ] secrets

## API

- [ ] input validation
- [ ] response compatibility
- [ ] status codes
- [ ] pagination
- [ ] sensitive fields excluded

## Testing

- [ ] regression test
- [ ] edge cases
- [ ] relevant tests executed
- [ ] no false test claims

推荐使用方式

完成配置后,可以直接在 Cursor Agent 中这样使用:

帮我分析这个 Yii2 项目的订单创建流程。

先不要改代码。

先找出:
1. Controller
2. Model
3. Service
4. 数据库表
5. Redis
6. 事务
7. 权限验证
8. 可能的 N+1

最后给出改造方案。

然后:

按照刚才的方案实施。

要求:
1. 不修改无关文件
2. 保持现有 API 兼容
3. 保持 Yii2 项目现有架构
4. 所有数据库写操作检查事务
5. 检查 N+1
6. 增加必要测试
7. 修改完成后运行相关测试

代码完成后:

请按照 yii2-backend 的 review checklist 对刚才的修改进行 Code Review。

重点检查:
- N+1
- SQL 性能
- Redis
- 事务
- 并发
- 权限
- IDOR
- 参数验证
- API 兼容性
- PHP 内存
- 大数据量处理

不要修改代码,只输出问题和严重程度。

最后:

根据刚才 Code Review 的结果修复所有 P0/P1 问题。

修复后重新运行相关测试,并告诉我实际执行了哪些测试。
2026-04-10

一、什么是支持向量机(SVM)

支持向量机(Support Vector Machine)本质是:

寻找一个“间隔最大”的最优分类超平面

它不仅追求“分开”,更追求:

分得最稳、泛化最好

这是它和逻辑回归最大的区别。


1)二维直观理解

二维空间中,超平面就是一条直线:

[
w^T x + b = 0
]

其中:

  • (w):方向
  • (b):偏置
  • 距离超平面最近的点:支持向量

这些点决定最终边界。(AiDocZh)


二、最大间隔原理(核心)

SVM 的核心目标是:

让两类样本距离分类边界尽可能远

分类边界两侧还有两条间隔边界:

[
w^T x + b = 1
][
w^T x + b = -1
]

两条边界距离:

[
\frac{2}{|w|}
]

所以:

最大化间隔 = 最小化 (|w|^2)

核心优化目标

\min_{w,b} \frac{1}{2}|w|^2 \quad s.t.; y_i(w^Tx_i+b)\ge 1

这就是经典 Hard Margin SVM


三、为什么最大间隔泛化更强

因为间隔越大,模型对扰动越不敏感。

例如你的 OCR、合同分类场景里:

  • 文本特征有噪声
  • OCR 有误字
  • 向量空间有偏移

大间隔能显著提高鲁棒性。

这也是 SVM 在:

  • 文本分类
  • 小样本分类
  • 高维稀疏特征

里非常强的原因。(scikit-learn)


四、软间隔 SVM(生产最常用)

现实数据通常线性不可分。

所以引入松弛变量:

[
\xi_i
]

允许少量点越界。


优化目标

\min_{w,b,\xi} \frac{1}{2}|w|^2 + C\sum_i \xi_i \quad s.t.; y_i(w^Tx_i+b)\ge 1-\xi_i

其中:

  • (C):误分类惩罚系数
  • 大 (C):更严格
  • 小 (C):更平滑

五、核技巧(SVM 真正强大的地方)

这是 SVM 最核心进阶点。


核心思想

当低维不可分时:

映射到高维空间再线性可分
[
\phi(x)
]

但直接升维成本很高。

于是引入:

Kernel Trick

只计算内积:

[
K(x_i,x_j)=\phi(x_i)^T\phi(x_j)
]

(AiDocZh)


六、常见核函数


1)线性核

[
K(x,z)=x^Tz
]

适合:

  • 文本分类
  • TF-IDF
  • 高维稀疏特征

2)多项式核

[
K(x,z)=(x^Tz+c)^d
]

适合非线性交互。


3)RBF 高斯核(最常用)

K(x,z)=\exp(-\gamma|x-z|^2)

特点:

  • 默认首选
  • 泛化稳定
  • 非线性能力强

scikit-learn 默认就非常推荐。(scikit-learn)


七、Python 实战:线性 SVM


1)基础分类

import numpy as np
from sklearn.svm import SVC
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

X, y = load_iris(return_X_y=True)

X = X[:, :2]
scaler = StandardScaler()
X = scaler.fit_transform(X)

x_train, x_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

model = SVC(kernel="linear", C=1.0)
model.fit(x_train, y_train)

print("accuracy:", model.score(x_test, y_test))

(scikit-learn)


八、RBF SVM(推荐生产)

from sklearn.svm import SVC

rbf_model = SVC(
    kernel="rbf",
    C=2.0,
    gamma="scale"
)

rbf_model.fit(x_train, y_train)
print(rbf_model.score(x_test, y_test))

参数经验(非常重要)


C 参数

C = 0.1 / 1 / 10 / 100
  • 小:防过拟合
  • 大:拟合更强

gamma 参数

gamma = "scale"

经验:

  • 大 gamma:边界复杂
  • 小 gamma:边界平滑

九、可视化决策边界(深入理解)

import matplotlib.pyplot as plt

def plot_boundary(model, X, y):
    x_min, x_max = X[:,0].min()-1, X[:,0].max()+1
    y_min, y_max = X[:,1].min()-1, X[:,1].max()+1

    xx, yy = np.meshgrid(
        np.linspace(x_min, x_max, 300),
        np.linspace(y_min, y_max, 300)
    )

    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    plt.contourf(xx, yy, Z, alpha=0.3)
    plt.scatter(X[:,0], X[:,1], c=y)
    plt.show()

这个对理解:

  • 核函数效果
  • C 对边界影响
  • gamma 复杂度

特别重要。


十、适合你的实战场景(重点)

结合你当前方向,SVM 很适合:


1)OCR 文本分类

例如:

  • 合同类型分类
  • 版权文件分类
  • 工单分类
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer

text_svm = Pipeline([
    ("tfidf", TfidfVectorizer(max_features=10000)),
    ("svm", SVC(kernel="linear"))
])

高维文本特征里,线性 SVM 非常强。(scikit-learn)


2)图像小样本分类

例如:

  • 缺陷检测
  • OCR 字符分类
  • 边缘纹理识别

3)异常检测边界学习

One-Class SVM 也非常适合:

  • 风险合同检测
  • 非法文本检测
  • OCR 异常页

十一、SVM vs XGBoost 怎么选

这是生产里最常见问题。

场景推荐
小样本高维SVM
文本 TF-IDFLinear SVM
非线性中小数据RBF SVM
百万级结构化表XGBoost
超大规模在线服务LR / GBDT

十二、生产级调参方案(推荐)

from sklearn.model_selection import GridSearchCV

params = {
    "C": [0.1, 1, 10],
    "gamma": ["scale", 0.1, 0.01],
    "kernel": ["rbf"]
}

grid = GridSearchCV(SVC(), params, cv=5)
grid.fit(x_train, y_train)

print(grid.best_params_)

十三、面试高频总结

核心记忆:


SVM 本质

最大间隔分类器

核心目标

最小化 (|w|^2)

最强能力

核技巧处理非线性

2026-04-10
让 Agent 真正具备本地知识检索、混合召回、自动索引、文档问答、持续记忆能力
  • Elasticsearch 检索
  • OCR / 合同系统
  • 本地文档知识库
  • 自动化工作流
  • RAG Agent

OpenClaw + Ollama + Elasticsearch 本地知识库 Agent 实战

核心目标:
所有数据本地闭环,不走云端 API,OpenClaw 调度 Agent,Ollama 负责推理,Elasticsearch 负责混合检索与长期知识存储。(AI Agent Knowledge Base)

一、整体架构设计

推荐你直接采用这套生产架构:

User / Telegram / Web
        ↓
   OpenClaw Gateway
        ↓
   Agent Runtime
   ├── Tool Router
   ├── Memory Search
   ├── File Parser
   └── Workflow Skills
        ↓
   Ollama (Qwen2.5 / DeepSeek)
        ↓
Elasticsearch
   ├── BM25
   ├── Dense Vector
   ├── HNSW
   └── Metadata Filter
        ↓
MinIO / 本地文件

其中:

  • OpenClaw = Agent 编排层
  • Ollama = 本地 LLM
  • Elasticsearch = 混合检索层
  • MinIO = 原文档存储层

这正好和你现在的 ES + OCR 技术栈高度一致。


二、环境准备


1)启动 Elasticsearch

docker run -d \
  --name es \
  -p 9200:9200 \
  -e discovery.type=single-node \
  -e xpack.security.enabled=false \
  -e ES_JAVA_OPTS="-Xms2g -Xmx2g" \
  docker.elastic.co/elasticsearch/elasticsearch:8.15.0

2)启动 Ollama

ollama serve
ollama pull qwen2.5:7b
ollama pull nomic-embed-text

本地 embedding 强烈推荐 nomic-embed-text。社区在 OpenClaw hybrid RAG 中大量使用。(reddit.com)


3)启动 OpenClaw

openclaw onboard

新版已经支持自动发现本地 Ollama。(OpenClaw Alpha)


三、Elasticsearch 索引设计(核心)

这里是整个系统最关键的一层。


知识库索引 Mapping

PUT local_knowledge
{
  "mappings": {
    "properties": {
      "doc_id": { "type": "keyword" },
      "title": { "type": "text" },
      "content": { "type": "text" },
      "vector": {
        "type": "dense_vector",
        "dims": 768,
        "index": true,
        "similarity": "cosine",
        "index_options": {
          "type": "hnsw"
        }
      },
      "tags": { "type": "keyword" },
      "created_at": { "type": "date" }
    }
  }
}

非常适合:

  • 合同文档
  • OCR PDF
  • 技术文档
  • 项目知识库
  • API 文档

四、Python 文档入库 Pipeline(重点)

下面给你生产级 Python 脚本。


1)文档切块

from pathlib import Path


def chunk_text(text, size=500, overlap=100):
    chunks = []
    start = 0
    while start < len(text):
        end = start + size
        chunks.append(text[start:end])
        start += size - overlap
    return chunks

2)调用 Ollama Embedding

import requests


def embed(text):
    resp = requests.post(
        "http://localhost:11434/api/embeddings",
        json={
            "model": "nomic-embed-text",
            "prompt": text
        }
    )
    return resp.json()["embedding"]

3)写入 Elasticsearch

from elasticsearch import Elasticsearch
from datetime import datetime
import uuid

es = Elasticsearch("http://localhost:9200")


def index_doc(title, content):
    chunks = chunk_text(content)

    for chunk in chunks:
        vector = embed(chunk)

        es.index(
            index="local_knowledge",
            document={
                "doc_id": str(uuid.uuid4()),
                "title": title,
                "content": chunk,
                "vector": vector,
                "tags": ["contract"],
                "created_at": datetime.now()
            }
        )

五、混合检索(BM25 + 向量)

这是效果最好的方案。

OpenClaw 社区也非常推荐 hybrid memorySearch。(reddit.com)


Python Hybrid Search

def hybrid_search(query, query_vector):
    return es.search(
        index="local_knowledge",
        query={
            "bool": {
                "should": [
                    {"match": {"content": query}},
                    {
                        "script_score": {
                            "query": {"match_all": {}},
                            "script": {
                                "source": "cosineSimilarity(params.q, 'vector') + 1.0",
                                "params": {"q": query_vector}
                            }
                        }
                    }
                ]
            }
        },
        size=5
    )

六、接入 OpenClaw Skill(核心实战)

你真正要的是让 Agent 自动调用 ES。


skill 目录结构

skills/
└── es-rag/
    ├── SKILL.md
    └── search.py

SKILL.md

# ES RAG Search

When users ask about:
- contract
- OCR files
- project docs
- Elasticsearch architecture
- deployment scripts

Always call search.py first.
Use retrieved context before answering.

Skill 是 OpenClaw 最核心扩展能力。(TechRadar)


search.py

import sys

query = sys.argv[1]
vec = embed(query)
result = hybrid_search(query, vec)

for hit in result["hits"]["hits"]:
    print(hit["_source"]["content"])

七、OpenClaw Agent 工作流优化(非常关键)

很多人只做了“能检索”,但 Agent 不一定主动用。

社区最佳实践是:

强制写入 AGENTS.md 规则,让 Agent 优先 memorySearch,再读文件 (reddit.com)

AGENTS.md

Before opening files directly:
1. Always use Elasticsearch hybrid retrieval
2. Prefer semantic recall
3. Merge top5 chunks
4. Then ask Ollama to summarize

这一条非常关键。

否则 Agent 容易:

  • ls
  • 遍历文件夹
  • 逐个打开 markdown
  • 浪费大量 token

八、适配你的 OCR / 合同系统(强烈推荐)

你当前业务非常适合:

PDF
 ↓
OCR
 ↓
结构化字段抽取
 ↓
Chunk
 ↓
Embedding
 ↓
Elasticsearch
 ↓
OpenClaw Agent

例如:

  • 用户问:某合同违约条款是什么?
  • Agent 自动检索 OCR 结果
  • 返回原始 chunk + 总结结果
  • 自动附带合同编号

九、性能优化(生产级)


1)混合权重推荐

直接用社区验证过的权重:

{
  "vectorWeight": 0.7,
  "textWeight": 0.3
}

(reddit.com)


2)MMR 去重

lambda = 0.7

避免多个 chunk 高度重复。


3)时间衰减

halfLifeDays = 30

适合你的合同、版权咨询、工单系统。


十、最适合你的最终生产架构

Nginx
 ↓
OpenClaw
 ↓
Ollama(Qwen2.5)
 ↓
Elasticsearch(HNSW)
 ↓
MinIO
 ↓
OCR / Contract / Copyright System

这套是你当前最匹配的 本地私有 Agent 知识库方案


2026-04-10

OpenClaw 本地部署详细教程(Linux / Mac / Windows 通用)


一、OpenClaw 是什么

OpenClaw 是一个 本地优先的 AI Agent 自动化平台,你可以理解为:

本地版「可执行任务的 ChatGPT + 自动化助手」

它支持:

  • 本地 LLM(Ollama)
  • OpenAI / Claude / Gemini
  • 文件操作
  • Shell 执行
  • Web 自动化
  • Slack / Telegram / Discord
  • 定时任务
  • 多 Agent 协作

特别适合你这种做:

  • 本地知识库
  • 自动代码生成
  • 文档处理
  • 爬虫调度
  • 运维自动化
  • OCR / 合同流转

的场景。(TechRadar)


二、部署方式推荐(最稳)

对于本地部署,我建议你直接用:

官方 install-cli 本地前缀安装

优点:

  • 不污染系统 Node
  • 所有文件都在 ~/.openclaw
  • 好迁移
  • 好备份
  • 适合服务器长期运行

官方推荐命令:(OpenClaw)

curl -fsSL https://openclaw.ai/install-cli.sh | bash

三、服务器配置建议

最低配置

2 Core CPU
4GB RAM
20GB SSD

推荐配置(本地模型)

8 Core CPU
16GB RAM
100GB SSD
RTX 3060+

如果你准备接 Ollama 本地模型

建议:

32GB RAM + 12GB VRAM

四、Linux 本地部署(推荐 Ubuntu)


1)安装

curl -fsSL https://openclaw.ai/install-cli.sh | bash

默认目录:

~/.openclaw

目录结构一般如下:

~/.openclaw/
├── bin/
├── config/
├── data/
├── logs/
├── tools/
└── runtime/

2)验证安装

~/.openclaw/bin/openclaw --version

或者加入环境变量:

echo 'export PATH="$HOME/.openclaw/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

然后:

openclaw --version

3)初始化服务

openclaw onboard --install-daemon

这一步会自动:

  • 初始化配置
  • 安装守护进程
  • 创建用户服务
  • 启动 Gateway

官方推荐这样做。(OpenClaw)


五、接入本地 Ollama(重点)

这个是本地部署最关键的一步。


1)安装 Ollama

curl -fsSL https://ollama.com/install.sh | sh

2)拉取模型

推荐你先用:

ollama pull qwen2.5:7b

或者:

ollama pull llama3.1:8b

3)验证模型

ollama run qwen2.5:7b

4)OpenClaw 配置模型

编辑:

~/.openclaw/config/models.json

写入:

{
  "providers": {
    "ollama": {
      "base_url": "http://127.0.0.1:11434",
      "model": "qwen2.5:7b"
    }
  }
}

社区大量本地部署都这么配。(reddit.com)


六、Docker 部署(更适合服务器)

如果你服务器主要跑 Docker,推荐这个。


docker-compose.yml

version: "3.9"

services:
  openclaw:
    image: node:24
    container_name: openclaw
    restart: always
    ports:
      - "3000:3000"
    volumes:
      - /data/openclaw:/root/.openclaw
    command: >
      sh -c "
      npm install -g openclaw@latest &&
      openclaw onboard --install-daemon &&
      tail -f /dev/null
      "

启动:

docker compose up -d

七、常用运维命令


查看状态

openclaw gateway status

查看健康检查

openclaw doctor

查看日志

tail -f ~/.openclaw/logs/gateway.log

重启

openclaw gateway restart

停止

openclaw gateway stop

八、Nginx 反向代理(生产推荐)

你经常做部署,这块对你很重要。

server {
    listen 80;
    server_name claw.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

九、安全配置(非常重要)

OpenClaw 默认权限很高,一定要做权限收敛。研究显示其工具链调用存在较大攻击面。(arXiv)

建议:


禁止高危工具

进入配置:

Config -> Tools -> Deny

建议先禁用:

  • shell
  • browser
  • gateway
  • sessions
  • web_search

尤其你本地模型推理能力有限时,禁掉很多工具反而更稳。(reddit.com)


绑定本地

127.0.0.1:3000

不要直接公网裸露。


配合 Nginx + Basic Auth

auth_basic "OpenClaw";
auth_basic_user_file /etc/nginx/.htpasswd;

十、和你技术栈最搭的玩法(推荐)

结合你的方向,我建议你这样用:


1)本地代码 Agent

  • Vue 自动生成页面
  • Python 数据脚本
  • Shell 自动部署
  • Elasticsearch DSL 自动生成

2)文档自动化

  • 合同 OCR
  • DOCX 解析
  • PDF 分类
  • 自动标签抽取

3)RAG 自动化

  • 本地知识库索引
  • 自动 chunk
  • 自动 embedding
  • 自动 ES 入库

十一、故障排查(高频)


问题1:找不到命令

openclaw: command not found

修复:(OpenClaw)

export PATH="$HOME/.openclaw/bin:$PATH"

问题2:本地模型无响应

检查:

curl http://127.0.0.1:11434/api/tags

问题3:服务没启动

openclaw gateway status
openclaw doctor

十二、最适合你的生产级架构(推荐)

Nginx
   ↓
OpenClaw
   ↓
Ollama(Qwen2.5)
   ↓
Elasticsearch
   ↓
MinIO
   ↓
Your OCR / Contract System

这套特别适合:

  • OCR合同系统
  • 版权咨询平台
  • Elasticsearch检索
  • 自动化工作流

一、什么是 Quorum NWR 算法

在分布式存储系统中,同一份数据通常会保存多个副本:

  • N(Number of Replicas):副本总数
  • W(Write Quorum):写成功所需确认副本数
  • R(Read Quorum):读成功所需返回副本数

Quorum 的核心目标是在:

  • 一致性(Consistency)
  • 可用性(Availability)
  • 延迟(Latency)

之间取得平衡。

其核心公式:

R + W > N

满足该条件时,任意一次读操作与最近一次成功写操作一定至少命中一个共同副本。(systemoverflow.com)


二、NWR 的数学本质

假设:

N = 3
W = 2
R = 2

那么:

R + W = 4 > 3

说明:

  • 写入至少进入 2 个副本
  • 读取至少读取 2 个副本
  • 任意两组大小分别为 2 的集合,在 3 个节点内必然相交

这就是 Quorum Intersection(法定人数交集)


三、Python 从零实现 Quorum 存储模型

下面我们直接用 Python 构建一个最小可运行的 Quorum KV。

from dataclasses import dataclass, field
from typing import Dict, List, Tuple
import time
import random


@dataclass
class Replica:
    node_id: int
    store: Dict[str, Tuple[int, str]] = field(default_factory=dict)

    def write(self, key: str, version: int, value: str):
        self.store[key] = (version, value)
        return True

    def read(self, key: str):
        return self.store.get(key, (0, None))


class QuorumKV:
    def __init__(self, n: int, w: int, r: int):
        self.n = n
        self.w = w
        self.r = r
        self.version = 0
        self.replicas = [Replica(i) for i in range(n)]

    def put(self, key: str, value: str):
        self.version += 1
        ack = 0
        selected = random.sample(self.replicas, self.n)

        for replica in selected:
            replica.write(key, self.version, value)
            ack += 1
            if ack >= self.w:
                return True
        return False

    def get(self, key: str):
        selected = random.sample(self.replicas, self.r)
        versions = [replica.read(key) for replica in selected]
        return max(versions, key=lambda x: x[0])

四、验证 R+W>N 的强一致读取

cluster = QuorumKV(n=3, w=2, r=2)

cluster.put("name", "alice")
print(cluster.get("name"))

cluster.put("name", "bob")
print(cluster.get("name"))

输出:

(1, 'alice')
(2, 'bob')

说明读取到了最新版本。


五、为什么 Python 仿真最适合理解 Quorum

我们继续用 Monte Carlo 做随机一致性实验。


def simulate(n, w, r, rounds=10000):
    success = 0

    for _ in range(rounds):
        write_set = set(random.sample(range(n), w))
        read_set = set(random.sample(range(n), r))

        if write_set & read_set:
            success += 1

    return success / rounds


print(simulate(3, 2, 2))
print(simulate(5, 3, 3))
print(simulate(5, 2, 2))

预期:

1.0
1.0
0.7x

R+W<=N 时,交集概率下降,不再保证强一致。


六、深入分析:不同参数组合的系统特性

1)高可用读优化

N=3, W=2, R=1

特点:

  • 读非常快
  • 写保证多数派
  • 可能读旧数据

典型系统:Dynamo 风格系统。(arxiv.org)


2)强一致推荐组合

N=3, W=2, R=2

特点:

  • 工程上最常见
  • 可容忍 1 节点故障
  • 读写都具备较强一致性

3)写优先系统

N=5, W=4, R=2

适合:

  • 金融账本
  • 订单系统
  • 元数据管理

七、Python 模拟节点故障场景

class FaultyReplica(Replica):
    def __init__(self, node_id, fail_rate=0.2):
        super().__init__(node_id)
        self.fail_rate = fail_rate

    def write(self, key, version, value):
        if random.random() < self.fail_rate:
            return False
        return super().write(key, version, value)

扩展故障集群:

class FaultyQuorumKV(QuorumKV):
    def __init__(self, n, w, r, fail_rate=0.2):
        self.n = n
        self.w = w
        self.r = r
        self.version = 0
        self.replicas = [FaultyReplica(i, fail_rate) for i in range(n)]

    def put(self, key, value):
        self.version += 1
        ack = 0

        for replica in self.replicas:
            if replica.write(key, self.version, value):
                ack += 1

        return ack >= self.w

测试:

cluster = FaultyQuorumKV(5, 3, 3, fail_rate=0.3)

ok = sum(cluster.put("k", str(i)) for i in range(1000))
print(ok / 1000)

这可以直接用于分析:

  • 节点故障率
  • quorum 成功率
  • SLA 可用性

八、Quorum 与 Paxos / Raft 的区别

很多人容易混淆:

Quorum

  • 副本读写策略
  • 偏向存储层
  • 解决副本一致性

Paxos / Raft

  • 分布式共识
  • 偏向日志复制
  • 保证全局顺序一致

多数派提交本质也是一种特殊 Quorum。(geeksforgeeks.org)


九、生产级 Python:加入 Version + Read Repair

def read_repair(cluster: QuorumKV, key: str):
    selected = random.sample(cluster.replicas, cluster.r)
    versions = [r.read(key) for r in selected]
    latest = max(versions, key=lambda x: x[0])

    for replica in selected:
        version, _ = replica.read(key)
        if version < latest[0]:
            replica.write(key, latest[0], latest[1])

    return latest

这就是 Cassandra / Dynamo 常见的:

  • Read Repair
  • Hinted Handoff
  • Anti Entropy

思想基础。


十、面试级总结(高频)

核心公式

R + W > N

常见推荐

N=3, W=2, R=2

核心收益

  • 高可用
  • 可容错
  • 延迟可控
  • 一致性可调

核心代价

  • 存在脑裂窗口
  • 非线性一致性
  • 时钟/version依赖
  • repair 成本

十一、Python 性能压测脚本(进阶)

import time

cluster = QuorumKV(5, 3, 3)
start = time.time()

for i in range(100000):
    cluster.put("counter", str(i))
    cluster.get("counter")

print("QPS:", 100000 / (time.time() - start))

十二、结语

Quorum NWR 的价值不在于“背公式”,而在于:

N/W/R 三个参数直接控制一致性、可用性、延迟三角平衡
2025-10-18

一、前言

在使用 Element Plus(或 Element UI)构建管理后台时,<el-table> 是最常见的组件之一。
无论是渲染列表、订单数据、监控日志、还是可视化报表,都离不开它。

但在复杂场景中,我们经常会遇到这样的需求:

  • ✅ 只给某一列的单元格添加特殊颜色;
  • ✅ 根据某个值动态调整字体或背景;
  • ✅ 对特定条件的行或列设置警告样式;
  • ✅ 自定义 hover 效果、边框或图标。

此时,cell-style 函数属性就是核心入口。
它能让我们对每一个单元格实现精确的样式控制。


二、基本原理概览

<el-table> 提供了几个与样式相关的函数钩子属性:

属性名作用返回类型
cell-style控制单元格样式对象或函数返回对象
header-cell-style控制表头单元格样式对象或函数返回对象
row-style控制整行样式对象或函数返回对象
header-row-style控制表头整行样式对象或函数返回对象
cell-class-name控制单元格 class 名称函数返回字符串

cell-style 的调用机制:

cell-style({ row, column, rowIndex, columnIndex })

返回一个对象形式的 CSS 样式:

return {
  color: 'red',
  backgroundColor: '#fef0f0',
  fontWeight: 'bold'
}

执行时机:

  • 每次渲染表格时(初次或更新数据后)都会被触发;
  • 每个单元格都会独立调用一次;
  • 可根据 row/column 进行条件判断。

三、最小可运行示例

让我们从一个最小 Vue 示例开始。

示例1:基础用法

<template>
  <el-table :data="tableData" :cell-style="setCellStyle" border style="width: 600px">
    <el-table-column prop="name" label="姓名" width="120" />
    <el-table-column prop="age" label="年龄" width="80" />
    <el-table-column prop="score" label="成绩" width="100" />
  </el-table>
</template>

<script setup>
import { ref } from 'vue'

const tableData = ref([
  { name: '张三', age: 18, score: 95 },
  { name: '李四', age: 22, score: 65 },
  { name: '王五', age: 25, score: 45 }
])

const setCellStyle = ({ row, column }) => {
  if (column.property === 'score') {
    if (row.score >= 90) {
      return { backgroundColor: '#e1f3d8', color: '#67c23a' } // 优秀
    } else if (row.score < 60) {
      return { backgroundColor: '#fde2e2', color: '#f56c6c' } // 不及格
    }
  }
  return {}
}
</script>

🟢 运行效果:

  • 当成绩 ≥ 90 时,单元格变绿色;
  • 当成绩 < 60 时,单元格变红;
  • 其他情况保持默认。

四、cell-style 的函数参数详解

Element Plus 源码中,cell-style 的回调参数如下:

{
  row,          // 当前行数据
  column,       // 当前列信息(含 prop、label 等)
  rowIndex,     // 当前行索引
  columnIndex   // 当前列索引
}

参数说明表:

参数名类型说明
rowObject当前行的完整数据对象
columnObject当前列的配置信息(含 prop, label 等)
rowIndexNumber当前行的序号(从0开始)
columnIndexNumber当前列的序号(从0开始)

应用场景举例:

  • column.property 用来判断是哪一列;
  • row.someField 用来判断该行的状态;
  • rowIndex 可实现奇偶行样式;
  • columnIndex 可实现首列或末列特殊样式。

五、图解执行流程

以下是 el-table 渲染过程中 cell-style 的调用流程:

┌──────────────────────────────┐
│         渲染 el-table        │
└───────────────┬──────────────┘
                │
                ▼
      每列渲染 el-table-column
                │
                ▼
    渲染每个单元格 <td> 内容
                │
                ▼
 调用 cell-style({ row, column, rowIndex, columnIndex })
                │
                ▼
     返回 style 对象 → 绑定到 <td> 的 style 属性

从源码角度看(简化版):

const style = getCellStyle({
  row,
  column,
  rowIndex,
  columnIndex
})

<td :style="style"> ... </td>

六、进阶实战:条件样式与动态计算

1️⃣ 条件样式 - 多列判断

const setCellStyle = ({ row, column }) => {
  if (column.property === 'age' && row.age > 20) {
    return { color: '#409EFF', fontWeight: 'bold' }
  }
  if (column.property === 'score' && row.score < 60) {
    return { backgroundColor: '#fde2e2', color: '#f56c6c' }
  }
}

🟩 要点:
可通过 column.property 精确判断是哪一列。


2️⃣ 奇偶行差异

const setCellStyle = ({ rowIndex }) => {
  if (rowIndex % 2 === 0) {
    return { backgroundColor: '#fafafa' }
  }
}

可以搭配 row-style 做全行样式的统一控制。


3️⃣ 根据业务状态动态样式

假设我们有订单状态表:

<el-table :data="orders" :cell-style="orderCellStyle">
  <el-table-column prop="orderId" label="订单号" />
  <el-table-column prop="status" label="状态" />
</el-table>
const orderCellStyle = ({ row, column }) => {
  if (column.property === 'status') {
    switch (row.status) {
      case '已支付':
        return { color: '#67c23a', fontWeight: 'bold' }
      case '未支付':
        return { color: '#e6a23c' }
      case '已取消':
        return { color: '#909399', textDecoration: 'line-through' }
    }
  }
}

七、与 cell-class-name 的区别与配合

属性返回类型控制方式使用场景
cell-style对象直接设置样式(style)简单样式、动态计算颜色
cell-class-name字符串添加 class 名称复用 CSS class 样式、更灵活控制

例如:

<el-table :cell-class-name="cellClassName">
  ...
</el-table>
const cellClassName = ({ column, row }) => {
  if (column.property === 'status' && row.status === '异常') {
    return 'danger-cell'
  }
}
.danger-cell {
  background-color: #fde2e2 !important;
  color: #f56c6c !important;
}

推荐实践:

  • 若样式为固定预定义样式,使用 cell-class-name
  • 若样式随数值变化(如渐变、动态颜色),使用 cell-style

八、复杂表格案例:多条件动态高亮

假设我们有一个销售数据表:

姓名地区销售额完成率
张三华东12000095%
李四华南7500070%
王五西北3500040%

我们希望:

  • 销售额低于 50000 → 红底;
  • 完成率 > 90% → 绿色;
  • 地区为 “华南” → 黄色高亮。

实现代码:

<template>
  <el-table :data="sales" :cell-style="salesCellStyle" border>
    <el-table-column prop="name" label="姓名" />
    <el-table-column prop="region" label="地区" />
    <el-table-column prop="sales" label="销售额" />
    <el-table-column prop="rate" label="完成率" />
  </el-table>
</template>

<script setup>
import { ref } from 'vue'

const sales = ref([
  { name: '张三', region: '华东', sales: 120000, rate: 95 },
  { name: '李四', region: '华南', sales: 75000, rate: 70 },
  { name: '王五', region: '西北', sales: 35000, rate: 40 }
])

const salesCellStyle = ({ row, column }) => {
  if (column.property === 'sales' && row.sales < 50000) {
    return { backgroundColor: '#fde2e2', color: '#f56c6c' }
  }
  if (column.property === 'rate' && row.rate > 90) {
    return { backgroundColor: '#e1f3d8', color: '#67c23a' }
  }
  if (column.property === 'region' && row.region === '华南') {
    return { backgroundColor: '#fdf6ec', color: '#e6a23c' }
  }
}
</script>

九、结合动态主题与 CSS 变量

若你项目使用了 暗色模式/亮色模式切换,可以将样式与 CSS 变量结合。

:root {
  --danger-bg: #fde2e2;
  --danger-color: #f56c6c;
}

.dark {
  --danger-bg: #5a3d3d;
  --danger-color: #ff8c8c;
}
const setCellStyle = ({ row, column }) => {
  if (column.property === 'score' && row.score < 60) {
    return {
      backgroundColor: 'var(--danger-bg)',
      color: 'var(--danger-color)'
    }
  }
}

十、性能优化与注意事项

1️⃣ 尽量避免复杂计算
因为 cell-style 会在每次渲染时对每个单元格执行。

优化策略

  • 提前计算标记字段;
  • 使用缓存或 computed 属性;
  • 避免在函数内创建过多对象。

2️⃣ 合理使用 class 与 style 混合策略

对于大数据量表格(>5000行):

  • 优先使用 cell-class-name
  • cell-style 仅用于少量动态样式。

3️⃣ 不推荐直接操作 DOM
不要在 cell-style 内使用 DOM API 或 $refs,这会破坏虚拟DOM渲染机制。


十一、工程实践总结

在企业级项目中,建议建立一个通用样式工具模块:

// table-style-utils.js
export const highlightByValue = ({ row, column, key, threshold, color }) => {
  if (column.property === key && row[key] > threshold) {
    return { color }
  }
}

然后在多个表格中复用:

<el-table :cell-style="(ctx) => highlightByValue({ ...ctx, key: 'score', threshold: 90, color: '#67c23a' })" />

十二、结语

cell-style 虽小,却是 Element Plus 表格中最灵活的定制点之一。

通过本篇文章你已经学会:

  • ✅ 理解 cell-style 的底层执行机制;
  • ✅ 灵活应用参数进行条件判断;
  • ✅ 区分 cell-stylecell-class-name
  • ✅ 构建多条件动态样式逻辑;
  • ✅ 实现工程化样式管理与优化。

📘 附录:完整工程模板下载结构

vue-el-table-style-demo/
├── src/
│   ├── App.vue
│   ├── components/
│   │   └── ScoreTable.vue
│   └── utils/
│       └── table-style-utils.js
├── package.json
└── vite.config.js

其中 ScoreTable.vue 即本文的完整代码,可直接运行。
该示例完全兼容 Vue 3 + Element Plus。


2025-10-09

第一章 无人机编队协同的基础概念与应用场景

1.1 无人机编队的定义

无人机编队(UAV Swarm Formation)是指多架无人机通过通信与协作控制,实现空间队形的自动保持、变换和任务分配的系统。它的核心目标是实现 分布式自治控制(Distributed Autonomous Control)

1.2 应用场景

  • 军事与巡逻任务:集群打击、编队侦察
  • 灾害搜救:大范围搜索、分区覆盖
  • 农业监测:智能喷洒、地形感知
  • 表演与娱乐:灯光秀、群体路径规划

1.3 集群智能的核心思想

每架无人机可视为一个 智能体(Agent)
整个编队系统是一个 多智能体系统(Multi-Agent System, MAS)

MAS 的关键特征:

  • 去中心化(Decentralized)
  • 局部通信(Local Communication)
  • 全局协作(Global Objective)
  • 复杂动态耦合(Dynamic Coupling)

第二章 分布式控制理论基础

2.1 集中式 vs 分布式控制

控制类型特点缺点
集中式控制所有无人机由中央节点统一决策单点故障、通信瓶颈
分布式控制每架无人机根据邻居状态独立决策收敛速度依赖拓扑结构

2.2 通信拓扑结构(Graph Topology)

设通信网络为图 ( G = (V, E) ):

  • ( V = {1, 2, ..., N} ):无人机集合
  • ( E \subseteq V \times V ):通信边集合

若无人机 ( i ) 能与 ( j ) 通信,则 ( (i, j) \in E )。

常见拓扑:

  • 全连接(Fully Connected)
  • 环形(Ring)
  • 星形(Star)
  • 网格(Grid)

2.3 邻接矩阵与拉普拉斯矩阵

定义邻接矩阵:

$$ A_{ij} = \begin{cases} 1, & (i,j) \in E\ 0, & 其他 \end{cases} $$

定义度矩阵 ( D = diag(d_1, d_2, ..., d_N) ),其中 ( d_i = \sum_j A_{ij} )。

拉普拉斯矩阵:

$$ L = D - A $$

它在一致性分析中扮演关键角色。


第三章 一致性算法(Consensus Algorithm)详解

3.1 一致性问题的定义

目标:让所有无人机的状态 ( x_i ) 收敛到共同值。

$$ \lim_{t\to\infty} |x_i(t) - x_j(t)| = 0, \quad \forall i, j $$

3.2 离散时间一致性模型

$$ x_i(k+1) = x_i(k) + \epsilon \sum_{j \in N_i} a_{ij}(x_j(k) - x_i(k)) $$

其中:

  • ( \epsilon ):步长
  • ( N_i ):邻居集合
  • ( a_{ij} ):通信权重

3.3 连续时间一致性模型

$$ \dot{x}*i = \sum*{j \in N_i} a_{ij}(x_j - x_i) $$

用矩阵形式写为:

$$ \dot{X} = -L X $$

其中 ( X = [x_1, x_2, ..., x_N]^T )。

若图连通,系统会收敛到平均值:

$$ x^* = \frac{1}{N} \sum_i x_i(0) $$


第四章 Leader-Follower 与行为层次控制模型

4.1 Leader-Follower 模型

部分无人机作为 Leader,其他为 Follower。

Follower 的控制律:

$$ u_i = k \sum_{j \in N_i} a_{ij} (x_j - x_i) $$

Leader 的状态由外部轨迹生成器定义:

$$ \dot{x}_L = f(t) $$

Follower 将收敛到 Leader 的轨迹附近。

4.2 行为层控制模型

基于 Boids 模型(Reynolds, 1987)

  • 分离(Separation):避免碰撞
  • 对齐(Alignment):速度方向一致
  • 聚合(Cohesion):靠近邻居中心

综合控制律:

$$ u_i = k_1 f_{sep} + k_2 f_{align} + k_3 f_{cohesion} $$


第五章 分布式控制算法设计与推导

以二维空间为例,定义每个无人机状态:

$$ p_i = [x_i, y_i]^T, \quad v_i = [v_{x_i}, v_{y_i}]^T $$

控制律:

$$ \dot{v}*i = \sum*{j \in N_i} a_{ij} (v_j - v_i) + b_i (p^* - p_i) $$

其中 ( p^* ) 为编队期望形态中心。

如果引入 Leader:

$$ \dot{v}_i = -c_1 (p_i - p_j^*) - c_2 (v_i - v_j) $$


第六章 Python 仿真环境搭建

6.1 仿真依赖

pip install numpy matplotlib

6.2 无人机类定义

import numpy as np

class UAV:
    def __init__(self, pos, vel=np.zeros(2)):
        self.pos = np.array(pos, dtype=float)
        self.vel = np.array(vel, dtype=float)
        
    def update(self, acc, dt=0.1):
        self.vel += acc * dt
        self.pos += self.vel * dt

6.3 控制器实现(基于一致性)

def consensus_control(uavs, A, k=1.0):
    N = len(uavs)
    acc = [np.zeros(2) for _ in range(N)]
    for i in range(N):
        for j in range(N):
            if A[i, j] == 1:
                acc[i] += k * (uavs[j].pos - uavs[i].pos)
    return acc

6.4 主仿真循环

import matplotlib.pyplot as plt

N = 5
A = np.ones((N, N)) - np.eye(N)
uavs = [UAV(np.random.rand(2) * 10) for _ in range(N)]

for t in range(200):
    acc = consensus_control(uavs, A, k=0.1)
    for i in range(N):
        uavs[i].update(acc[i], dt=0.1)
    
    if t % 10 == 0:
        plt.clf()
        plt.xlim(0, 10)
        plt.ylim(0, 10)
        for u in uavs:
            plt.scatter(u.pos[0], u.pos[1], color='b')
        plt.pause(0.05)

运行后,所有无人机会逐渐聚合到一个点。


第七章 从算法到编队:视觉化仿真实战

你可以扩展仿真以实现队形控制:

7.1 期望编队定义(如三角形)

formation = np.array([[0,0], [2,0], [1,1.732], [3,1.732], [2,3.464]])
center = np.mean([u.pos for u in uavs], axis=0)

7.2 队形控制律

def formation_control(uavs, formation, A, k=0.1):
    center = np.mean([u.pos for u in uavs], axis=0)
    acc = []
    for i, u in enumerate(uavs):
        target = center + formation[i] - np.mean(formation, axis=0)
        acc_i = k * (target - u.pos)
        acc.append(acc_i)
    return acc

运行后你将看到无人机自动形成规则队形。


第八章 通信延迟、丢包与容错机制设计

8.1 延迟建模

延迟 ( \tau ) 会导致控制律:

$$ u_i(t) = \sum_{j \in N_i} a_{ij} [x_j(t - \tau) - x_i(t)] $$

8.2 丢包机制

可使用 最近一次有效状态保持(Last-Valid-Hold) 策略。

last_positions = [u.pos.copy() for u in uavs]
for i in range(N):
    for j in range(N):
        if np.random.rand() < 0.9:  # 10% 丢包
            neighbor_pos = uavs[j].pos
        else:
            neighbor_pos = last_positions[j]

第九章 强化学习与分布式编队控制融合方向

现代研究将 强化学习(RL) 融入分布式控制:

  • 每个无人机为一个智能体
  • 状态:自身 + 邻居信息
  • 动作:速度或方向调整
  • 奖励:保持队形、避免碰撞

代表算法:

  • MADDPG (Multi-Agent Deep Deterministic Policy Gradient)
  • MAPPO (Multi-Agent Proximal Policy Optimization)

可参考开源框架:

  • PettingZoo + RLlib
  • MARLlib

目录

  1. 引言:限流的意义与应用场景
  2. 限流算法概览

    • 固定窗口限流
    • 滑动窗口限流
    • 漏桶与令牌桶
  3. 分布式滑动窗口限流的原理

    • 滑动窗口算法思路
    • 分布式实现挑战
    • Redis与Lua结合优势
  4. Redis+Lua实现分布式滑动窗口限流

    • 数据结构设计
    • Lua脚本详解
    • Redis调用方式
  5. 完整代码示例

    • Python示例
    • Node.js示例
  6. 工作流程图解
  7. 性能优化与注意事项
  8. 总结与实践建议

1. 引言:限流的意义与应用场景

在高并发场景下,服务端需要对请求进行限流,以防止系统过载。典型应用场景包括:

  • API接口防刷
  • 秒杀活动限流
  • 微服务调用流量控制

分布式系统中,单点限流容易成为瓶颈,因此采用Redis+Lua实现的分布式滑动窗口限流,成为高性能、高可用的方案。


2. 限流算法概览

2.1 固定窗口限流(Fixed Window)

  • 按固定时间窗口统计请求数量
  • 简单,但存在“临界点超额”的问题
窗口长度:1秒
请求限制:5次
时间段:[0s-1s]
请求次数统计:超过5次则拒绝

2.2 滑动窗口限流(Sliding Window)

  • 按时间连续滑动,统计最近一段时间的请求
  • 精度高,平滑处理请求峰值
  • 实现方式:

    • 精确计数(存储请求时间戳)
    • Redis Sorted Set(ZSET)存储请求时间戳

2.3 漏桶与令牌桶

  • 漏桶:固定出水速度,适合平滑处理请求
  • 令牌桶:以固定速率生成令牌,灵活控制突发请求
本文重点讲解滑动窗口算法。

3. 分布式滑动窗口限流的原理

3.1 滑动窗口算法思路

滑动窗口算法核心:

  1. 记录请求时间戳
  2. 每次请求:

    • 删除超出窗口的旧请求
    • 判断当前窗口内请求数量是否超限
    • 超限则拒绝,否则允许

公式

允许请求数量 = COUNT(时间戳 > 当前时间 - 窗口长度)

3.2 分布式实现挑战

  • 多实例并发请求
  • 原子性操作要求:检查+增加
  • 高并发下操作Redis性能问题

3.3 Redis+Lua结合优势

  • Lua脚本在Redis端执行,保证原子性
  • 减少网络往返次数,提高性能

4. Redis+Lua实现分布式滑动窗口限流

4.1 数据结构设计

使用 Redis Sorted Set (ZSET):

  • key:接口标识 + 用户ID
  • score:请求时间戳(毫秒)
  • value:唯一标识(可用时间戳+随机数)

4.2 Lua脚本详解

-- KEYS[1] : 限流key
-- ARGV[1] : 当前时间戳 (毫秒)
-- ARGV[2] : 窗口长度 (毫秒)
-- ARGV[3] : 最大请求数

local key = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local limit = tonumber(ARGV[3])

-- 删除超出窗口的旧请求
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)

-- 获取当前窗口请求数量
local count = redis.call('ZCARD', key)

if count >= limit then
    return 0  -- 限流
else
    -- 添加新请求
    redis.call('ZADD', key, now, now .. '-' .. math.random())
    -- 设置过期时间
    redis.call('PEXPIRE', key, window)
    return 1  -- 允许
end

4.3 Redis调用方式

Python调用示例(使用redis-py

import redis
import time

r = redis.Redis(host='localhost', port=6379, db=0)

lua_script = """
-- Lua脚本内容同上
"""

def is_allowed(user_id, limit=5, window=1000):
    key = f"rate_limit:{user_id}"
    now = int(time.time() * 1000)
    return r.eval(lua_script, 1, key, now, window, limit)

for i in range(10):
    if is_allowed("user123"):
        print(f"请求{i}: 允许")
    else:
        print(f"请求{i}: 限流")

Node.js调用示例(使用ioredis

const Redis = require('ioredis');
const redis = new Redis();

const luaScript = `
-- Lua脚本内容同上
`;

async function isAllowed(userId, limit=5, window=1000) {
    const key = `rate_limit:${userId}`;
    const now = Date.now();
    const result = await redis.eval(luaScript, 1, key, now, window, limit);
    return result === 1;
}

(async () => {
    for (let i = 0; i < 10; i++) {
        const allowed = await isAllowed('user123');
        console.log(`请求${i}: ${allowed ? '允许' : '限流'}`);
    }
})();

5. 工作流程图解

+---------------------+
|  用户请求到达服务端  |
+---------------------+
           |
           v
+---------------------+
|  执行Lua脚本(原子)  |
|  - 清理过期请求      |
|  - 判断请求数        |
|  - 添加请求记录      |
+---------------------+
           |
     +-----+-----+
     |           |
     v           v
  允许请求      限流返回
  • Lua脚本保证操作原子性
  • Redis ZSET高效管理时间戳

6. 性能优化与注意事项

  1. 键过期设置:使用PEXPIRE防止ZSET无限增长
  2. ZSET最大长度:可结合ZREMRANGEBYRANK控制极端情况
  3. Lua脚本缓存:避免每次发送脚本,提高性能
  4. 分布式部署:所有实例共享同一个Redis节点/集群

7. 总结与实践建议

  • 滑动窗口比固定窗口更平滑,适合高并发场景
  • Redis+Lua实现保证原子性和性能
  • 分布式系统可横向扩展,限流逻辑一致

实践建议

  1. 精确控制请求速率,结合缓存和数据库保护后端
  2. 监控限流命中率,动态调整参数
  3. Lua脚本可扩展:按接口/用户/IP限流

2025-09-06

1. 引言

1.1 为什么要降维?

在实际的机器学习项目中,我们经常面临这样的问题:

  • 数据维度过高,训练速度极慢;
  • 特征高度相关,模型泛化能力差;
  • 可视化维度太高,无法直观理解;
  • “维度灾难”导致 KNN、聚类等算法性能下降。

这些问题统称为 高维问题。解决方法之一就是 降维,即用更少的维度表示原始数据,同时保留尽可能多的信息。

1.2 PCA 的地位

主成分分析(Principal Component Analysis, PCA)是最经典的降维方法,广泛应用于:

  • 图像压缩(如人脸识别中的特征脸 Eigenfaces)
  • 金融因子建模(提取市场主要波动因子)
  • 基因组学(从上万个基因中提取少量主成分)
  • 文本处理(稀疏矩阵降维,加速训练)

1.3 本文目标

本文将从 理论原理、数学推导、代码实现、应用案例 四个方面,全面解析 PCA,并结合 Python 工程实践,展示如何在真实项目中使用 PCA 进行特征降维。


2. PCA 原理与数学推导

2.1 几何直观

假设我们有二维数据点,点云分布沿着一条斜线。如果我们要用一维表示这些点,那么最佳方式是:

  • 找到点云方差最大的方向
  • 把点投影到这个方向

这就是 第一主成分

进一步,第二主成分是与第一主成分正交的方向,方差次大。


2.2 协方差矩阵

数据矩阵 $X \in \mathbb{R}^{n \times d}$,先中心化:

$$ X_{centered} = X - \mu $$

协方差矩阵:

$$ \Sigma = \frac{1}{n} X^T X $$

$\Sigma$ 的元素含义:

$$ \sigma_{ij} = Cov(x_i, x_j) = \mathbb{E}[(x_i - \mu_i)(x_j - \mu_j)] $$

它描述了不同特征之间的相关性。


2.3 特征分解与主成分

我们要求解:

$$ \max_w \quad w^T \Sigma w \quad \text{s.t. } \|w\|=1 $$

解为:

$$ \Sigma w = \lambda w $$

也就是协方差矩阵的特征分解。最大特征值对应的特征向量就是第一主成分。

扩展到 k 维:取前 k 个特征值对应的特征向量组成矩阵 $V_k$,数据投影为:

$$ X_{reduced} = X \cdot V_k $$


2.4 与 SVD 的关系

奇异值分解(SVD):

$$ X = U \Sigma V^T $$

其中 $V$ 的列向量就是 PCA 的主成分方向。相比直接特征分解,SVD 更稳定,尤其适用于高维数据。


3. Python 从零实现 PCA

3.1 手写 PCA 类

import numpy as np

class MyPCA:
    def __init__(self, n_components):
        self.n_components = n_components
        self.components = None
        self.mean = None
    
    def fit(self, X):
        # 1. 均值中心化
        self.mean = np.mean(X, axis=0)
        X_centered = X - self.mean
        
        # 2. 协方差矩阵
        cov_matrix = np.cov(X_centered, rowvar=False)
        
        # 3. 特征分解
        eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
        
        # 4. 排序
        sorted_idx = np.argsort(eigenvalues)[::-1]
        eigenvectors = eigenvectors[:, sorted_idx]
        eigenvalues = eigenvalues[sorted_idx]
        
        # 5. 取前k个
        self.components = eigenvectors[:, :self.n_components]
    
    def transform(self, X):
        X_centered = X - self.mean
        return np.dot(X_centered, self.components)
    
    def fit_transform(self, X):
        self.fit(X)
        return self.transform(X)

3.2 应用到鸢尾花数据集

from sklearn.datasets import load_iris
import matplotlib.pyplot as plt

X = load_iris().data
y = load_iris().target

pca = MyPCA(n_components=2)
X_reduced = pca.fit_transform(X)

plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, cmap='viridis')
plt.title("Iris Dataset PCA")
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.show()

结果:不同鸢尾花品种在二维平面上明显可分。


4. Scikit-learn 实现 PCA

from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

# 标准化
X_scaled = StandardScaler().fit_transform(X)

pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X_scaled)

print("解释方差比例:", pca.explained_variance_ratio_)

输出示例:

解释方差比例: [0.72 0.23]

说明前两个主成分解释了 95% 的方差。


5. PCA 在特征工程中的应用案例

5.1 图像压缩(Eigenfaces)

from sklearn.datasets import fetch_olivetti_faces

faces = fetch_olivetti_faces().data
pca = PCA(n_components=100)
faces_reduced = pca.fit_transform(faces)

print("原始维度:", faces.shape[1])
print("降维后:", faces_reduced.shape[1])
  • 原始数据:4096维
  • 降维后:100维

仍能保留主要人脸特征。


5.2 金融风险建模

import numpy as np
from sklearn.decomposition import PCA

np.random.seed(42)
returns = np.random.randn(1000, 200)  # 模拟股票收益率

pca = PCA(n_components=10)
factor_returns = pca.fit_transform(returns)

print("累计解释率:", np.sum(pca.explained_variance_ratio_))

结果:前 10 个因子即可解释 80%+ 的市场波动。


5.3 文本特征降维

在 NLP 中,TF-IDF 特征维度可能达到 10 万。PCA 可加速分类器训练:

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVD
from sklearn.datasets import fetch_20newsgroups

data = fetch_20newsgroups(subset='train')
vectorizer = TfidfVectorizer(max_features=20000)
X_tfidf = vectorizer.fit_transform(data.data)

svd = TruncatedSVD(n_components=100)
X_reduced = svd.fit_transform(X_tfidf)

print("降维后形状:", X_reduced.shape)

5.4 基因表达数据

基因表达数据常有上万个基因,PCA 可提取主要差异:

import pandas as pd
from sklearn.decomposition import PCA

# 模拟基因表达数据 (100个样本,5000个基因)
X = np.random.rand(100, 5000)

pca = PCA(n_components=50)
X_reduced = pca.fit_transform(X)

print("累计解释率:", np.sum(pca.explained_variance_ratio_))

6. 高级变体

6.1 增量 PCA

适合大数据集:

from sklearn.decomposition import IncrementalPCA

ipca = IncrementalPCA(n_components=50, batch_size=100)
X_reduced = ipca.fit_transform(X)

6.2 核 PCA

解决非线性问题:

from sklearn.decomposition import KernelPCA

kpca = KernelPCA(n_components=2, kernel='rbf')
X_kpca = kpca.fit_transform(X)

6.3 稀疏 PCA

提升可解释性:

from sklearn.decomposition import SparsePCA

spca = SparsePCA(n_components=2)
X_spca = spca.fit_transform(X)

7. 工程实践技巧与踩坑总结

  1. 必须标准化:不同量纲影响方差计算。
  2. 碎石图选择主成分数:避免过多或过少。
  3. 小心信息损失:过度降维可能导致分类性能下降。
  4. 核 PCA 参数敏感:需要调节核函数和参数。
  5. 大数据推荐 IncrementalPCA:避免内存溢出。

8. 总结与展望

本文从 数学原理 出发,逐步解析了 PCA 的核心思想,展示了 手写实现 → sklearn 实现 → 多领域应用 的完整路径。