Featured Posts

纠结与释怀 这几天的纠结让我度日如年,伴随我的是焦虑和失眠。好久没有这样的感觉了。 我总是患得患失,这是一种心理问题,在做选择的时候,反复对比各方的优劣,放不下东,也舍不得西。 自己不知道怎么选择,然后反复问家人和朋友,即使问到答案,也不能让自己安心顺从。 幸运的是,在反复纠结之后,我逐渐想明白了一些道理。 选择,就要付出代价,必定有所得有所失,我应该勇敢承担起责任,坦然面对自己的选择带来的变化和影响。 纠结的时候,我感觉自己是个懦弱的人,害怕犯错,害怕不好的结果。 现在,我鼓起勇气,自己做出选择,不管对错,我接受,不后悔。 我要感谢猛哥,花费很多时间和精力,前后沟通,给我提供了非常难得的机会,在我最终没有选择这个机会的时候,仍然支持我的选择,并告诉我他这里的大门永远向我敞开。 此时,时间像突然停止了一样,飞快打字的手,也一下停住了,我反复看着这句话,感觉到眼眶周围热热的,滑滑的…… 我想我的勇气,多半来自于猛哥对我的关照。 我只求将来有机会能够报答猛哥的知遇之恩。 是时候为自己的选择努力工作了,大家一起加油!

Readmore

CentOS: cannot restore segment prot after reloc 最近在研究CentOS,用xampp装一套集成的LAMP环境,结果在启动Apache的时候报错: cannot restore segment prot after reloc: Permission denied 原因是 modules/mod_perl.so 不能加载。 查了一下可能是SELINUX的问题,有一个解决方法: 用 chcon...

Readmore

PHP 文件下载 IE 无法打开页面 IE 又有一个弱得不行的问题让我发现! 有个项目,要限制文件的下载权限,只有注册用户才可以下载,用户登录后,点击下载链接,弹出保存附件的提示。 我用...

Readmore

Subversion neon 诡异配置 一波三折 今天发现前几天装的 subversion 居然没法通过 http 协议访问版本库! Subversion 出现 svn: Unrecognized URL scheme for 'http://.....'  这样的错误提示。 检查 svn 客户端是否支持...

Readmore

  • Prev
  • Next

MySQL 多级同步 Master Slave

Posted on : 29-07-2009 | By : leakon | In : MySQL

0

多级同步,备份,服务器排列情况:

A -> B -> C

在 A、B 之间,A 是 Master,B 是 Slave。

在 B、C 之间,B 是 Master,C 是Slave。

这里最重要的是 B,他既是 Slave,又是 Master。

应该在 my.cnf 中加入下述 2 行:

# binary logging - not required for slaves, but recommended

log-bin=mysql-bin

log-slave-updates

开启 mysql-bin,并在 Slave 更新的同时,也同步更新 mysql-bin。

只有这样,C 才能在 A 更新时,也同步得到更新。

SofavDB_Debug_PDO MySQL 执行时间 日志

Posted on : 09-01-2009 | By : leakon | In : MySQL, PHP, Web开发, 原创

0

这个类可以完全替代 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

MySQL 索引 优化 Using filesort

Posted on : 07-01-2009 | By : leakon | In : MySQL, Web开发, 原创

2

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 的数据,页面咔的一下就出来了……

PHP MySQL localhost 127.0.0.1

Posted on : 29-11-2008 | By : leakon | In : MySQL, PHP, 原创

0

今天刚发现一个问题,PHP连接MySQL的时候,不同环境的localhost会有不同的结果。

我的服务器装了2套PHP,其中有一个是用源码编译的,另一个是xampp集成包。

编译的php用于生产环境,xampp用来建立测试环境。

把线上的代码放到测试环境下,居然报告无法连接MySQL!

刚开始以为是端口的问题,可是我在命令行下怎么连接都没问题。最后抱着试试看的想法,把localhost改成了127.0.0.1,这回居然成功了。

我不能理解,线上代码运行的好好的,配置文件就是localhost呀,怎么到测试环境就必须改成127.0.0.1才行?

两套环境,主要的区别就是PHP,虽然版本都一样,不过xampp是编译好的,我估计问题出在这里。

然后立刻写了一个测试程序:

