您的位置:首页 > 编程语言 > PHP开发

session_write_close() PHP在访问Session数据时存在互斥情况,导致Ajax请求响应缓慢

2012-11-16 09:18 465 查看
概要:PHP在访问Session数据时存在互斥情况,导致Ajax请求响应缓慢。

一,问题分析:

最近在统计网站请求响应时间时,发现有很多请求时间超过0.5秒,login/info最为明显。

经过分析,login/info本身操作并不复杂,读取Session中的数据,查询一次数据库,然后返回。

进一步跟踪发现,PHP在访问Session数据时,首先需要获取到竞争的锁,否则就会Sleep一段时间,然后重试。

1,没有竞争情况下的过程跟踪:

sendto(4, "incr 5q8of2888he4b7986oqu947qv4.lock 1\r\n", 40, MSG_NOSIGNAL, NULL, 0) = 40

sendto(4, "add 5q8of2888he4b7986oqu947qv4.lock 768 15 1\r\n1\r\n", 49, MSG_NOSIGNAL, NULL, 0) = 49

sendto(4, "get 5q8of2888he4b7986oqu947qv4\r\n", 32, MSG_NOSIGNAL, NULL, 0) = 32

select(5, [4], [], NULL, {1, 0}) = 1 (in [4], left {1, 0})

# 有两种情况可以成功获取锁:1,incr失败(NOT_FOUND)并且add成功,2,锁的原始值是0,incr返回1。

# 下面是第二种情况。

recvfrom(4, " 1
\r\nNOT_STORED\r\n ", 32768, 0, NULL, NULL) = 15

recvfrom(4, "VALUE 5q8of2888he4b7986oqu947qv4 0 0\r\n\r\nEND\r\n", 32768, 0, NULL, NULL) = 45

2,存在竞争情况下的过程跟踪:

17692 sendto(4, "incr 3qvs0ci9nplv43baail60cdbm5.lock 1\r\n", 40, MSG_NOSIGNAL, NULL, 0) = 40

17692 sendto(4, "add 3qvs0ci9nplv43baail60cdbm5.lock 768 15 1\r\n1\r\n", 49, MSG_NOSIGNAL, NULL, 0) = 49

17692 sendto(4, "get 3qvs0ci9nplv43baail60cdbm5\r\n", 32, MSG_NOSIGNAL, NULL, 0) = 32

17692 select(5, [4], [], NULL, {1, 0}) = 1 (in [4], left {1, 0})

# 锁已经存在,incr返回2,表明锁已经被其他进程占有。

# Sleep 5ms后重试。

17692 recvfrom(4, "2\r\nNOT_STORED\r\n", 32768, 0, NULL, NULL) = 15

17692 recvfrom(4, "VALUE 3qvs0ci9nplv43baail60cdbm5 0 0\r\n\r\nEND\r\n", 32768, 0, NULL, NULL) = 45

17692 nanosleep({0, 5000000}, NULL) = 0

17692 select(5, [4], [4], NULL, {1, 0}) = 1 (out [4], left {1, 0})

17692 sendto(4, "incr 3qvs0ci9nplv43baail60cdbm5.lock 1\r\n", 40, MSG_NOSIGNAL, NULL, 0) = 40

17692 sendto(4, "add 3qvs0ci9nplv43baail60cdbm5.lock 768 15 1\r\n1\r\n", 49, MSG_NOSIGNAL, NULL, 0) = 49

17692 sendto(4, "get 3qvs0ci9nplv43baail60cdbm5\r\n", 32, MSG_NOSIGNAL, NULL, 0) = 32

17692 select(5, [4], [], NULL, {1, 0}) = 1 (in [4], left {0, 996000})

# 依旧没有成功,Sleep 10ms后重试

17692 recvfrom(4, "3\r\nNOT_STORED\r\nVALUE 3qvs0ci9nplv43baail60cdbm5 0 0\r\n\r\nEND\r\n", 32768, 0, NULL, NULL) = 60

17692 nanosleep({0, 10000000}, NULL) = 0

17692 select(5, [4], [4], NULL, {1, 0}) = 1 (out [4], left {1, 0})

17692 sendto(4, "incr 3qvs0ci9nplv43baail60cdbm5.lock 1\r\n", 40, MSG_NOSIGNAL, NULL, 0) = 40

17692 sendto(4, "add 3qvs0ci9nplv43baail60cdbm5.lock 768 15 1\r\n1\r\n", 49, MSG_NOSIGNAL, NULL, 0) = 49

17692 sendto(4, "get 3qvs0ci9nplv43baail60cdbm5\r\n", 32, MSG_NOSIGNAL, NULL, 0) = 32

17692 select(5, [4], [], NULL, {1, 0}) = 1 (in [4], left {0, 998000})

# 依旧没有成功,Sleep 20ms后重试

17692 recvfrom(4, "4\r\n", 32768, 0, NULL, NULL) = 3

17692 recvfrom(4, "NOT_STORED\r\nVALUE 3qvs0ci9nplv43baail60cdbm5 0 0\r\n\r\nEND\r\n", 32768, 0, NULL, NULL) = 57

17692 nanosleep({0, 20000000}, NULL) = 0

17692 select(5, [4], [4], NULL, {1, 0}) = 1 (out [4], left {1, 0})

17692 sendto(4, "incr 3qvs0ci9nplv43baail60cdbm5.lock 1\r\n", 40, MSG_NOSIGNAL, NULL, 0) = 40

17692 sendto(4, "add 3qvs0ci9nplv43baail60cdbm5.lock 768 15 1\r\n1\r\n", 49, MSG_NOSIGNAL, NULL, 0) = 49

17692 sendto(4, "get 3qvs0ci9nplv43baail60cdbm5\r\n", 32, MSG_NOSIGNAL, NULL, 0) = 32

17692 select(5, [4], [], NULL, {1, 0}) = 1 (in [4], left {1, 0})

# 获取成功,incr返回1,表明其他进程已经释放锁。

17692 recvfrom(4, "1\r\nNOT_STORED\r\nVALUE 3qvs0ci9nplv43baail60cdbm5 0 0\r\n\r\nEND\r\n", 32768, 0, NULL, NULL) = 60

经过统计,目前有30%左右的请求都存在Session锁竞争的情况,造成请求响应缓慢。

部分请求由于锁竞争导致延迟的时间超过200 ms。

二,解决方案

锁机制集成在PHP的Memcache Session模块中,并且是互斥锁而不是读写锁。

只读操作也需要获取互斥锁后才能完成。

造成锁竞争的根本原因是程序持有锁的时间太长。根本的解决方案是及时释放锁。

目前我们的程序基本流程如下:

(1)加载Session模块,执行session_start(),此时程序就开始持有锁。

(2)访问Session中的数据,例如member信息。

(3)执行正常业务逻辑,访问数据库,渲染页面等。

(4)程序结束,此时程序自动释放Session的锁。

其中第(3)个步骤耗时最长,大部分情况下,在这个步骤,已经不需要访问Session数据,已经可以释放锁。

具体改进:

1,修改Session类的构造函数,session_start后复制$_SESSION数据到对象内部变量,然后立即调用session_write_close。

2,只读操作的方法,例如userdata,从对象内部变量获取数据。

3,写入操作的方法,例如set_userdata,先调用session_start,然后更新$_SESSION和内部变量,然后立即调用session_write_close。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: