本章的内容主要讲解了如何给数据库的CURD查询添加回调事件,以及如何在最底层的SQL层面进行监听和做出性能分析及对查询性能做出优化建议,最后给出了一些安全方面的建议,学习内容主要从性能分析和优化,以及安全三个方面进行讲解:
- 性能分析
- 数据库调试模式
- 获取查询次数
- 获取SQL
- 开启性能分析
- SQL监听
- 性能优化
- SQL优化
- 字段缓存
- 数据缓存
- 模型缓存
- 查询事件
- 数据安全
- 底层防护
- 写入过滤
- 安全建议
- 总结
性能分析
除了一些糟糕的业务逻辑,框架的性能瓶颈一般都是在数据库(其它方面的性能没什么好纠结的)。业务逻辑的优化暂时不在本书的讨论范畴,我们首先来学习如何进行数据库的性能分析。
数据库调试模式
和应用的调试模式不同,数据库有自己独立的调试模式开关,在第一章我们已经提过,数据库配置参数中的debug参数就是数据库调试模式的开关。
// 数据库调试模式'debug' => true,
数据库调试模式开启后,可以支持下列行为:
- 记录SQL日志;
- 分析SQL性能;
- 支持SQL监听;
由于上述行为不可避免会产生额外的开销,因此对性能存在一定的影响,但并不大,因为所有的日志是最终统一一次性写入,而且可以设置为某个用户才写入日志。
在生产模式下面,必须关闭应用调试模式(app_debug),否则会暴露你的服务器敏感信息。和应用调试模式不同,开启数据库调试模式并不会对外暴露任何安全信息,因此是否开启数据库调试模式,看自己的需求。
获取查询次数
使用Db::getQueryTimes()方法可以获取当前的数据库查询次数,如果使用true作为参数的话可以获取包括写操作在内的查询次数。
// 获取读操作次数$read = Db::getQueryTimes();// 获取所有的查询次数$count = Db::getQueryTimes(true);
如果开启了页面Trace显示的话,可以直观的看到当前请求的查询信息。
调用存储过程会被认为是执行一次查询操作而非写操作,尽管存储过程内部可能会有写入操作。
获取SQL
可以用getLastsql方法获取最后一次执行的SQL语句,无论是使用Db类还是模型类,所以下面的方式都是有效的:
Db::name('user')->where('id', '>', 0)->select();echo Db::getLastSql();$user = User::get(1);echo $user->getLastsql();
getLastSql方法即使关闭数据库调试模式一样有效
如果使用了文件类型记录日志,并且开启了数据库调试模式的话,在日志文件中可以看到所有的SQL历史记录。
开启性能分析
框架不但能记录SQL日志,而且可以对查询的SQL语句作出性能分析,帮助你快速找出数据库性能瓶颈。
确保在数据库配置文件中开启下面两个参数:
// 开启数据库调试模式 'debug' => true, // 开启SQL性能分析 'sql_explain' => true,
开启sql_explain参数后,会对查询的SQL做EXPLAIN解析(由每个连接器类的getExplain方法完成查询SQL分析),并把解析结果合并记录到SQL日志中(注意:目前仅对Mysql数据库有效)。
下面是一个查询的分析日志例子:
[ SQL ] SELECT * FROM `user` WHERE `id` IN (2) [ RunTime:0.000703s ][ EXPLAIN : array ( 'id' => 1, 'select_type' => 'SIMPLE', 'table' => 'think_user', 'partitions' => NULL, 'type' => 'system', 'possible_keys' => 'PRIMARY', 'key' => NULL, 'key_len' => NULL, 'ref' => NULL, 'rows' => 1, 'filtered' => 100.0, 'extra' => NULL, ) ]
SQL日志中会记录每个SQL的执行时间以及EXPLAIN分析结果,框架只是记录分析结果,至于如何查出问题和解决则需要你具备一定的SQL性能分析和优化知识。
当EXPLAIN分析结果中的extra中使用了filesort或者temporary的话,系统会额外记录一个警告错误告诉我们某条SQL存在性能问题需要处理。
SQL监听
如果觉得内置的性能分析不够全面,完全可以对执行的SQL进行监听并且对接第三方的SQL分析类库。使用listen方法注册SQL监听,例如可以在应用公共文件或者某个行为扩展中添加如下代码:
Db::listen(function (