性能优化
本文介绍 CarefreeCMS 的性能优化方法和最佳实践。
静态化
为什么要静态化?
- 极速访问:无需 PHP 解析和数据库查询
- 降低服务器负载:减少资源消耗
- 更好的 SEO:搜索引擎更容易抓取
详细说明请参考 静态化生成。
静态化策略
全站静态化
适用于内容更新不频繁的网站:
# 生成全站
php think static:all
# 定时生成
0 2 * * * cd /var/www/cms && php think static:all
增量静态化
只生成变更的内容:
// 文章保存时自动生成
public function save($data)
{
$article = Article::create($data);
event('ArticleCreated', $article);
return $article;
}
// 监听器
class ArticleCreatedListener
{
public function handle($article)
{
(new StaticService())->generateArticle($article->id);
}
}
缓存优化
数据缓存
使用 Redis
配置 .env:
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
缓存策略
// 缓存热门文章
$hotArticles = Cache::remember('hot_articles', 3600, function() {
return Article::order('view_count', 'desc')
->limit(10)
->select();
});
// 清除缓存
Cache::forget('hot_articles');
// 标签缓存
Cache::tags(['articles'])->put('article_1', $article, 3600);
Cache::tags(['articles'])->flush(); // 清除标签下所有缓存
缓存预热
// 预加载常用数据
public function warmup()
{
Cache::put('categories', Category::all(), 86400);
Cache::put('hot_tags', Tag::orderBy('article_count', 'desc')
->limit(20)->get(), 3600);
Cache::put('site_config', SiteConfig::first(), 86400);
}
页面缓存
HTTP 缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
location ~* \.(html)$ {
expires 1h;
add_header Cache-Control "public";
}
OPcache
启用 PHP OPcache:
; php.ini
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
数据库优化
索引优化
创建索引
-- 常用查询字段添加索引
CREATE INDEX idx_status ON articles(status);
CREATE INDEX idx_category ON articles(category_id);
CREATE INDEX idx_create_time ON articles(create_time);
-- 复合索引
CREATE INDEX idx_status_time ON articles(status, create_time);
检查索引使用
EXPLAIN SELECT * FROM articles WHERE status = 1;
查询优化
使用字段选择
// ✓ 只查询需要的字段
Article::field('id, title, summary')->select();
// ✗ 查询所有字段
Article::select();
预加载关联
// ✓ 预加载避免 N+1 问题
$articles = Article::with('category', 'tags')->select();
// ✗ N+1 查询问题
$articles = Article::select();
foreach ($articles as $article) {
echo $article->category->name; // 每次循环都查询
}
分页优化
// 使用游标分页(适合大数据量)
$articles = Article::where('id', '>', $lastId)
->limit(20)
->select();
数据库连接池
配置 .env:
DB_POOL_MIN=2
DB_POOL_MAX=10
慢查询日志
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
-- 分析慢查询
mysqldumpslow /var/log/mysql/slow.log
前端优化
资源压缩
压缩 CSS/JS
# 使用 webpack/vite
npm run build
# 启用 Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript;
图片优化
// 生成缩略图
$image = Image::open($file);
$image->thumb(800, 600)->save($thumbPath);
// 使用 WebP 格式
$image->save($webpPath, 'webp', 80);
CDN 加速
配置 CDN
'cdn' => [
'enabled' => true,
'domain' => 'https://cdn.example.com',
'assets' => ['css', 'js', 'images'],
],
静态资源使用 CDN
<link rel="stylesheet" href="{asset path='css/style.css' /}">
<!-- 输出:https://cdn.example.com/css/style.css -->
懒加载
图片懒加载
<img data-src="image.jpg" class="lazyload" alt="">
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('.lazyload').forEach(img => {
observer.observe(img);
});
</script>
代码分割
// 路由懒加载
const Article = () => import('./views/Article.vue')
const routes = [
{ path: '/article/:id', component: Article }
]
服务器优化
Nginx 配置
# 工作进程数
worker_processes auto;
# 每个进程的连接数
events {
worker_connections 1024;
}
http {
# 启用 Gzip
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript;
# 开启 Keepalive
keepalive_timeout 65;
keepalive_requests 100;
# 缓冲区大小
client_body_buffer_size 128k;
client_max_body_size 10m;
# 开启 HTTP/2
listen 443 ssl http2;
}
PHP-FPM 配置
; 进程管理
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
; 请求超时
request_terminate_timeout = 300
; 慢日志
slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 5s
负载均衡
Nginx 负载均衡
upstream backend {
least_conn;
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
}
server {
location / {
proxy_pass http://backend;
}
}
队列优化
异步任务
发送邮件
// 加入队列
Queue::push(SendEmailJob::class, [
'to' => $email,
'subject' => $subject,
'content' => $content,
]);
// 处理队列
php think queue:work
批量操作
// 批量生成静态页
Queue::push(GenerateStaticJob::class, [
'type' => 'articles',
'ids' => [1, 2, 3, 4, 5],
]);
定时任务
// 定时清理过期缓存
Schedule::call(function() {
Cache::tags(['temp'])->flush();
})->daily();
// 定时生成静态页
Schedule::command('static:all')->dailyAt('02:00');
监控与分析
性能监控
APM 工具
使用 New Relic、Blackfire 等工具监控性能。
自定义监控
// 记录执行时间
$start = microtime(true);
// 业务逻辑
$articles = Article::select();
$time = microtime(true) - $start;
Log::info('查询耗时', ['time' => $time]);
性能分析
Xdebug Profiler
xdebug.mode=profile
xdebug.output_dir=/tmp/xdebug
查询分析
// 开启 SQL 日志
\think\facade\Db::listen(function($sql, $time) {
if ($time > 1000) {
Log::warning('慢查询', [
'sql' => $sql,
'time' => $time
]);
}
});
优化检查清单
后端优化
- [ ] 启用静态化
- [ ] 配置 Redis 缓存
- [ ] 启用 OPcache
- [ ] 优化数据库索引
- [ ] 使用预加载避免 N+1
- [ ] 配置队列处理异步任务
- [ ] 优化数据库连接池
前端优化
- [ ] 压缩 CSS/JS
- [ ] 优化图片大小
- [ ] 启用图片懒加载
- [ ] 使用 CDN
- [ ] 开启 Gzip
- [ ] 实现代码分割
服务器优化
- [ ] 配置 HTTP/2
- [ ] 启用 Gzip
- [ ] 设置合理的缓存策略
- [ ] 优化 PHP-FPM 配置
- [ ] 配置负载均衡(如需要)
监控
- [ ] 配置性能监控
- [ ] 记录慢查询
- [ ] 监控服务器资源
- [ ] 定期分析日志
性能测试
压力测试
使用 Apache Bench
ab -n 1000 -c 100 https://example.com/
使用 wrk
wrk -t12 -c400 -d30s https://example.com/
性能指标
关注以下指标:
- 响应时间:< 200ms
- 吞吐量:QPS > 1000
- 并发数:支持 100+ 并发
- 错误率:< 0.1%