$connA = mysql_connect('127.0.0.1:3306', 'leakon', 'pass');
$connB = mysql_connect('localhost:3306', 'leakon', 'pass');
var_dump($connA);
var_dump($connB); 

在命令行下分别用编译和xampp的php执行上述代码,果然发现两项结果不一样。

后来分析了一下,按照这种方式理解:编译PHP的时候需要指定MySQL的安装路径,这个时候localhost就指向对应的MySQL。与编译版的PHP不一样,xampp指向的是随包附带的二进制版MySQL,因此他发现这个MySQL的root密码不对,拒绝连接。

但用127.0.0.1作为主机地址时,PHP就不会按照编译的localhost找MySQL服务器,而是根据端口来找,这回就没问题了。

同时也发现了一个问题,当用localhost:port作为主机地址时,PHP会忽略端口号!

不信你试试上面的代码,那个port写成什么都无所谓,只要是localhost,就会链接特定的MySQL。

不知道为什么,就当经验,记住这个事实吧!

PHP 安全设置 %5c magic_quotes_gpc GBK

Posted on : 07-09-2008 | By : leakon | In : MySQL, PHP, 默认分类

1

安全问题。

今天看到一个朋友的blog上,写到PHP的安全设置,应该开启magic_quotes_gpc,避免SQL注入等。

可是,我在用Symfony开发时,文档上说应该关闭php.ini的magic_quotes_gpc,以前我不知道为什么,今天突然有些明白了。

这个设置,只能起到非常有限的安全作用,却有可能带来更严重的问题。

PHP的放置SQL注入,很多网站或教程上,都写着用addslashes或mysql_escape_string来过滤,或者用自动的magic_quote。这些函数的作用,就是查询字符串中的单引号、双引号和斜线"\",在这些字符前面插入一个斜线"\"。

如果遇到了"頫運鳿黒靄錦鳿黒靄錦"这样的字符,而你的数据库、网页又是GBK编码的话,上面的3种过滤函数,都会给你带来麻烦,因为这些字符的第二个字节的编码,是%5c,恰好是"\"对应的编码。

原本是2个字节的汉字,被这些函数变成了3个字节,最后的2个字节是连续的"\",而这第3个"\"会把后续的引号转义,因此SQL语句就会出错,导致更新无法执行。

安全建议:

  1. 设置php.ini,默认关闭magic_quotes_gpc
  2. 使用mysql_real_escape_string对字符串变量进行转义,注意,转义前判断magic_quotes_gpc是否已开启
  3. 使用utf-8编码,可以有效避免此类问题

MySQL bin-log PURGE 清除日志

Posted on : 09-07-2008 | By : leakon | In : Manual, MySQL

0

我有一个每天进行大量数据更新的程序,写数据库,用的是innoDB,而且开了bin-log,这段时间,写了几百G的日志,太大了,前一段时间设置过 expire-logs-days =3,但没有生效,不知为什么。

后来实在受不了,上网找了找,原来有另一个方法清除bin-log,且看中文版:

PURGE {MASTER | BINARY} LOGS TO 'log_name'

PURGE {MASTER | BINARY} LOGS BEFORE 'date'

用于删除列于在指定的日志或日期之前的日志索引中的所有二进制日志。这些日志也会从记录在日志索引文件中的清单中被删除,这样被给定的日志成为第一个。

例如:

PURGE MASTER LOGS TO 'mysql-bin.010';

PURGE MASTER LOGS BEFORE '2008-06-22 13:00:00';

清除3天前的 binlog

PURGE MASTER LOGS BEFORE DATE_SUB( NOW( ), INTERVAL 3 DAY);

BEFORE变量的date自变量可以为'YYYY-MM-DD hh:mm:ss'格式。MASTER和BINARY是同义词。

如果您有一个活性的从属服务器,该服务器当前正在读取您正在试图删除的日志之一,则本语句不会起作用,而是会失败,并伴随一个错误。不过,如果从属服务器是休止的,并且您碰巧清理了其想要读取的日志之一,则从属服务器启动后不能复制。当从属服务器正在复制时,本语句可以安全运行。您不需要停止它们。

要清理日志,需按照以下步骤:

1. 在每个从属服务器上,使用SHOW SLAVE STATUS来检查它正在读取哪个日志。

2. 使用SHOW MASTER LOGS获得主服务器上的一系列日志。

