Archive for 一月, 2011

我们的登录系统,使用了php的session机制,与cookie相比,无疑它是很安全的。同时,为了提升用户体验,页面上大量的使用了ajax延迟加载,而这些加载的数据,多是要求登录的,也就是说加载时需要进行session的验证。

这样造成的结果是,用户打开一个页面,先看到页面框架load完毕,然后浏览器发出一堆的ajax请求,这些ajax请求变成了串行的!在网络或者服务器繁忙的时候,用户体验可想而知!

那为什么session会造成这样的后果呢?PHP手册上有这样的句子(http://www.php.net/manual/en/function.session-write-close.php

session data is locked to prevent concurrent writes only one script may operate on a session at any time

也就是说session_start函数调用后,会对session加锁,以避免其他web请求修改了session的内容,而且从这里可以看到,该锁是彻底的互斥锁。针对该问题,网上也有人给出了解决方案,即在session打开并使用完毕之后,立即session_write_close掉,以允许其他web请求正常进行。另外,既然函数名称为write close,经测试,调用之后,仍然可以读取$_SESSION中的值,但是写入则失败。

session_start();
$_SESSION[‘test’] = ‘abcd';
session_write_close();
$_SESSION[‘a’] = ‘aaaa';
var_dump($_SESSION);

这里,立即var_dump session可以看到a=>’aaaa’,但是它并没有真的写入,在其他页面进行请求,是请求不到的。

当然,另一方面,我们也可以使用cookie来避免这种竞争的存在。由于cookie对于用户是可见的,所以一些敏感内容不可以直接存储,需要进行加密。自己实现加密算法未免有些重复造轮子,所以我从Cake框架中摘取出了一段加密代码:

下载地址:http://flykobetest-xfiles.stor.sinaapp.com/2cfc19a8c680453cf374317c946bddad.txt

最近在做基于OAUTH的一些事情,其所需的oauth_token,oauth_token_secret,user_id原先都是以数组的形式存储在session中的。而cookie不支持array,而我又不想在一个域名下建立多个cookie的key,所以就可以将需要存储的数组转化为GET参数的形式,以&和=连接。这里需要注意的是,key和value中都可能出现我们的连接符号&和=,所以在编码前,请对key和value分别urlencode。

最近项目中,出于内存效率考虑,对函数的返回值采取了值引用的方式。在control层的函数中,声明某变量为global的,然后调用该值引用函数进行初始化。然后在view层中,试图使用该global的变量失败,其值为NULL。

而如果函数直接返回值,而非引用方式,则正常。

简化代码如下:

function &fun(){
        $arr = array(
                        'a' => 1,
                        'b' => 2
                        );
        return $arr;
}

function child(){
        global $product;
        $product =& fun();
}

function child2(){
        $GLOBALS['product'] =& fun();
}

child();
global $product;
var_dump($product);

child2();
global $product;
var_dump($product);

调用child函数后,product为NULL,而调用child2函数后,product有值。

在两个global $product之后,分别调用xdebug_debug_zval函数,可以看到以下输出:

product: (refcount=1, is_ref=1)=NULL
product: (refcount=1, is_ref=1)=array (‘a’ => (refcount=1, is_ref=0)=1, ‘b’ => (refcount=1, is_ref=0)=2)

即child之后,product变量存在,但是其值为NULL;而child2之后,product变量和其值都存在。

在php手册里看到这么一句话:

在一个函数域内部用 global 语句导入的一个真正的全局变量实际上是建立了一个到全局变量的引用

这意味着,在child函数中,声明了一个指向global product”指针”的引用,并且改变了改引用的值。翻译成C语言即(注意:PHP的引用不是指针地址的引用,是符号表别名,后面会介绍。所以这里翻译成C代码,仅仅是表述其意。):

char * product = null;
void init(char*product){
    product = (char*)malloc(10);
}

那么很明显该product不会被初始化。

进一步查看php的引用原理,发现如下解释:

如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见。可以通过使用 $GLOBALS 数组避免这一点

把 global $var; 当成是 $var =& $GLOBALS[‘var’]; 的简写。从而将其它引用赋给 $var 只改变了本地变量的引用。

<?php
$var1
= “Example variable”;
$var2 = “”;

function global_references($use_globals)
{
global
$var1, $var2;
if (!
$use_globals) {
$var2 =& $var1; // visible only inside the function
} else {
$GLOBALS[“var2″] =& $var1; // visible also in global context
}
}

global_references(false);
echo
“var2 is set to ‘$var2‘\n”; // var2 is set to ”
global_references(true);
echo
“var2 is set to ‘$var2‘\n”; // var2 is set to ‘Example variable’
?>

从php手册上可以看到,php的引用区别于C等语言,它不是指针,是“符号表别名”。

php引用与C引用的区别

php引用与C引用的区别

图画的简陋了点 :( ,在php中如果$q=&$p,则它们指向同一块地址,但是p和q之间没有直接关系。而c中,如果q=&p,则q是指向指针的指针,改变**q才会改变*p的值。

参考:

引用计数基本知识  http://php.net/manual/zh/features.gc.refcounting-basics.php

回收周期(Collecting Cycles) http://php.net/manual/zh/features.gc.collecting-cycles.php

变量范围 http://php.net/manual/zh/language.variables.scope.php

每次ssh连接到服务器,都会等待至少几秒钟。

刚开始怀疑是DNS解析的问题,但是换了ip直接连接,还是很慢。

又怀疑是网络问题,但是ping的速度还行。

使用ssh的debug模式:ssh -v *.*.*.*,看到连接在这里阻塞:

debug1: Next authentication method: gssapi-with-mic
debug1: Unspecified GSS failure.  Minor code may provide more information
No credentials cache found
debug1: Unspecified GSS failure.  Minor code may provide more information
No credentials cache found
debug1: Unspecified GSS failure.  Minor code may provide more information
No credentials cache found

既然这里反正验证不成功也可以登录,于是就去服务器上,把/etc/ssh/sshd_config 中的GSSAPIAuthentication改为no,并且reload了sshd服务。再次尝试,就很快了!

sudo fdisk -l 列出硬盘块数、及大小等情况

smartctl是硬盘检测工具,可以smartd作为daemon形式运行在后台,也可以smartctl交互式运行。具体的运行命令可以参考:http://blog.shadypixel.com/monitoring-hard-drive-health-on-linux-with-smartmontools/

常用的命令为:

$ sudo smartctl -i /dev/sda 检查/dev/sda硬盘是否支持smart(据说可以在bios里打开)

$ sudo smartctl -H /dev/sda 检测硬盘情况 ,如果的结果不是PASSED,则说明硬盘可能有问题,虽然有可能并不是立即显现,但是还是需要更换。

与硬盘相关的其他命令:

fdisk -l 会列出硬盘情况。我们用了LVM,据说与raid相关,我还没搞懂。所以,df时候,看到的device name与fdisk的device会不同。

df -h 会列出硬盘的使用情况。

LVM命令:

sudo /usr/sbin/pvdisplay
sudo /usr/sbin/vgdisplay
sudo /usr/sbin/lvdisplay

之后一周,希望可以实现以下简单的OPS方案,对我们的服务器进行初步的功能和性能监控。

Linux服务器性能监控

cpu、内存使用率、网络带宽、硬盘占用率, 利用sar,配合其他命令,间隔采样(比如1分钟),记录下来,暂时可以不形成图形。在达到某个峰值,要能够发邮件告警。(top、vmstat、iostat、netstat、iptraf、nethogs、uptime)

MySql数据库监控

每天将mysql的bin-log备份,可以暂时备份到我的私人服务器。怎样监控mysql的性能呢?看看slow log,定期分析哪些语句慢。尝试优化。

Lighttpd监控

脚本监控是否有lighttpd进程运行,cron定期wget指定页面,看是否正常访问,类似于heartbeat。也可以统计每个web页面平均耗时,有目的的优化。

memcached监控

脚本监控是否有memcached进程运行,定期查看其hit率。

硬盘备份

除了数据库之外,还需要将无法恢复的数据备份,比如code,应该是上传到svn服务器上的。而image也是需要备份的。

Log分析,监控pv

据说google analisy很好用,以后可以考虑使用。暂时做log分析,监控pv变化。

还有一些名词,记录下来备用。

smtp可以运行在待监控机器上,由其他监控程序收集它的数据,生成数据图。

cacti是一个很强大的图形化的监控工具。需要mysql、apache等配合,它作为数据中心。

nagios、mrgt也是图形化的监控工具。

BGP带宽,可以使网通和电信用户访问都很快。

做负载均衡,可以使应用层的,使用nginx、haproxy;也可以用tcp层的LVS。

一台服务器可以做硬盘阵列,不过这个我是真不懂。

sar常用参数总结

监控某个时间段内的cpu使用率
sar -u -f /var/log/sa/sa25 -s ’10:00:00′ -e ’14:30:00′
监控网络情况
sar -n DEV -f /var/log/sa/sa25  -s ’10:00:00′ -e ’15:00:00′
sar -n DEV -f /var/log/sa/sa25  -s ’10:00:00′ -e ’15:00:00′ | perl -n -e ‘if(m/eth0/){split; print $_[0].”,”.$_[1].”,”.$_[5].”\n”;}’
监控负载
sar -q 1 1
——————————————————————————
2011-6-16添加:

这两天察觉服务器较慢,正好分析了一下。

首先,使用top命令,进入top后,按cpu使用率排序(P),发现cpu经常逼近90%,这时mysql的cpu使用最高,能达到200%以上(不明白为什么会超过100%,多核的原因?暂时不发散去追究了)。

第二步,因为原来出现过网络带宽的问题(租用的服务器,共享带宽),所以使用sar -n DEV 5 100每隔5s运行一次、共运行100次,查看带宽使用情况。

flykobe@server ~ $ sar -n DEV 5 100

14时16分45秒     IFACE   rxpck/s   txpck/s   rxbyt/s   txbyt/s   rxcmp/s   txcmp/s  rxmcst/s

14时16分50秒        lo     49.80     49.80  27443.00  27443.00      0.00      0.00      0.00

14时16分50秒      eth0    353.60    467.80  41493.00 570056.20 0.00      0.00      0.00

14时16分50秒      eth1      0.00      0.00      0.00      0.00      0.00      0.00      0.00

14时16分50秒      sit0      0.00      0.00      0.00      0.00      0.00      0.00      0.00

这里关心eth0的rxbyt/s   txbyt/s,即读入、写出字节数/秒。注意,这里的单位是byte,而服务器提供商给我们提供的网络带宽的单位是bit,需要做×8转换。这里的读写总带宽为5m,承受范围之内。

然后,再用iostat 5命令每隔5s运行一次,看看是否有io问题。发现即使是在cpu较忙碌的时候,iowait也不是很高,所以暂时排除是io问题。

avg-cpu:  %user   %nice %system %iowait  %steal   %idle

13.01    0.00    7.20   12.36    0.00   67.43

最后,用sar -q 5 100每隔5s运行一次,查看负载情况,我们是4核的服务器,所以负载别持续超过4就ok了。

ok,那就集中考虑mysql的优化问题了

——————————————————

2011-9-7 添加

sar命令依靠sa來收集信息,存儲在/var/log/sa/目錄下。 该命令依靠cron运行:

flykobe@desktop happy_itry_v2 # cat /etc/cron.d/sysstat

# run system activity accounting tool every 10 minutes

*/10 * * * * root /usr/lib/sa/sa1 1 1

# generate a daily summary of process accounting at 23:53

53 23 * * * root /usr/lib/sa/sa2 -A

当用户开始觉得你的网页缓慢,就可以着手考虑优化的问题了。

个人感觉,这应该分为两方面,一是代码架构方面的,看看有没有什么问题,二是系统级别的,是不是租用的服务器硬件不足了?

但是,真正做起来,不能只凭感觉,得借助于工具。慢,为什么慢?哪里慢?只有找出来,才可能做优化。

首先看一下系统检测工具,http://www.cyberciti.biz/tips/top-linux-monitoring-tools.html 这里列举了20个常用的linux检测工具,因为我的网站比较小,所以仅选择了一些基本的工具,比如top、ps、vmstat、iostat、netstat、iptraf、nethogs等。

安装方式就不说了,不外乎自己下载源码以及相关的依赖包,或者借助于yum、emerge之类的自动安装工具。

以下也是边摸索边记录,不一定连贯。据说,一般服务器的性能瓶颈可能为:内存大小、内存i/o、磁盘i/o、网络i/o、cpu等。

1、通过web server的status监控页面,我们的QPS其实不高。

2、top命令查看,cpu 也很空闲

3、vmstat -S m 5 查看内存使用:

flykobe@server nethogs $ vmstat -S m 5
procs ———–memory— —swap– —–io—- –system– —–cpu——
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0     36     90    186   2801    0    0    10    57    4    2  0  0 99  0  0
0  0     36     87    186   2801    0    0     0   386 1136  504  1  1 98  0  0
0  0     36     87    186   2801    0    0     0   497 1240  355  1  0 98  1  0
0  0     36     87    186   2801    0    0     0   134 1126  126  0  0 100  0  0
0  0     36     87    186   2801    0    0     0   108 1083  135  0  0 100  0  0
0  0     36     86    186   2801    0    0     0   302 1114  303  1  1 98  1  0
0  0     36     86    186   2801    0    0     0   317 1095  181  0  0 100  0  0
flykobe@server nethogs $ vmstat -S m 5 -f
522105 forks

其中,procs列出进程相关信息,r是run的进程数目,b是block的进程数目;memory列出内存信息,包括虚拟内存,swpd是已使用的虚拟内存大小,free是空闲内存大小,buff是用作buff的内存大小,cache是用作cache的内存大小,单位都是M,因为我使用了-S m命令指定了单位。swap si是每秒从硬盘交换到内存的数量,so是内存交换到硬盘的数量。io的bi是接收到的块,bo是发送的块个数,单位是块/每秒。system in是每秒中断的数目,cs是上下文交换的数目。具体如下:

Procs

r: The number of processes waiting for run time.

b: The number of processes in uninterruptible sleep.

Memory

swpd: the amount of virtual memory used.

free: the amount of idle memory.

buff: the amount of memory used as buffers.

cache: the amount of memory used as cache.

inact: the amount of inactive memory. (-a option)

active: the amount of active memory. (-a option)

Swap

si: Amount of memory swapped in from disk (/s).

so: Amount of memory swapped to disk (/s).

IO

bi: Blocks received from a block device (blocks/s).

bo: Blocks sent to a block device (blocks/s).

System

in: The number of interrupts per second, including the clock.

cs: The number of context switches per second.

CPU
These are percentages of total CPU time.
us: Time spent running non-kernel code. (user time, including nice time)
sy: Time spent running kernel code. (system time)
id: Time spent idle. Prior to Linux 2.5.41, this includes IO-wait time.
wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle.
st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown.

从cpu、内存角度考虑,我们服务器应该是没有问题的。

另外,查看具体进程的内存占用:

ps axu|grep ‘php-cgi\|PID’|less  其中RSS列是内存使用,单位是kB。

4、再利用iostat看下磁盘i/o是否有问题:

flykobe@server nethogs $ iostat -dx

Linux 2.6.18-194.el5 (localhost.localdomain)    2011年01月25日

Device:         rrqm/s   wrqm/s   r/s   w/s rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util

sda               0.10     9.43  0.30  9.54    40.89   151.77    19.57     0.09    8.97   0.65   0.64

sdb               0.10     9.42  0.30  9.55    40.99   151.77    19.56     0.09    8.92   0.63   0.62

dm-0              0.00     0.00  0.78 18.97    81.77   151.77    11.82     0.79   39.97   0.41   0.80

dm-1              0.00     0.00  0.00  0.00     0.00     0.00     2.01     0.00    3.14   0.47   0.00

dm-2              0.00     0.00  0.78 18.97    81.77   151.77    11.82     0.79   39.97   0.41   0.80

dm-3              0.00     0.00  0.78 18.97    81.76   151.74    11.83     0.79   39.97   0.41   0.80

dm-4              0.00     0.00  0.00  0.00     0.01     0.03     8.00     0.00   53.95   2.36   0.00

需要关注的是r/s、w/s列,代表每秒read、write操作的次数,那么对于我们的服务器,为什么会是写操作反而大于读操作呢?

但是,从这里的数据显示,应该并不算是一个忙碌的磁盘i/o系统,所以暂时先不管它。

对磁盘读写测速:

flykobe@server ~ $ cat /etc/fstab

/dev/VolGroup00/LogVol00 /                       ext3    defaults        1 1

LABEL=/boot             /boot                   ext3    defaults        1 2

tmpfs                   /dev/shm                tmpfs   defaults        0 0

devpts                  /dev/pts                devpts  gid=5,mode=620  0 0

sysfs                   /sys                    sysfs   defaults        0 0

proc                    /proc                   proc    defaults        0 0

/dev/VolGroup00/LogVol01 swap                    swap    defaults        0 0

flykobe@server ~ $ sudo /sbin/hdparm  -t /dev/VolGroup00/LogVol00

/dev/VolGroup00/LogVol00:

Timing buffered disk reads:  136 MB in  4.76 seconds =  28.55 MB/sec

5、再使用netstat命令,得到网络的基本情况:

flykobe@server nethogs $ netstat -Ieth0 -e

Kernel Interface table

eth0      Link encap:Ethernet  HWaddr 00:15:17:B6:E0:B4

inet addr:202.85.221.240  Bcast:202.85.221.255  Mask:255.255.255.128

inet6 addr: fe80::215:17ff:feb6:e0b4/64 Scope:Link

UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

RX packets:111512895 errors:0 dropped:0 overruns:0 frame:0

TX packets:153435934 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

RX bytes:16224517492 (15.1 GiB)  TX bytes:185118605369 (172.4 GiB)

Memory:b8820000-b8840000

其中RX packets代表收到的包数目,TX packets代表发送的包数目。

而要查看当前的连接情况,可以使用sudo netstat -tulpn。

这里,我看不出问题,因为我们更应该关心的是网络带宽和流量,所以再借助于iptraf命令,它是一个图形化(命令行模式下的图形,蓝屏的)的监测网络状态的命令。它有5种模式:

| IP traffic monitor              |

| General interface statistics    |

| Detailed interface statistics   |

| Statistical breakdowns…       |

| LAN station monitor  |

它可以获得每秒接收和发送的流量,像我们,针对80端口(web端口),in的带宽占用是64kbits/s,out的带宽占用是1270kbit/s。虽然并不大,但是鉴于我们的bandwidth目前仅2.4m,则很容易就造成带宽不足了。我测试了一下,当访问网站上的一个flash时,out的带宽轻易到达2m以上。

另外,到达是哪个进程占用了网络资源呢?我想当然的认为是web server,但是仍需要验证一下,这时就可以使用nethogs了。

NetHogs version 0.7.0

PID USER     PROGRAM                      DEV        SENT      RECEIVED

14077 wwwrun   /opt/lighttpd/sbin/lighttpd  eth0       2.392       0.813 KB/sec

0     root     ..0:39029-123.125.106.19:80             0.129       0.594 KB/sec

0     root     ..240:80-117.87.238.87:3364             6.895       0.324 KB/sec

6195  yicheng  sshd: yicheng@pts/1          eth0       1.172       0.188 KB/sec

0     root     ..40:80-116.231.16.207:4425             0.119       0.131 KB/sec

0     root     ..0:39026-123.125.106.19:80             0.108       0.108 KB/sec

0     root     ..0:39028-123.125.106.19:80             0.108       0.108 KB/sec

0     root     ..0:39027-123.125.106.19:80             0.108       0.108 KB/sec

6848  yicheng  sshd: yicheng@pts/2          eth0       0.306       0.035 KB/sec

0     root     ..0:80-58.216.156.134:14843             0.000       0.012 KB/sec

0     root     ..240:80-222.247.56.14:1417             0.011       0.012 KB/sec

0     root     ..40:80-116.231.16.207:4322             0.011       0.012 KB/sec

0     root     unknown TCP                             0.000       0.000 KB/sec

TOTAL                                               11.358       2.443 KB/sec

那么如果说瓶颈真的出在网络i/o上,解决方法是什么呢?

最简单的单例模式,用php实现代码如下:

class S{
        static private $instance;
        private $name;
        private function __construct($name){
                $this->name = $name;
        }

        static function get_instance($name){
                if (!self::$instance){
                        self::$instance = new S($name);
                }
                return self::$instance;
        }
}
$p = S::get_instance('abc');
var_dump($p);
$q = S::get_instance('abc');
var_dump($q);
$l = S::get_instance('abcd');
var_dump($l);

但是由之后的测试代码可以看到,即使用不同的初始化参数,得到的都是同一个object。这对于像数据库连接之类的操作来说,显然是不可行的。

object(S)#1 (1) {
[“name:private”]=>
string(3) “abc”
}
object(S)#1 (1) {
[“name:private”]=>
string(3) “abc”
}
object(S)#1 (1) {
[“name:private”]=>
string(3) “abc”
}

