最新发表

高效的MySQL分页

PERCONA PERFORMANCE CONFERENCE 2009上,来自雅虎的几位工程师带来了一篇”Efficient Pagination Using MySQL“的报告,有很多亮点,本文是在原文基础上的进一步延伸。

采集者烂JJ

首先看一下分页的基本原理:

采集者烂JJ

mysql> explain SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20\G
***************** 1. row **************
id: 1
select_type: SIMPLE
table: message
type: index
possible_keys: NULL
key: PRIMARY
key_len: 4
ref: NULL
rows: 10020
Extra:
1 row in set (0.00 sec) Mysql

limit 10000,20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里,如果是limit 100000,100,需要扫描100100行,在一个高并发的应用里,每次查询需要扫描超过10W行,性能肯定大打折扣。文中还提到limit n性能是没问题的,因为只扫描n行。

http://leo108.com

文中提到一种”clue”的做法,给翻页提供一些”线索”,比如还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,如果我们只提供”上一页”、”下一页”这样的跳转(不提供到第N页的跳转),那么在处理”上一页”的时候SQL语句可以是:

Mysql

SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20;

处理”下一页”的时候SQL语句可以是:

高效的MySQL分页

SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 20;

  http://leo108.com

不管翻多少页,每次查询只扫描20行。 http://leo108.com

缺点是只能提供”上一页”、”下一页”的链接形式,但是我们的产品经理非常喜欢”<上一页 1 2 3 4 5 6 7 8 9 下一页>”这样的链接方式,怎么办呢?

采集者烂JJ

如果LIMIT m,n不可避免的话,要优化效率,只有尽可能的让m小一下,我们扩展前面的”clue”做法,还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,比如要跳到第8页,我看的SQL语句可以这样写: http://leo108.com

SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20,20;

跳转到第13页:

采集者烂JJ

SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 40,20;

  leo108's blog

原理还是一样,记录住当前页id的最大值和最小值,计算跳转页面和当前页相对偏移,由于页面相近,这个偏移量不会很大,这样的话m值相对较小,大大减少扫描的行数。其实传统的limit m,n,相对的偏移一直是第一页,这样的话越翻到后面,效率越差,而上面给出的方法就没有这样的问题。 http://leo108.com

注意SQL语句里面的ASC和DESC,如果是ASC取出来的结果,显示的时候记得倒置一下。

Mysql

已在60W数据总量的表中测试,效果非常明显。

高效的MySQL分页

转自:http://www.fuchaoqun.com/2009/04/efficient-pagination-using-mysql/

leo108's blog

linux下shell命令的常用快捷键

下面是一些shell的常用快捷键,快捷键玩熟悉了在一定程度上是可以提高工作效率滴…

未经允许严禁转载

Ctrl + a 切换到命令行开始 采集者烂JJ

Ctrl + e 切换到命令行末尾

linux下shell命令的常用快捷键

Ctrl + l 清除屏幕内容

http://leo108.com

Ctrl + u 清除光标之前的内容

leo108's blog

Ctrl + k 清除光标之后的内容 linux

Ctrl + h 类似于退格键 采集者烂JJ

Ctrl + r 在历史命令中查找 (这个非常好用,输入关键字就调出以前的命令了)

leo108's blog

Ctrl + c 终止命令

采集者烂JJ

Ctrl + d 退出shell

http://leo108.com/pid-1913.asp

Ctrl + z 转入后台运行.. 采集者烂JJ

alt键比较少用,因为很多地方与远程登陆工具是有冲突的..

linux

Alt + f 切换光标前的字母

采集者烂JJ

Alt + b 切换光标后的字母

http://leo108.com/pid-1913.asp

 

Shell

原文:http://www.sudo-u.com/archives/48   (链接好像失效= =)

Shell

PHP生成长微博

目前仅支持纯文字的长微博生成,而且输入的html只能包含p标签。 leo108's blog

过段时间会增加图片以及页眉页脚支持。

采集者烂JJ

/**
 * @name cwb
 * php生成长微博
 * @param 
 *       $str 格式化后的html,仅支持p标签
 *       $size 字体大小
 *       $font_path字体路径
 *       $save_path 图片保存路径
 * @todo  增加图片支持
 * @author  leo108 root@leo108.com
 */
