志凡'S BLOG

关注PHP,WEB开发,jquery ,PHP教程,程序优化,还有一些些互联网的乱七八糟的一些东西

Archive for the ‘PHP相关’ Category

php函数unserialize数据返回false问题分析

with 5 comments

问题表现情况如下:

emlog缓存的保存方式是将php的数据对象(数组)序列化(serialize)后以文件的形式存放,读取缓存的时候直接反序列化 (unserialize)缓存字符串即可读取数据,关于序列化和反序列化的原理请看我先前的文章《php函数serialize() 与unserialize()不完全研究》

我从用户那里取到的缓存的序列化数据为:

a:1:{s:8:”kl_album”;a:4:{s:5:”title”;s:12:”精彩瞬 间”;s:3:”url”;s:41:”http://www.kaisay.cn/?plugin=kl_album”;s:8:”is_blank”;s:7:”_parent”;s:4:”hide”;s:1:”n”;}}

咋一看了解序列化的人都会说,这个数据很正常啊,没什么问题呢。可是直接把这段字符串进行unserialize,返回的值却是个False;

代码

var_dump(unserialize(’a:1:{s:8:”kl_album”;a:4:{s:5:”title”;s:12:”精彩瞬 间”;s:3:”url”;s:41:”http://www.kaisay.cn/?plugin=kl_album”;s:8:”is_blank”;s:7:”_parent”;s:4:”hide”;s:1:”n”;}}’));

点击查看原图

运行结果

点击查看原图

问题出在哪里呢?答案是 s:41:”http://www.kaisay.cn/?plugin=kl_album”

序列化字符串中标定该字符串http://www.kaisay.cn/?plugin=kl_album的长度是41,可是我们自己数一下却只有37个 字符。就是因为这个问题,导致php反序列化字符串失效。

如果将字符串长度改成37,那么程序就会顺利的反序列化

代码:

var_dump(unserialize(’a:1:{s:8:”kl_album”;a:4:{s:5:”title”;s:12:”精彩瞬 间”;s:3:”url”;s:37:”http://www.kaisay.cn/?plugin=kl_album”;s:8:”is_blank”;s:7:”_parent”;s:4:”hide”;s:1:”n”;}}’));

点击查看原图

通过google后才发现,这个问题国外已经很多的网友遇到了,在官方 手册unserialize函数页面的评论中就有很多网友在讨论和研究这个问题的解决方案。

这种情况发生的原因有多种可能,最大的可能就是在序列化数据的时候的编码和反序列化时的编码不一样导致字符串的长度出现偏差。例如数据库编码latin1 和UTF-8字符长度是不一样的。

解决方案:

自己用php来纠正序列化字符串中字符串长度的问题,链接

<?php
$unserialized
= preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $unserialized );
?>

另外一个网友提出一个在非utf-8情况下的BUG链接

# ascii字符 "\0" 被解析成了 '\0' (\0在C中是字符串的结束符等于chr(0),错误解析后算了2个字符)

// 这么写会出问题
$error = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $unserialized );

// 这么写就没事
$works = preg_replace('!s:(\d+):"(.*?)";!se', '"s:".strlen("$2").":\"$2\";"', $unserialized );

// 根据上面的情况我写出的测试例子(注意代码必须用asc格式保存运行)
$test = ’s:7:”hahaha’. chr(0) .’”;’;
echo preg_replace(’!s:(\d+):”(.*?)”;!se’, “’s:’.strlen(’$2′).’:\”$2\”;’”, $test );
echo ‘<br>’;
echo preg_replace(’!s:(\d+):”(.*?)”;!se’, ‘”s:”.strlen(”$2″).”:\”$2\”;”‘, $test );
echo ‘<br>’;
echo unserialize(preg_replace(’!s:(\d+):”(.*?)”;!se’, “’s:’.strlen(’$2′).’:\”$2\”;’”, $test ));
echo ‘<br>’;
echo unserialize(preg_replace(’!s:(\d+):”(.*?)”;!se’, ‘”s:”.strlen(”$2″).”:\”$2\”;”‘, $test ));

下图是运行结果:很显然chr(0) 变成了 \0

点击查看原图
还有一个情况就是单双引号也会出现长度计算错误的问题:链接

<?php
//--------------------------------------------
// 请看例子的值. 数据从来那里来都没有关系,无论是数据库还是代码中
$heightoptionslist = <<<HEIGHTEND
a:3:{s:37:"Hands/inches (eg. 13&#039; 2&quot;HH)";s:6:"option";s:25:"Inches only (eg.39&quot;)";s:6:"option";s:24:"Centimeters (eg. 153cms)";s:6:"option";}
HEIGHTEND;
//--------------------------------------------
// 将序列化的字符串转换回数组
$heightoptionslist = unserialize($heightoptionslist);
//--------------------------------------------
// 打印值,我们可以很容易看懂
echo "<div><pre>\$heightoptionslist = [\n".print_r($heightoptionslist, true)."\n]</pre></div>";
?>

当字符串中带有没有转换的引号的时候,就会出问题了:

<?php
//——————————————–
// 另外一个例子,
// 这次有没有转换过的引号在里面
$heightoptionslist = <<<HEIGHTEND
a:3:{s:26:”Hands/inches (eg. 13\’ 2\”HH)”;s:6:”option”;s:20:”Inches only (eg.39\”)”;s:6:”option”;s:24:”Centimeters (eg. 153cms)”;s:6:”option”;}
HEIGHTEND;
//——————————————–
//
将 序列化的字符串转换回数组.
$heightoptionslist = unserialize($heightoptionslist);
//--------------------------------------------
// 返回了一个空的结果.
echo "<div><pre>\$heightoptionslist = [\n".print_r($heightoptionslist, true)."\n]</pre></div>";
?>

以上两端代码运行结果:

点击查看原图
在字符串还有\r字符的时候计算字符串的长度的时候也会出现问题:链接

When dealing with a string which contain "\r", it seems that the length is not evaluated correctly. The following solves the problem for me :

<?php
// remove the \r caracters from the $unserialized string
$unserialized = str_replace(“\r”,“”,$unserialized);

// and then unserialize()
unserialize($unserialized);
?>
总结:解决方案

UTF-8

function mb_unserialize($serial_str) {
$serial_str= preg_replace(’!s:(\d+):”(.*?)”;!se’, “’s:’.strlen(’$2′).’:\”$2\”;’”, $serial_str );
$serial_str= str_replace(”\r”, “”, $serial_str);
return unserialize($serial_str);
}

ASC

function asc_unserialize($serial_str) {

$serial_str = preg_replace('!s:(\d+):"(.*?)";!se', '"s:".strlen("$2").":\"$2\";"', $serial_str );
$serial_str= str_replace(”\r”, “”, $serial_str);
return unserialize($serial_str);
}

希望本文能给所有遇到该问题的朋友一点帮助

原文:http://hi.baidu.com/qiaoyuetian/blog/item/dfc3654442e7258fb3b7dc64.html

Written by zifa

七月 23rd, 2010 at 5:53 下午

Posted in PHP相关

Tagged with ,

常用正则表达式

with one comment

“^\d+$”  //非负整数(正整数 + 0)
“^[0-9]*[1-9][0-9]*$”  //正整数
“^((-\d+)|(0+))$”  //非正整数(负整数 + 0)
“^-[0-9]*[1-9][0-9]*$”  //负整数
“^-?\d+$”    //整数
“^\d+(\.\d+)?$”  //非负浮点数(正浮点数 + 0)
“^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$”   //正浮点数
“^((-\d+(\.\d+)?)|(0+(\.0+)?))$”  //非正浮点数(负浮点数 + 0)
“^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$”   //负浮点数
“^(-?\d+)(\.\d+)?$”  //浮点数
“^[A-Za-z]+$”  //由26个英文字母组成的字符串
“^[A-Z]+$”  //由26个英文字母的大写组成的字符串
“^[a-z]+$”  //由26个英文字母的小写组成的字符串
“^[A-Za-z0-9]+$”  //由数字和26个英文字母组成的字符串
“^\w+$”  //由数字、26个英文字母或者下划线组成的字符串
“^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$”    //email地址
“^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$”  //url
/^(d{2}|d{4})-((0([1-9]{1}))|(1[1|2]))-(([0-2]([1-9]{1}))|(3[0|1]))$/   //  年-月-日
/^((0([1-9]{1}))|(1[1|2]))/(([0-2]([1-9]{1}))|(3[0|1]))/(d{2}|d{4})$/   // 月/日/年
“^([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)$”   //Emil
/^((\+?[0-9]{2,4}\-[0-9]{3,4}\-)|([0-9]{3,4}\-))?([0-9]{7,8})(\-[0-9]+)?$/     //电话号码
“^(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5])$”   //IP地址 Read the rest of this entry »

Written by zifa

七月 12th, 2010 at 3:24 上午

Posted in PHP相关

Tagged with

piwik 网站分析系统

with 2 comments

piwik 网站分析系统是一个以Google Analytics 为目标的网站分析工具,官方网址为 http://piwik.org/ 前身是phpMyVisit。

目前版本的piwik可以很好的满足小流量站点使用,在大型站点的使用上还有些小问题。基本上一个月更新一次修正版本,改正一些错误,提供实用的功 能。在piwik dev trac里可以清晰的看到roadmap 和tickets 对未来的规划很清晰。

大部分人使用Google Analytics 即可满足网站分析的需要。貌似流量太大会有所限制,但是豆瓣现在也在使用Google Analytics

小部分人使用收费的网站分析软件。

piwik适合一些需要自建网站分析系统,又不需要付费软件的人使用。

piwik可以看到哪些数据?

1.可定制的组件和组件位置的控制台,将你关心的数据在一个页面内显示出来。

2.访客客户端信息,包括地理位置,浏览器类型,屏幕分辨率…… 访问客户端时间和服务器时间,Engagement 访问记录 等

3.页面访问量排行、进入页面、退出页面、出站地址等

4.访问来源概况、搜索引擎和关键字状况、来源网站等

5.目标,自定义站点目标并监视其完成量。

piwik 的live 可以实时看到用户访问  十分的吸引人。

原文:http://marc.f2e.org/2010/06/26/about-piwik/

Written by zifa

六月 28th, 2010 at 9:08 下午

SEO没内容,别和我谈技术

without comments

这里主要谈谈最近的一些领悟,我们知道SEO是很重要的,但他不仅仅是个技术活。所以要谈SEO应该从广义上去谈才有意义。

做SEO的人都会告诉你收录量是关键,这应该说是最基本底线。说白了你网站要有页面,要有内容。

收录量有很多因素决定的,有一个是更新频率,你网站要是更新的少,搜索引擎就会认为你没什么内容也不会深入抓你,你更新频率不够你的内容也不能更快更多的让用户找到。很多时候保证你页面更新的方法就是加入相关资讯,例如一个标题为越野车的页面,你如果你这页面只是车类型的一些数据,品牌的话,这样就会使你的页没不会更新,加入破关资讯人的页面就可以保持更新频率啦。

要说的其实就是内容为王,关键看你的内容多不多,好不好,是不是符合用户需求的,所以内容不光要量,还要质。

所以一个网站一定要注重内容,现在都提倡UGC,但你网站有没有这个能力?我认为在你网站具备这个能力之前,你自己需要有产生内容的能力。

内容有很多种形式,拿我们273为例子,车源数据,是标准化出来的东西,因为标准化,然后他只是数据,所以他像个数据库,冷冰冰的,所以搜索引擎会让为它是机器产生的,固化的死板的,其实用户也是这样的,并不是说这新的数据不好,但他缺少活跃度。

所以为什么要资讯,其实资讯就因为他的随意性,它更活跃性。比如同样是在说一辆QQ车,一个车辆数据,告诉你车辆参数,车况。但传达的信息是死的,还需要用户自己去分析。如果同样是一个资讯它可以告诉用户有这么一车辆时尚的代步用车省油它是QQ车,对用户传达的效果我想是不一样的。

还有引导方式,什么是好的引导方式,就是给用户提供一个捷径,让他尽快找到他要的东西。我认为因为我们不知道用户要的是什么,所以我们要给用户选择,但要给他的是最接近的结果。并帮助他更方便的寻找。

我写的东西有时候散文,大家是凑合着很。这是我写东西的随意性,目前是把自己的东西传达出来。欢迎批评建议。

Written by zifa

五月 16th, 2010 at 10:03 下午

Posted in PHP相关

Tagged with

怎样把自己培养成为一个优秀的程序员

with 2 comments

态度篇

1. 做实事:不要抱怨,发牢骚,指责他人,找出问题所在,想办法解决。对问题和错误,要勇于承担。

2. 欲速则不达:用小聪明、权宜之计解决问题,求快而不顾代码质量,会给项目留下要命的死角。

3. 对事不对人:就事论事,明智、真诚、虚心地讨论问题,提出创新方案。

4. 排除万难,奋勇前进:勇气往往是克服困难的唯一方法。

学习篇

5. 跟踪变化:新技术层出不穷并不可怕。坚持学习新技术,读书,读技术杂志,参加技术活动,与人交流。要多理解新词背后的所以然,把握技术大趋势,将新技术用 于产品开发要谨慎。

6. 对团队投资:打造学习型团队,不断提高兄弟们的平均水平。

7. 懂得丢弃:老的套路和技术,该丢,就得丢。不要固步自封。

8. 打破砂锅问到底:不断追问,真正搞懂问题的本质。为什么?应该成为你的口头禅。

9. 把握开发节奏:控制好时间,养成好习惯,不要加班。

开发流程篇

10. 让客户做决定:让用户在现场,倾听他们的声音,对业务最重要的决策应该让他们说了算。

11. 让设计指导而不是操纵开发:设计是前进的地图,它指引的是方向,而不是目的本身。设计的详略程度应该适当。

12. 合理地使用技术:根据需要而不是其他因素选择技术。对各种技术方案进行严格地追问,真诚面对各种问题。

13. 让应用随时都可以发布:通过善用持续集成和版本管理,你应该随时都能够编译、运行甚至部署应用。

14. 提早集成,频繁集成:集成有风险,要尽早尽量多地集成。

15. 提早实现自动化部署

16. 使用演示获得频繁反馈

17. 使用短迭代,增量发布

18. 固定价格就意味着背叛承诺:估算应该基于实际的工作不断变化。

用户篇

19. 守护天使:自动化单元测试是你的守护天使。

20. 先用它再实现它:测试驱动开发其实是一种设计工具。

21. 不同环境,就有不同问题:要重视多平台问题。

22. 自动验收测试

23. 度量真实的进度:在工作量估算上,不要自欺欺人。

24. 倾听用户的声音:每一声抱怨都隐藏着宝贵的真理。

编程篇

25. 代码要清晰地表达意图:代码是给人读的,不要耍小聪明。

26. 用代码沟通:注释的艺术。

27. 动态地进行取舍:记住,没有最佳解决方案。各种目标不可能面面俱到,关注对用户重要的需求。

28. 增量式编程:写一点代码就构建、测试、重构、休息。让代码干净利落。

29. 尽量简单:宁简勿繁。如果没有充足的理由,就不要使用什么模式、原则和特别的技术。

30. 编写内聚的代码:类和组件应该足够小,任务单一。

31. 告知,不要询问:多用消息传递,少用函数调用。

32. 根据契约进行替换:委托往往优于继承。

调试篇

33. 记录问题解决日志:不要在同一地方摔倒两次。错误是最宝贵的财富。

34. 警告就是错误:忽视编译器的警告可能铸成大错。

35. 对问题各个击破:分而治之是计算机科学中最重要的思想之一。但是,要从设计和原型阶段就考虑各部分应该能够很好地分离。

36. 报告所有的异常

37. 提供有用的错误信息:稍微多花一点心思,出错的时候,将给你带来极大便利。

团队协作篇

38. 定期安排会面时间:常开会,开短会。

39. 架构师必须写代码:不写代码的架构师不是好架构师。好的设计都来自实际编程。编程可以带来深入的理解。

40. 实行代码集体所有制:让开发人员在系统不同区域中不同的模块和任务之间轮岗。

41. 成为指导者:教学相长。分享能提高团队的总体能力。

42. 让大家自己想办法:指引方向,而不是直接提供解决方案。让每个人都有机会在干中学习。

43. 准备好后再共享代码:不要提交无法编译或者没有通过单元测试的代码!

44. 做代码复查:复查对提高代码质量、减少错误极为重要。

45. 及时通报进展与问题:主动通报,不要让别人来问你。

Written by zifa

五月 8th, 2010 at 1:37 上午

Posted in PHP相关

Tagged with ,

php获取https下的内容

without comments

直接用file_get_contents,

$url = ('https://xxx.com");  
file_get_contents($url);

会报错;

Warning: file_get_contents(https://xxx.com) [function.file-get-contents]: failed to open stream: No such file   o&#114; directory in D:wampwwwgrabber_clientindex.php on line 3

用curl的方式是可以的:

<?php
$url = ('https://xxx.com');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$result = curl_exec($ch);
print_r($result);
?>

重点是以下两句:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

Written by zifa

五月 7th, 2010 at 12:22 上午

Posted in PHP相关

Tagged with

一个电子商务适用的函数

without comments

网购的价格筛选条件一般都是自己设定的,比如1-100,100-300,300-1000。
但怎么样才是最合理的区间呢,当然还是数据说的算,下面函数可以帮你把价格数组合理的分为几组。

<?php
 
/**
* This code was created by Jose Fonseca (josefonseca@blip.pt) 
*
* Please feel free to use it in either commercial or non-comercial applications,
* and if you enjoy using it feel free to let us know or to comment on our
* technical blog at http://code.blip.pt
*/
 
 
print_r(kmeans(array(1, 3, 2, 5, 6, 2, 3, 1, 30, 36, 45, 3, 15, 17), 3));
 
 
/**
* This function takes a array of integers and the number of clusters to create.
* It returns a multidimensional array containing the original data organized
* in clusters.
*
* @param array $data
* @param int $k
*
* @return array
*/
function kmeans($data, $k)
{
        $cPositions = assign_initial_positions($data, $k);
        $clusters = array();
 
        while(true)
        {
                $changes = kmeans_clustering($data, $cPositions, $clusters);
                if(!$changes)
                {
                        return kmeans_get_cluster_values($clusters, $data);
                }
                $cPositions = kmeans_recalculate_cpositions($cPositions, $data, $clusters);
        }
}
 
/**
*
*/
function kmeans_clustering($data, $cPositions, &$clusters)
{
        $nChanges = 0;
        foreach($data as $dataKey => $value)
        {
                $minDistance = null;
                $cluster = null;
                foreach($cPositions as $k => $position)
                {
                        $distance = distance($value, $position);
                        if(is_null($minDistance) || $minDistance > $distance)
                        {
                                $minDistance = $distance;
                                $cluster = $k;
                        }
                }
                if(!isset($clusters[$dataKey]) || $clusters[$dataKey] != $cluster)
                {
                        $nChanges++;
                }
                $clusters[$dataKey] = $cluster;
        }
 
        return $nChanges;
}
 
 
 
 
function kmeans_recalculate_cpositions($cPositions, $data, $clusters)
{
        $kValues = kmeans_get_cluster_values($clusters, $data);
        foreach($cPositions as $k => $position)
        {
                $cPositions[$k] = empty($kValues[$k]) ? 0 : kmeans_avg($kValues[$k]);
        }
        return $cPositions;
}
 
function kmeans_get_cluster_values($clusters, $data)
{
        $values = array();
        foreach($clusters as $dataKey => $cluster)
        {
                $values[$cluster][] = $data[$dataKey];
        }
        return $values;
}
 
 
function kmeans_avg($values)
{
        $n = count($values);
        $sum = array_sum($values);
        return ($n == 0) ? 0 : $sum / $n;
}
 
/**
* Calculates the distance (or similarity) between two values. The closer
* the return value is to ZERO, the more similar the two values are.
*
* @param int $v1
* @param int $v2
*
* @return int
*/
function distance($v1, $v2)
{
  return abs($v1-$v2);
}
 
/**
* Creates the initial positions for the given
* number of clusters and data.
* @param array $data
* @param int $k
*
* @return array
*/
function assign_initial_positions($data, $k)
{
        $min = min($data);
        $max = max($data);
        $int = ceil(abs($max - $min) / $k);
        while($k-- > 0)
        {
                $cPositions[$k] = $min + $int * $k;
        }
        return $cPositions;
}

输出

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 3
            [2] => 2
            [3] => 5
            [4] => 6
            [5] => 2
            [6] => 3
            [7] => 1
            [8] => 3
        )
 
    [2] => Array
        (
            [0] => 30
            [1] => 36
            [2] => 45
        )
 
    [1] => Array
        (
            [0] => 15
            [1] => 17
        )
 
)

Written by zifa

四月 29th, 2010 at 11:26 上午

Posted in PHP相关

MySQL 之Explain

with one comment

Explain命令用于查看执行效果。虽然这个命令只能搭配select类型语句使用,如果你想查看update,delete类型语句中的索引效果,也不是太难的事情,只要保持条件不变,把类型转换成select就行了。

explain的语法如下:

explain [extended] select … from … where …

如果使用了extended,那么在执行完explain语句后,可以使用show warnings语句查询相应的优化信息。

==============================================================

mk-visual-explain工具扩展了explain,它提供了一种更直观的树形表现形式,使用方法很简单:

mk-visual-explain
mk-visual-explain -c
mysql -e “explain select * from mysql.user” | mk-visual-explain

也可以在MySQL命令行里通过设置pager的方式来执行:

mysql> pager mk-visual-explain
mysql> explain [extended] select … from … where …

==============================================================

进入正题,为了让例子更具体化,我们先来建一个表,插入一点测试数据:

CREATE TABLE IF NOT EXISTS `article` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`author_id` int(10) unsigned NOT NULL,
`category_id` int(10) unsigned NOT NULL,
`views` int(10) unsigned NOT NULL,
`comments` int(10) unsigned NOT NULL,
`title` varbinary(255) NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`)
);

INSERT INTO `article`
(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES
(1, 1, 1, 1, ‘1′, ‘1′),
(2, 2, 2, 2, ‘2′, ‘2′);

缺省只建了一个主键,没有建其他的索引。测试时,如果你时间充裕,应该尽可能插入多一点的测试数据,怎么说也应该保证几千条。如果数据量过少,可能会影响 MySQL在索引选择上的判断。如此一来,一旦产品上线,数据量增加。索引往往不会按照你的预想工作。

下面让我们设置一个任务:查询category_id为1且comments大于1的情况下,views最多的article_id。

问题很简单,SQL也很简单:

SELECT author_id
FROM `article`
WHERE category_id = 1 AND comments > 1
ORDER BY views DESC
LIMIT 1

下面让我们用explain命令查看索引效果:

EXPLAIN SELECT author_id
FROM `article`
WHERE category_id = 1
AND comments > 1
ORDER BY views DESC
LIMIT 1

这时explain部分结果如下:

type: ALL
key: NULL
Extra: Using where; Using filesort

显示数据库进行了全表扫描,没有用到索引,并且在过程中文件排序。这样的结果肯定是糟糕的,下面让我们通过建立索引优化一下它:

ALTER TABLE `article` ADD INDEX x ( `category_id` , `comments`, `views` ) ;

这时explain部分结果如下:

type: range
key: x
Extra: Using where; Using filesort

虽然不再是全表扫描了,但是仍然存在文件排序。一般来说,文件排序都是由于ORDER BY语句一起的,而我们已经把views字段放到了联合索引里面,为什么没有效果呢?这是因为按照BTree的工作原理,先排序 category_id,如果遇到相同的category_id则再排序comments,如果遇到相同的comments则再排序views。当comments字段在联合索引里处于中间位置时,因为comments > 1条件是一个范围值(所谓range),MySQL目前无法利用索引再对后面的views部分进行检索,如果换成是是comments in (’a', ‘b’, ‘c’)这样的多等情况则可以,关于这一点,在High Performance MySQL一书中专门有过叙述,名为Avoiding Multiple Range Conditions,在复合索引里,仅仅只能保存一个range类型的查询字段,并且要放到复合索引的末尾,否则,range类型查询字段后面的索引无效。详细的介绍大家可以自己查阅。从这个意义上来说,此时的category_id, comments, views复合索引的效果不会比category_id, comments复合索引的效果好。

文件排序是否会引起性能问题要视数据分布情况而定。这里有一个案例可供参考:Using index for ORDER BY vs restricting number of rows.

多数情况下应该避免出现它。此时可以这样设置索引:

ALTER TABLE `article` ADD INDEX y ( `category_id` , `views` ) ;

这时explain部分结果如下:

type: range
key: x
Extra: Using where; Using filesort

很奇怪,系统无视我们刚建立的y索引,还使用x索引。导致仍然存在文件排序。

如果你也出现了类似的情况,可以使用强制索引:

EXPLAIN SELECT author_id
FROM `article`
FORCE INDEX ( y )
WHERE category_id = 1
AND comments > 1
ORDER BY views DESC
LIMIT 1

这时explain部分结果如下:

type: ref
key: y
Extra: Using where

也可以删除x索引,那样系统会自动使用y索引(有时候MySQL比较傻,所以你得会使用FORCE INDEX)。

后记:Explain的type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,得保证查询至少达到range级别,最好能达到ref,否则就可能会出现性能问题。

Explain的Extra信息也相当重要,如果此信息显示Using filesort或者Using temporary的话,噩梦即将开始,不过也不尽然,比如说在一个WHERE … ORDER BY … 类型的查询里,很多时候我们无法创建一个兼顾WHERE和ORDER BY的索引,此时如果按照WHERE来确定索引,那么在ORDER BY时,就必然会引起Using filesort,文件排序是好是坏需要仔细判断,说白了就是看是先过滤再排序划算,还是先排序再过滤划算,正确答案取决与数据分布的情况,具体的情况可以参考Using index for ORDER BY vs restricting number of rows。

Explain具体含义参见此链接:http://dev.mysql.com/doc/refman/5.1/en/using-explain.html

Written by zifa

四月 27th, 2010 at 9:31 下午

Posted in PHP相关

Tagged with

使用PHP调用MySQL的存储过程

with one comment

  MySQL好像从5.0开始才引入存储过程,反正以前做应用的时候从没碰过,不过现在因为主要作内部系统,所以很多应用都用到了存储过程,当然前台有时候也需要调用MySQL存储过程,PHP的MySQL Lib好像支持的不是很好,不过我搜索了些资料,虽然不多,但是还是尝试的使用了,现在介绍一下方法,以便用到的朋友不用再头疼。
  lMySQL扩展也是支持存储过程的,不过只支持无返回结果的存储过程,如果该存储过程存在输出,这个调用就会抛出一个错误,具体错误忘记了。调用方式很简单:

$rs = mysql_query(”call func(’str’)”, $conn);

  这个如果有返回结果,比如返回字符串,就会报错,我现在找到的解决方法是使用MySQLi扩展:

$rs = mysqli_query(”call func(’str’)”, $conn);

  这样$rs就作为一个普通的MySQL Result来使用了,非常方便。
  在调用存储过程的时候,可能还会碰到一个问题,那就是可能会顺序调用多个存储过程,这样同样会报错,解决方法就是在调用一个存储过程并处理完成后,手动关闭MySQL链接,然后再次connect,再调用另一个存储过程再处理,需要调用多少个存储过程,就重新连接多少次。
  因为我不是很了解MySQL扩展,以上纯属经验总结,如果有哪位高手知道来龙去脉,欢迎指点。

Written by zifa

四月 25th, 2010 at 11:59 下午

Posted in PHP相关

Tagged with , ,

开源 PHP 验证码 Securimage 带语音识别

with one comment

Written by zifa

四月 23rd, 2010 at 8:31 下午

Posted in PHP相关