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

概要: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。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值