function cwb($str,$size,$font_path,$save_path){
    $str = strip_tags($str,'<p>');
    $matches = array();
    preg_match_all("/<p[\s\S]+?<\/p>/", $str, $matches);
    foreach ($matches[0] as $key => $value) {
        $matches[0][$key] = preg_replace("/<p[^>]*>/", "", $matches[0][$key]);
        $matches[0][$key] = str_replace('</p>', '', $matches[0][$key]);
        $matches[0][$key] = trim($matches[0][$key]);
    }
    $newrows = array();
    foreach ($matches[0] as $key => $str) {
        $strlen = mb_strlen($str,'utf-8');
        if ($strlen == 0) {
            continue;
        }
        $text = '';
        for($i = 0; $i < $strlen; $i++) {
            $char = mb_substr($str,$i,1,'utf-8');
            $text . $char;
            $bbox = imagettfbbox($size,0,$font_path,$text.$char);
            if($bbox[2] > 320){
                $newrows[] = $text;
                $text = $char;
            }else{
                $text .= $char;
            }
        }
        $newrows[] = $text;
        $newrows[] = '';
    }
    $height = count($newrows) * 16 + 30;
    $im = imagecreatetruecolor(360, $height);
    $white = imagecolorallocate($im, 255, 255, 255);
    $black = imagecolorallocate($im, 0, 0, 0);
    imagefill($im, 0, 0, $white);
    imagecopyresampled($im,$thumb_im,20,10,0,0,$pic_width,$pic_height,$pic_width,$pic_height);
    $curheight = $pic_height + 30;
    foreach ($newrows as $key => $value) {
        imagettftext($im , $size, 0, 20, $curheight, $black, $font_path, $value);
        $curheight += 16;
    }
    imagepng($im,$save_path);
}

  http://leo108.com

PHP计算后序表达式(逆波兰式)

百度谷歌搜索无果,只好自己造一次轮子。

http://leo108.com

/**
 * rpn2value
 * 计算逆波兰式
 * @author   leo108 root@leo108.com
 */
function rpn2value($str){
    $arr = explode(',',$str);
    $stack = array();
    $len = count($arr);
    for($i=0;$i<$len;$i++){
        if(is_numeric($arr[$i])){
            array_push($stack,$arr[$i]);
        }else{
            $op = $arr[$i];
            $right = array_pop($stack);
            $left = array_pop($stack);
            eval("\$re = $left $op $right;");
            array_push($stack,$re);
        }   
    }
    return $stack[0];
}

使用方法:

后序表达式

$str = "1,2,3,+,*,4,-,5,+,7,*";
echo rpn2value($str);

另附中序转后序代码,版权归原作者所有 后序表达式

/**
 * math_rpn
 *
 * 实现逆波兰式算法
 *
 * @author   sparkHuang 260558820@qq.com
 * @version  RPN 1.0.0
 *
 */
class math_rpn {
    //初始的计算表达式
    private $_expression = '';
    //处理后的逆波兰表达式
    private $_rpnexp = array();
    //模拟栈结构的数组
    private $_stack  = array('#');
    //正则判断
    //private $_reg    = '/^([A-Za-z0-9\(\)\+\-\*\/])*$/';
    //优先级
    private $_priority = array('#' => 0, '(' => 10, '+' => 20, '-' => 20, '*' => 30, '/' => 30);
    //四则运算
    private $_operator = array('(', '+', '-', '*', '/', ')');
    public function __construct($expression) {
        $this->_init($expression);
    }
    private function _init($expression) {
        $this->_expression = $expression;
    }
    public function exp2rpn() {
        $len = strlen($this->_expression);
        for($i = 0; $i < $len; $i++) {
            $char = substr($this->_expression, $i, 1);
            if ($char == '(') {
                $this->_stack[] = $char;
                continue;
            } else if ( ! in_array($char, $this->_operator)) {
                $this->_rpnexp[] = $char;
                continue;
            } else if ($char == ')') {
                for($j = count($this->_stack); $j >= 0; $j--) {
                    $tmp = array_pop($this->_stack);
                    if ($tmp == "(") {
                        break;
                    } else {
                        $this->_rpnexp[] = $tmp;
                    }
                }
                continue;
            } else if ($this->_priority[$char] <= $this->_priority[end($this->_stack)]) {
                $this->_rpnexp[] = array_pop($this->_stack);
                $this->_stack[]  = $char;
                continue;
            } else {
                $this->_stack[] = $char;
                continue;
            }
        }
        for($i = count($this->_stack); $i >= 0; $i--) {
            if (end($this->_stack) == '#') break;
            $this->_rpnexp[] = array_pop($this->_stack);
        }
        return $this->_rpnexp;
    }
}
//测试实例
$expression = "(A*(B+C)-E+F)*G";
var_dump($expression);
$mathrpn = new math_rpn($expression);
var_dump($mathrpn->exp2rpn());

 