我们只需要做简单的变化,就可以实现这一需求:

class P{
        private $name;
        static private $instances = array();

        private function __construct($name){
                $this->name = $name;
        }

        static function get_instance($name){
                if(empty(self::$instances[$name])){
                        self::$instances[$name] = new P($name);
                }
                return self::$instances[$name];
        }
}

再次测试可以看到,p和q指向同一object,而l指向了另外object:

object(P)#1 (1) {

[“name:private”]=>

string(3) “abc”

}

object(P)#1 (1) {

[“name:private”]=>

string(3) “abc”

}

object(P)#2 (1) {

[“name:private”]=>

string(4) “abcd”

}

再继续,如果class P被继承,继承的类仍然要保持单例,例如:

class C extends P{

}

$c = C::get_instance('abc');

var_dump($c);

这时,可以看到$c仍然是P的object,因为在P::get_instance中我们是显示写明了new P的。所以,只要能够修改get_instance方法,使其能够得到真正想被实例化的类即可。可以重载class C的get_instance方法,但是太麻烦,这就意味着,我们每增加一个子类,就要多一个code copy和修改的过程。

对于php 5.3之后的代码,有get_called_class方法可以使用, 能够得到运行时的类名。5.3之前的,虽然没有该函数,但是已经有人封装实现了该功能:

