http header Expires not modified 304

困扰了我好久,看了 YSlow 的文档,也看了好多网站优化的书,按照书上的说法,加了 expires 和 cache-control 头,静态文件被缓存后,浏览器就不会再次发送请求了。

可是我看 apache 日志,仍然是有请求发到服务器,apache 发送 304 响应头。虽然起到了缓存的作用,但我希望是完全的客户端缓存,也就是说浏览器根本不会向服务器发请求询问。

今天又 google 了下,恍然大悟,原来是浏览器加载页面的策略不同:按刷新按钮与在地址栏输入网址按回车分别代表不同的动作。

地址栏输入,浏览器会优先使用缓存,如果图片文件缓存时设置了 expires ,那么在有效期内,浏览器不向服务端发任何请求,这样速度最快。

按刷新按钮,尽管本地有缓存,浏览器也会向服务器发一个询问请求,如果服务器端文件没有改变,则会返回一个 304 的相应头,告诉浏览器可以使用本地缓存,否则就返回新的文件。

这就解释了我之前遇到的问题。我配置了 Apache 后,每次按 F5 刷新页面,同时观察日志,总是有浏览器请求,总是返回 304 响应。

今天我又试了下,每次都在地址栏按回车,这回只有 html 的请求,图片都使用了本地的缓存。

土了一把……

ExpiresActive On

ExpiresDefault “access plus 1 month”

Tags: ,

SofavDB_Debug_PDO MySQL 执行时间 日志

这个类可以完全替代 PDO,如果你需要知道自己的程序跟 MySQL 交互花费多少时间,而你又有洁癖,不想把自己的代码搞得一团糟,那我强烈地建议你试试这个 SofavDB_Debug_PDO。

通常,创建一个 PDO 连接,都是 new PDO()。

在开发环境中,你只需要把上面那个语句替换成 new SofavDB_Debug_PDO() 即可。

然后,凡是通过 prepare 的 statement 执行的任何查询和修改的语句,执行时间都能记录下来。

最后,通过 SofavDB_Debug_PDO::getTimer() 就可以得到一个数组,极其方便……

当然,这个类的执行效率会有一些小小的下降,不适合用于生产环境。

源码请见 GoogleCode:

http://leakon.googlecode.com/svn/trunk/leakon/php/sofav_db/SofavDB_Debug_PDO.php

Tags:

MySQL 索引 优化 Using filesort

MySQL 数据库,MyISAM 类型的表 table_item,有 5、6 个字段,主键是 id。

user_id 和 item_id 两个字段都是单独的 INDEX 类型的索引。

问题是如何发现的?

今天搞程序,在自己的开发环境下,打开一个简单的页面都要好长时间,不知道问题出在哪里,只发现硬盘灯闪个不停。

观察 Windows 的任务管理器,看到 mysqld-nt.exe 这个进程的 “I/O 读取字节” 高达十几 G!!!

再次测试,发现每次刷新页面,这个进程要读取几十 M 的数据。

奇了怪了,查看 SQL 语句,还有表结构,字段都建了索引了呀。

后来 EXPLAIN 了一下,看到结果是 Using where; Using filesort。

explain SELECT * FROM table_item WHERE user_id = 2 ORDER BY item_id LIMIT 0, 5

翻了 MySQL 手册,仔细看下 filesort 的说明,知道了 Using filesort 是一种速度很慢的外部排序。

不过我不理解为什么会使用 filesort 排序,WHERE 和 ORDER BY 用到的字段都是有索引的呀。

赶紧 Google,找到几篇解释 Using filesort 的文章,得到的启示就是索引定义不当,MySQL 没有用到索引。

记得以前 Chenbin 给俺们培训过 MySQL 的优化,我还参加了两次,总感觉自己了解了如何优化 MySQL,没想到这么简单的语句我都没能优化。

现在想起来一些 MySQL 的特性了。

  1. 一条 SQL 语句只能使用 1 个索引 (5.0-),MySQL 根据表的状态,选择一个它认为最好的索引用于优化查询
  2. 联合索引,只能按从左到右的顺序依次使用

这 2 点刚好可以解决我的问题。

