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。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux中,可以使用`pthread_cond_timedwait()`函数来实现请求响应和超机制。这个函数是基于条件变量的等待函数,可以设置超间。以下是一种使用`pthread_cond_timedwait()`实现超机制的方法: 1. 创建互斥锁和条件变量:使用`pthread_mutex_init()`和`pthread_cond_init()`函数创建互斥锁和条件变量。 2. 发送请求:在发送请求之前,获取互斥锁,然后设置超间。例如,可以使用`clock_gettime()`函数获取当前间,并计算出超间点。 3. 等待响应:使用`pthread_cond_timedwait()`函数等待条件变量,并在超间内等待响应。该函数会自动释放互斥锁,并在接收到信号或超重新获取互斥锁。 4. 处理响应:如果在超间内收到了响应,处理响应数据。如果超过了超间仍未收到响应,则执行相应的超处理逻辑。 5. 销毁互斥锁和条件变量:在不再需要,使用`pthread_mutex_destroy()`和`pthread_cond_destroy()`销毁互斥锁和条件变量。 下面是一个简单的示例代码: ```c pthread_mutex_t mutex; pthread_cond_t cond; struct timespec timeout; // 初始化互斥锁和条件变量 pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); // 设置超间 clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec += 5; // 设置超间为5秒 // 获取互斥锁 pthread_mutex_lock(&mutex); // 等待响应或超 int result = pthread_cond_timedwait(&cond, &mutex, &timeout); if (result == 0) { // 收到响应 // 处理响应数据 } else if (result == ETIMEDOUT) { // 超处理逻辑 } // 释放互斥锁 pthread_mutex_unlock(&mutex); // 销毁互斥锁和条件变量 pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); ``` 使用`pthread_cond_timedwait()`函数可以在等待期间设置超间,从而实现请求响应和超机制的控制。根据具体需求,可以在超发生执行相应的处理逻辑,以确保程序的稳定和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值