if(!function_exists('get_called_class')) {
function get_called_class() {
    $matches=array();
    $bt = debug_backtrace();
    $l = 0;
    do {
        $l++;
        if(isset($bt[$l]['class']) AND !empty($bt[$l]['class'])) {
            return $bt[$l]['class'];
        }
        $lines = file($bt[$l]['file']);
        $callerLine = $lines[$bt[$l]['line']-1];
        preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l]['function'].'/',
                   $callerLine,
                   $matches);
        if (!isset($matches[1])) $matches[1]=NULL; //for notices
        if ($matches[1] == 'self') {
               $line = $bt[$l]['line']-1;
               while ($line > 0 && strpos($lines[$line], 'class') === false) {
                   $line--;
               }
               preg_match('/class[\s]+(.+?)[\s]+/si', $lines[$line], $matches);
       }
    }
    while ($matches[1] == 'parent'  && $matches[1]);
    return $matches[1];
}
}

所以修改类P的get_instance method如下:
       static function get_instance($name){
                $class_name = get_called_class();

                $str = $class_name.$name;
                if(empty(self::$instances[$str])){
                        self::$instances[$str] = new $class_name($name);
                }
                return self::$instances[$str];
        }

注意,其中的instances的key,不但使用了用户传入的参数,而且使用了called class的名称,这样,即使有多个子类,也不会互相冲突。