3. 在所有的从属服务器中判定最早的日志。这个是目标日志。如果所有的从属服务器是更新的,这是清单上的最后一个日志。

4. 制作您将要删除的所有日志的备份。(这个步骤是自选的,但是建议采用。)

5. 清理所有的日志,但是不包括目标日志

本文来自: (www.91linux.com) 详细出处参考:http://www.91linux.com/html/article/database/mysql/20080622/12727.html

以下是MySQL手册的英文原版:

13.6.1.1. PURGE MASTER LOGS Syntax

PURGE {MASTER | BINARY} LOGS TO 'log_name'
PURGE {MASTER | BINARY} LOGS BEFORE 'date'

Deletes all the binary logs listed in the log index prior to the specified log or date. The logs also are removed from the list recorded in the log index file, so that the given log becomes the first.

Example:

PURGE MASTER LOGS TO 'mysql-bin.010';
PURGE MASTER LOGS BEFORE '2003-04-02 22:46:26';

The BEFORE variant's date argument can be in 'YYYY-MM-DD hh:mm:ss' format. MASTER and BINARY are synonyms.

This statement is safe to run while slaves are replicating. You do not need to stop them. If you have an active slave that currently is reading one of the logs you are trying to delete, this statement does nothing and fails with an error. However, if a slave is dormant and you happen to purge one of the logs it has yet to read, the slave will be unable to replicate after it comes up.

To safely purge logs, follow this procedure:

  1. On each slave server, use SHOW SLAVE STATUS to check which log it is reading.
  2. Obtain a listing of the binary logs on the master server with SHOW BINARY LOGS.
  3. Determine the earliest log among all the slaves. This is the target log. If all the slaves are up to date, this is the last log on the list.
  4. Make a backup of all the logs you are about to delete. (This step is optional, but always advisable.)
  5. Purge all logs up to but not including the target log.

You can also set the expire_logs_days system variable to expire binary log files automatically after a given number of days (see Section 5.2.3, “System Variables”). If you are using replication, you should set the variable no lower than the maximum number of days your slaves might lag behind the master.

MySQL 字符集

Posted on : 24-03-2008 | By : leakon | In : MySQL, 原创

0

两个不同的字符串,分别插入一个表,保存字符串的字段叫 word,在 word 上建了唯一索引(unique key)。

连续插入这 2 个字符串,第二个居然报错:Duplicate entry '%s' for key %d - Error: 1062 SQLSTATE: 23000 (ER_DUP_ENTRY)

是索引存在,不能插入 2 条相同 word 的记录。

这就奇怪了,明明是 2 个不同的字符串啊,urlencode() 和 md5() 计算出来的结果都不一样,怎么就不能插入呢?

后来仔细对比,并查了 ASCII 码表,发现有些字符经 urlencoe 以后,有一个字节是 %93,或者其他的,反正都大于 0x80 了。

如果这个字节加上后面的一个字节,不是一个有效的 GBK 编码,那么,MySQL 会把字符串截断到这个字节之前。

我做了一个测试,两个字符串经 urlencode 编码后是这样的:

'%3F%3F%3F%3F%3F%3F%3F%3F%3F%3F'

'%3F%3F%3F%3F%3F%3F%3F%3F%3F%3F%93%3F%3F%94%3F%3F%3F%3F%3F%3F'

在一个表建立唯一索引,然后按照先插入第一个字符串,再插入第二个,肯定会报错(GBK编码的数据库、表和字段)。

分析原因:

这段时间在钻研 mysql_real_escape_string() 与 addslashes() 等函数的内在区别。

mysql_real_escape_string() 需要数据库连接句柄作为第二个参数,也就是说,这个函数需要参考当前连接的字符集进行转义。

GBK 编码,汉字是双字节的,第一个字节的 ASCII 码必须要大于 0x80,而第二个字节必须满足另一个规律才可以。这个规律就是 GBK 的字符集。第一个字节大于 0x80 的双字节字符有 128 * 256 个,但并不是说 GBK 的字符集就有 32768 个字符,我们的汉字好像没有这么多。

mysql_real_escape_string() 函数会参考当前连接的字符集,检查需要转义的字符串的每一个字节。