http://leo108.com

php使用memcache存储session时报错解决方案

环境:centos+apache+php+memcache

使用的是php的memcached扩展(注意不是memchache扩展)。

PHP

按照网上的资料配置php.ini

http://leo108.com

session.save_handler=memcached
session.save_path="tcp://127.0.0.1:11211"

但是发现报错 PHP

Warning: Unknown: Failed to write session data (memcached). Please verify that the current setting of session.save_path is correct (tcp://127.0.0.1:11211) in Unknown on line 0

php使用memcache存储session时报错解决方案

解决方案是把tcp://去掉 http://leo108.com

session.save_handler=memcached
session.save_path="127.0.0.1:11211"

tcp://开头的是memcache扩展的写法,memchached扩展不需要。

未经允许严禁转载

被Alternative PHP Cache (APC)坑了

一个apache下的两个站点,一个是正式用的,另一个是测试版本,数据库相互独立。

采集者烂JJ

前几天发现一个很奇怪的BUG,测试版本的某个统计数据不正确,这个统计数据是通过sql查询得来的,并且用APC缓存10分钟,而正式版本的统计却是正确的。 APC

我直接在测试版本的mysql中查询,得到了正确的数据,去apc_fetch缓存的值,不正确。 http://leo108.com

于是尝试apc_clear_ca​che清空缓存,刷新之后发现数据正确了。 缓存

又过了10分钟之后,直接刷新页面,发现数据又错误了,查了半天没发现代码哪里有问题,一直拖到今天。

http://leo108.com/pid-1897.asp

今天同时开了正式用的和测试用的两个站点,突然发现两个统计数据是一样的(因为两个数据库互相独立,所以统计数据不一定会相同),我就想会不会是APC缓存是共享的? http://leo108.com

于是测试了下,在正式站点下新建一个php文件,apc_add(‘test’,’123′),然后在测试站也新建一个php,echo apc_fetch(‘test’),结果输出了123,验证了我之前的猜测。 leo108's blog

Shell实现FTP上传文件夹

#!/bin/bash 
updir=/root/sk    #要上传的文件夹
todir=sk          #目标文件夹
ip=127.0.0.1      #服务器
user=leo          #ftp用户名
password=123456        #ftp密码
sss=`find $updir -type d -printf $todir/'%P\n'| awk '{if ($0 == "")next;print "mkdir " $0}'` 
aaa=`find $updir -type f -printf 'put %p %P \n'` 
ftp -nv $ip <<EOF 
user $user $password
type binary 
prompt 
$sss 
cd $todir 
$aaa 
quit 
EOF

原文地址:http://rzl01.blog.51cto.com/3004337/579529

采集者烂JJ

C#删除WebBrowser控件Session

因为要搞一个类似帐号多开的小辅助,但是很坑爹的发现,在一个WebBrowser中,就算重新登录,显示的仍然是上一个帐号,尝试清空cookie无效,目测就是session的问题,因为session信息是属于httponly cookie,所以不能直接清除。

未经允许严禁转载

搜了一圈,最终在stackoverflow上找到答案。 session

清除httponly的cookie,可以借助winapi的InternetSetOption()。 session

using System.Runtime.InteropServices;

private const int INTERNET_OPTION_END_BROWSER_SESSION = 42;
[DllImport("wininet.dll", SetLastError = true)]
private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int lpdwBufferLength);

然后在需要清除session的地方使用

httponly

InternetSetOption(IntPtr.Zero, INTERNET_OPTION_END_BROWSER_SESSION, IntPtr.Zero, 0);

再次跳转之后就会发现session已经清除。 C#删除WebBrowser控件Session