ctags –regex-PHP=’/abstract class ([^ ]*)/\1/c/’  –regex-PHP=’/interface ([^ ]*)/\1/c/’ –regex-PHP=’/(public |static |abstract |protected |private )+function ([^ (]*)/\2/f/’ –totals=yes –langmap=PHP:+.inc -R *

如果没有以上的regex,则abstract class等就不会被ctags收录,从而在vim中就无法跳转。以上顺序相关,要先regex-PHP,再langmap,否则无效。

将以上作为vim的配置时,出错,还没空找解决方案。

web页面显示 Mysql connect fail, too many connections。

web server用的是lighttpd,查看其状态:http://hostname/pma-server-status并没有太多的连接。

这时,尝试使用普通账号登陆失败:mysql -u mysql -p,依然报too many connections。

在mysql的官方手册上http://dev.mysql.com/doc/refman/5.5/en/too-many-connections.html可以看到,当连接数过多的时候,仅超级用户才能登陆,所以改用root用户连接mysql。

连接上之后,使用show processlist;命令查看当前的连接情况,发现101个sleep状态的查询。这里面存在的问题是:

1、作为一个生产数据库,将max_connection设为100(可以通过show variables like ‘max%’看到)有点太少

2、大量sleep状态的query是否正常?

作为临时解决方案,可以在mysql里kill掉一些sleep的query(mysql>kill  query_id)。也可以暂时更改其max_con数目和允许的连接时间(过小也不行,一次访问未结束就被kill掉了):

mysql> set global max_connections = 500;

mysql> set global wait_timeout = 1000;

为了彻底解决,需要修改mysql的配置文件,将max_connection调大一些,比如500,然后重启mysql。

当然,更重要的是查看代码中是否存在bug。

Continue reading ‘MySql too many connections解决方案’ »

最近尝试用了用php的面向对象编程,当我试图用json_encode序列化一个对象数组时,发现结果是:[{}]

示例代码如下:

class P{
        private $a;
        private $b;

        // Magic get and set/*{{{*/
        public function __get($name){
                if (property_exists($this, $name)){
                        return $this->$name;
                } else {
                        throw new Exception("Get unknown property: $name");
                }
        }

        public function __set($name, $val/*mix*/){
                if (property_exists($this, $name)){
                        $this->$name = $val;
                } else {
                        throw new Exception("Set unknown property: $name(".json_encode($val).")");
                }
        }/*}}}*/

        public function prop(){
                return get_object_vars($this);
        }
}
$p = new P;
$p->a = 1;
var_dump($p);
var_dump(json_encode(array($p)));
var_dump($p->prop());

我的类P没有public vars,在php代码中,访问$p->a会自动调用magic method __get去获取。但是在json_encode中,它作为php扩展,是陷入到C代码中执行,看不到用户层面定义的__get,所以认为$p是一个空对象。

网上看到有用php封装一些json方法的,但是担心序列化大批量的会有性能问题,所以放弃,将数据类的vars都改为public的了。

另,get_object_vars在类内部调用,可以拿到所有的vars,包括private的。在类外调用,仅能拿到public的。