因此,如果一个汉字是 %bf%5c,并不会被转义成 %bf%5c%5c (addslashes() 的结果就是这样,\ 的ASCII 码就是 %5c),因为 %bf%5c 是一个有效的汉字。

我写了一个Wiki,里面是测试代码,用于验证上面的唯一索引的例子:

http://wiki.leakon.com/MySQLUniqueKeyCharset

安装php5出现 mysql configure failed

Posted on : 07-12-2007 | By : leakon | In : MySQL, 转载

1

安装PHP5时出现了错误。
configure: error: mysql configure failed. Please check config.log for more information.
./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2/bin/apxs --with-gd --with-jpeg-dir --with-zlib-dir --with-png-dir --with-ttf --with-mysql --enable-track-vars 出现的问题。

如果是自己选择GZ包安装的MYSQL。这里要加路径。
加的方法为--with-mysql-dir=/user/local/mysql
切记不可写为:--with-mysql=/user/local/mysql

数据库导入 乱码 数据被截断 数据不全

Posted on : 27-11-2007 | By : leakon | In : MySQL, 原创

0

今天碰到一个Case,是数据库导入的,很顺利地解决了,跟大家分享一下过程。

环境:

数据源服务器

mysql>show variables like 'char%';
character_set_client gbk
character_set_connection gbk
character_set_database gbk
character_set_results gbk
character_set_server gbk
character_set_system utf8

所有环境都是gbk,但备份脚本是这样的:
mysqldump my_db_name /home/leakon/db_bak/dump.sql

打开dump.sql,里面的中文字符都变成乱码了,在这个文件的头部,可以看到这么一行:
/*!40101 SET NAMES utf8 */;

也就是说,mysql在导出的时候,按照utf8字符集,进行了编码转换。

你可以这么理解,你和我都是中国人,说汉语,但我们之间沟通的时候用了一个翻译,这个翻译是英国人。

我就是那个数据源,英国的翻译就是dump.sql,我要跟你说中国话, 但翻译先把汉语翻译成英语,存在他的脑子里了。

这个时候你看他的内容,是没法理解的,这就是你看到的乱码。

下面,要把导出的数据,导入到新数据库中。

注意了,应该用下面的脚本:
mysql --default-character-set=latin1 -uroot my_new_db < dump.sql

我指定了latin1,作为连接mysql服务器的字符集。

实际上,导入的过程,就是让那个英国翻译,把英语再转换成汉语告诉你。这时,指定了latin1字符集, 就是告诉翻译,你让他把脑子里存的话,用英语翻译成汉语告诉你。

这里必须指定latin1,我也很奇怪为什么不指定utf8,我试过,不成功,服务器提示不支持这种编码。

具体原因我还会再研究,目前解决办法就是这样了。

指定字符集,就是让翻译按英语->汉语的转换程序进行翻译,否则,翻译可能是按英语->法语的顺序转换的,也就证明了,为什么导入数据库时,不指定编码,会是奇怪的乱码字符。

这里还有一个重要因素,就是数据库的字符集。

这个 my_new_db 创建的时候指定的是 gbk 字符集,建表语句是:
CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;

如果数据库的字符集和源数据库不一样,那么,在导入时,很肯能出现导入到一半,MySQL报语法错,或者本来10万行数据只导入了2万行就停了。

原因就是编码转换时解析引擎会把其中的某些字符理解为引号等字符,导致语法错乱。

其实,有彻底解决这些编码问题的办法,那就是保证所有过程都指定正确的编码。

下面就以gbk为例:

  1. 源数据库,数据库、表和字符串字段的编码统一设定为gbk
  2. PHP调用程序时,在创建MySQL连接后立即发送一条 set names 'gbk' 语句
  3. 导出时指定gbk字符集:mysqldump -uroot --opt --default-character-set=gbk my_db_name > dump.sql
  4. 检查文件内容,应该是正确编码的字符,用普通文本编辑器可以阅读
  5. 目标数据库建库脚本:CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci
  6. 导入时:mysql --default-character-set=gbk -uroot my_new_db < dump.sql

就这几个简单的步骤,就完全避免编码问题!

如果你要用utf8编码,把上述脚本的gbk都替换成utf8,把 gbk_chinese_ci 替换成 utf8_general_ci。

再重新导入,编码没问题,数据也完全导入了吧。