user_id 和 item_id 是 2 个索引,我的语句中,MySQL 选择了 user_id,那么 item_id 的索引没有起到任何用处,所以,当我要排序的时候,由于记录数较多,内存中的排序 buffer 满了,只能 Using filesort 进行外部排序,因此每次查询要从磁盘读取几十 M 的数据,太慢了。

修改表结构,删除 user_id 和 item_id 的 INDEX 索引,建立一个名为 user_item 的联合 UNIQUE 索引,顺序是先 user_id 后 item_id,再 EXPLAIN,这回只有 Using where 了。

再刷新页面,观察任务管理器,mysqld-nt.exe 只读取了 2K 的数据,页面咔的一下就出来了……

Tags:

Apache 漏洞 PHP 文件 上传

标题党一下,其实这个不应该算是 Apache 的漏洞。

简述漏洞情况:

有的网站有文件上传功能,尤其是可以上传 rar 文件的。如果文件名是 abc.php.rar,而且这个文件被放在了 http://www.leakon.com/uploads/abc.php.rar,你在地址栏输入这个地址,Apache 就会调用 PHP 模块去解析 abc.php.rar。

为什么会解析呢?

因为 Apache 按照文件扩展名,寻找 handler,比如,如果扩展名是 php,就交给 PHP 模块,如果是 jpg,就会输出 image/jpg 类型 的 http 头。

Apache 有一些已知的文件类型,在 Apache 目录的 conf/mime.types 文件中可以查看。

但这里没有 rar 的 handler,这时 Apache 会把 php 当作 abc.php.rar 这个文件的扩展名!

这个可能是 Apache 的一个解析规则吧,反正在 2.2.4 版本测试是这样的。

最简单的测试方法,写一个文件,内容是:

<?php
phpinfo();
?>

保存为 abc.php.rar,然后上传到你的服务器,看能不能执行吧。

前面我也说过,可能这不应该算是漏洞,只是一个规则,如果把这个规则去掉,可能相当大的一部分网站会因此受到影响。不可否认的,也是具有讽刺意味的是:我们的程序能够正常运行,有时是因为它处在一个有“漏洞”的环境中……

避免问题的方法有很多,比如在 mime 文件中加上 rar 类型的定义,再给 rar 文件加个 handler,再限制 upload 目录的执行权限等等。

我觉得比较好的方法是在服务端检查文件扩展名,如果应用程序需要上传 rar 等类型的文件,建议把文件在服务端改名,写成没有扩展名的字母和数字的组合,然后把原始文件名保存在数据库里,当需要下载的时候,用 http 头声明文件名。

单点处理,可以避免很多后续的相互依赖的安全工作。

Tags:

PHP clone 复制对象 优化对象创建速度

用 clone 结构来复制一个对象。

PHP 的对象赋值,默认是引用传递。

如果需要一个对象的副本,只能用 clone。

从性能上考虑,当在一个循环中需要反复创建一个新对象的时候,可以考虑在外层创建,在循环内层用 clone 来复制对象。这样性能将会得到很大提高。

这里有实地测试的代码和测试结果可以参考:

<<PHP 对象克隆 性能优化示例>>

引用一下结论:

仅仅是这么简单的一个对象,创建与克隆,性能差距就达到5至6倍,时间消耗已经不在一个数量级上了。

Tags: ,

Apache目录 密码 认证

1:建立用户密码文件

htpasswd -c /home/leakon/passwd_file username

-c 是指定密码文件的存放路径

接下来会提示输入密码。

重复执行这个命令,可以给多个用户创建密码文件。

 

2:建立好密码文件后,修改 httpd-vhosts.conf 文件

<VirtualHost *:80>

ServerAdmin admin@leakon.com

DocumentRoot “/home/www/web”

ServerName www.leakon.com

<Directory “/home/www/web/secret_directory”>

AuthType Basic

Allow from all

# 提示输入用户名和密码的文字

AuthName “Prompt String”

# 之前创建的密码文件

AuthUserFile /home/leakon/passwd_file

# 允许文件中所有用户都能访问

Require /home/leakon/valid_user_file

# 指定某个用户

Require user specific_username

</Directory>

</VirtualHost>

Tags: