前言: 我们的项目要频繁读写遍历缓存,起初用的Redis,CPU占用20%左右,显然不理想。 当接触到php自带的apcu后,简直就是神一样,单机效率超Redis几十甚至上百倍, 利用workerman的text协议来搭配apcu 也能秒Redis 2倍以上, 关键是...关键是...关键是CPU消耗不足redis十分之一, 上测试截图:
//走tcp
真的假的,比redis都高?
牛呀
如果只作为单纯的进程间共享变量函数以及类,可说是非常完美,高Redis百倍性能。 如果作为kv缓存的话有一些特殊情况,大致根据自己项目情况来选择, 第一种:对单key的过期时间有要求的,在遍历全部key会包含已过期的key且无法分辨过期和未过期。 第二种:对遍历全部key只要有效key的话,单key无法自动过期。 对于key未设置过期的不影响,这些是运行在cli下才有的问题,fpm下不存在能完美Redis特性并秒杀百倍性能。
对于以上的问题已解决,用git仓库源码编译(pecl.php.net下载的不行),需配置use_request_time=0,灰常的奈斯
您好,上面说的问题,在fpm模式下都不存在是吗,我们现在还是在fpm模式,最近我想着用apcu,至于无法共享cache的问题,我想着定时十分钟淘汰就好了,每隔十分钟去mysql或者redis拿一次数据
@贾亭西 从git拉取编译不管是fpm还是cli都没问题了
好的,谢了
apcu实际上已经边缘化了,只能单进程,整体的设计更适合fpm,在cli下面有挺多问题的,包括过期时间等,甚至不如shmop,早期shmop是为cli下设计的; 不考虑分布式、持久化容错的话,用shmop会更好,memcache也可以,性能都比redis更好;毕竟webman/workerman都常驻内存了,连接可以持久化。 早期实现过shmop/apcu + 边车定时器进程进行过期时间处理,实话实说,那个性能没法看,尤其是堆积数据过大的时候,时间复杂度毕竟是O(n),而且单进程的定时器在堆积数据过大的情况下也存在延时。
当然,这样玩一玩,了解实践一些服务层的知识肯定是好处,有益的
而且其实这里面我们还得出一个实践,用持久化连接的SQLite3的memory也非常快,和缓存是类似的,用file也可以达到redis的持久化效果,而且还带事务,当然肯定和纯内存是没办法比的;我们后续的很多小组件都是通过RPC + SQLite3进行的数据存储和缓存处理,相当稳定。
最主要的是SQLite3非常稳定,切具备工业化标准,PHP自带该拓展库无需额外安装及配置
cli下和fpm使用一致,并没有过期时间的问题,pecl.php.net下载的存在时间过期问题,从仓库拉源码编译是正常的,当然我们修改了源码增加了一个时间字段来遍历时更好的过滤失效key,我们已应用在生产环境。不考虑分布式以及持久化,只用简单的kv缓存的话,apcu就是神一样的存在。
在单机下,apcu可以完美替代workerman提供的globalData 实现多进程变量共享,且是globalData的几十倍效率。
不考虑持久化和分布式的话,其实常驻内存框架可以直接用php的静态变量,其中还可以使用Spl的一些数据结构,比如堆、优先队列,在处理包含资源类型的缓存的时候比apcu更有竞争力,而且性能更好,你们可以在8.0及以上的php版本测试一下,效果是比apcu更好的
都用php了还考虑啥性能,要性能用golang吧!
下图是我们生产环境中的各种方案,起初我们把后端用go+redis和go+自写cache,并发上来后都不如fpm+apcu,后来又使用了workerman+redis,起初也一样,无法完美使用apcu,导致Redis有些扛不住,又切换到了fpm+apcu,然后我们前端APP要websocket,没办法又用回了workerman+redis,不死心去研究了下apcu源码,最后就是workerman+apcu,终于比fpm+apcu强了。
go有两个节点飚的好高
Redis 为 127.0.0.1 本机,该服务端的Redis还存在IO瓶颈吗? 手动狗头
有道理啊,换成本机redis呢,,或者直接存workman 进程内存。。岂不是更快
redis只要走网络就会存在内核态和用户态的拷贝,就会被redis的epoll影响,apcu是走的共享内存,并不会存在内核态和用户态的拷贝问题,是比redis更快的
关注一下
谢谢作者和 chaz6chez的分享.
是这个源码地址吗?有谁教教我怎么编译。https://github.com/krakjoe/apcu
拉取到服务器,cd到源码目录,依次执行以下几个步骤 /server/php/bin/phpize (你的phpize路径) ./configure --with-php-config=/server/php/bin/php-config (你的php-config路径) make && make install
最后在php.ini里添加apcu的配置 [apcu] apc.enabled=1 apc.shm_segments=1 apc.shm_size=512M apc.entries_hint=0 apc.ttl=0 apc.gc_ttl=5 apc.mmap_file_mask= apc.slam_defense=0 apc.enable_cli=1 apc.use_request_time=0 apc.serializer="php" apc.coredump_unmap=0 apc.preload_path= ; ; extension=apcu.so
赞!
这是安装方法吗?
workerman本身已经常驻内存了,一步到位,直接使用全局变量岂不是更快! 程序启动时初始化全局变量:$a=[]; 写入/修改: $a["key"]="value"; $a["key2"]="value2";... 读取: $b=$a["key"];... 删除: unset($a["key"]);
全局变量的话进程与进程之间无法共享,主进程的变量子进程可读不可写
apcu本身也是单进程单线程设计的,多个主进程间或者多个子进程间apcu缓存不支持共享,每个进程都有独立的apcu缓存
如果仅提供单进程单线程服务,高并发且频繁读写增删缓存,例如实时游戏等追求极致性能的实例,不用想,使用全局变量肯定比其他外部扩展快
例如: <?php //error_reporting(0); use Workerman\Worker; use Workerman\Connection\TcpConnection; use Workerman\Timer;
require_once __DIR__ . '/vendor/autoload.php'; $worker = new Worker("websocket://0.0.0.0:10001"); // 创建websocket $worker->name = "桀桀桀桀"; $worker->count = 1; // 启动1个进程 如果开启多个进程,则每个进程都会产生独立的缓存,业务中如果提供向指定用户推送消息且开启多进程,同进程推送成功否则失败,进程间缓存不共享(在A鱼塘钓不了B鱼塘的罗飞鱼)
//$worker启动时 $worker->onWorkerStart = function () { //初始化MySQL redis等 global $test; //桀桀桀桀桀 全局变量 $test=[]; //初始化全局变量
//定时任务 每隔1800秒同步一次 $time_interval = 1800; Timer::add($time_interval, function () { global $test; //各种逻辑 然后操作$test }
} //用户握手连接时初始化用户数据 $worker->onConnect = function (TcpConnection $connection) { $connection->onWebSocketConnect = function (TcpConnection $connection, $request) { global $test; //从连接参数中获取用户数据 各种逻辑 然后操作$test }; }; //收发数据 $worker->onMessage = function (TcpConnection $connection, $data) { global $test; //业务逻辑 操作$test } // 用户连接断开时 $worker->onClose = function (TcpConnection $connection) { global $test; //各种逻辑 然后操作$test } Worker::runAll(); ?>
看业务情况只需修改下cli的php.ini脚本内存限制,如:memory_limit=256M
workerman主进程下不管多少个子进程,都是共享的,均可读写,并发能力也超棒
apcu可以开启mmap内存映射,非亲缘进程也可以共享,底层使用的是mmap()函数实现的
向大佬们学习!
最近在研究这个,https://www.workerman.net/plugin/133 ,可以看看,应该能用得上
他这个加锁我没搞明白,apcu内部已经有读锁机制,他再套一层是为啥?
对于map的操作,需要原子性加锁,因为是二级内容的修改,比如update
我觉得没这个必要,直接apcu_store就可以了,加锁apcu已经在内部有操作,我们只管用,随意并发写。
很多时候上层需要一个原子性操作来保证业务原子性,apcu底层提供了锁来保证apcu自己的函数具备原子性,但是上层的封装需要多次调用一个或N个apcu函数,需要保证上层业务的原子性,所以需要用到apcu提供的原子性锁,有些地方需要阻塞等待式调用,所以需要自行在此基础上实现抢占式锁来保证原子性和业务完整性
多进程下,AB两个进程在每一个apcu的函数上肯定是原子性的,但是有些时候A上面有多个apcu操作,B也有多个apcu函数操作,相互之间需要各自的原子性,如果不加锁,这些apcu函数会在apcu实际执行中穿插执行,并不能保证原子性,这个是测试后得到的结果,毕竟apcu只保证自己函数的原子性
apc在写入的时候,内部总是以 锁->写->释放的过程,不管你的有多少个进程同时操作,这不会变,也可以理解为阻塞写入,在业务代码上再套一层锁,真有必要吗? 麻烦分享下测试过程,这对我们很重要,目前没遇到过并发安全性问题,我们项目深度依赖apcu 希望能涨些知识 🙏
你没懂我的意思
假设两个进程同时执行以下操作: { 1.apuc_get('a',一些数据); 其他的一些业务结果a 2.apuc_store('a', a); 另一些业务 } 业务是一次原子性操作,业务逻辑是依赖apcu的数据
在apuc层面每一次调用都是原子性带锁的没错,会互斥没错,但是两个进程同时进行的时候,1/2的数据可能被另一个进程的操作污染,因为业务不具备原子性,那么我在上层增加atomic原子性操作,依赖自己实现的锁就可以避免这个问题,但代价就是当多个相同的进程同时执行对相同数据进行操作的时候,会互斥并排队;原理和redis的nx 或者 xx是类似的
由于apcu是非常高效的微妙级别的操作,通常来说上下文执行的过程是比apcu处理会慢的,所以很多时候感受不到这个问题,当并发量达到一定程度,或者其中业务存在一定执行时间的时候,这样的问题就会暴露出来;这样的问题就跟使用数据库对数据进行处理的时候不加事务一样,原则上是为了保证整个业务的完整性和原子性
具体场景就是当apcu储存了一个map,我需要对map里面的一个键值进行自增,那么我需要先读取出来,比如a=>1,当我利用apcu读取的时候可能是1,在我自增写入的时候我期望是2,但在这时候其他亲缘进程可能已经先于我操作了,就在我读取并自增的间隙内,已经自增到了2,那么最后我的结果还是2,不能保证是3;因为apcu的每次函数调用是一个原子性操作,但是很多业务是需要多次apcu的函数调用来完成一次原子性操作的
a进程map自增,b进程map也自增,存在ab进程都读取的是1,ab进程最后的结果都是2,而不是一个是2一个是3
原来存入的map,这下就明白了,我们用不到这种场景,我们都是直接存单key,比如5万台车需要实时上传位置,每台车一个key,这种场景下用map效率比单key低。
还有这个插件支持redis的nx和xx这两个场景,都需要锁+抢占式循环来进行实现,其他的业务实际上没有用到锁;包括后续的业务需要对mmap进行支持,我也在想办法如何对内存的变动进行有效的监听
老哥说的很详细,给我就解惑了。我现在有的项目是接收大量的http报文请求,然后分析以后存储到当前进程的内存变量二维数组中,通过条数积累到一定数量后统一上报给后端某个服务中。但是过程中出现了一些问题,比如由于要保证接收http报文的性能,所以开了多个进程,而进程隔离导致无法判断两个进程中的两份报文是否有重复的。所以打算优化这个操作,使用apcu共享内存,但是新的问题又出现了,每个进程中都要判断当前报文的条数是否符合上报的要求,然后符合要求的进行上报,上报完成后清空当前key,重复下一次报文积累,这样一定会出现并发的问题,比如上报了两次,或者上报的条数超过了等等并发问题。 我现在使用上述包试试,使用对上报、apcu的查询、存储进行加锁试试。
@xiaopi 不要存数组,存单key,给个识别的前缀标识,单开一个进程定时去遍历key进行上报即可,存数组这种形式明显不合理且性能低。
@army 感谢提醒,不过我这个项目对上报的时效性要求比较高,原先的逻辑是存在静态数组中,然后如果数组长度达到100或者10s中后,自动上报,所以是放在Timer定时器中的。改成apcu全局缓存后,请教一下老哥,如果单进程上报会不会效率比较低啊,如果多进程还是要加锁的吧? 还有一个是存单key的话,不太好获取上报的条数吧? 用的是这个扩展 https://www.workerman.net/plugin/133 不过里面提供了search方法,还不知道效率如何
\Workbunny\WebmanSharedCache\Cache::Search('/^abc.+$/', function ($key, $value) use (&$result) { $result[$key] = $value; }, 50);
@xiaopi 我认为跟我的场景差不多,我说下我的场景是怎么实现的,应该对你有帮助。 我这有5万台车实时上报位置,我将每台车的位置信息用apcu去缓存,set key为:“AAA:LINE:DRIVER:车牌号:经纬度“,也就是不断的向缓存中去set,单开了一个进程专门跑timer定时器,每2秒遍历一次apcu的key,组合成inert语句入库,整体下来5万台车的实时位置只需要每2秒执行一次sql,效率高得离谱,存单key不用考虑抢占锁什么的问题,希望能给到你思路。
@army 感谢,单进程遍历5万个key,这个过程是阻塞的吧,包括插入数据库的IO都是阻塞的,那么2秒钟之内可以完成这些操作吗,会不会导致Timer不准了啊,实际上不是间隔2s了。 还有单开进程指的是在worker进程中指定某个进程专门用来上报的,还是process中自定义的task进程啊? 如果自定义的task进程可以访问到worker进程set的全局缓存吗,我记得有亲缘性的进程才可以访问吧,我没测试过
感谢
@xiaopi 是阻塞的,所以要单开进程去跑,但是遍历+入库几百毫秒就完成了。单开进程是指 new 一个或多个Worker专门用来跑task
应该对比一下memcache,看看是不是还是这个情况。
memcache也涉及到网络和事件驱动库,效率还是没有apcu高
单走一个6
真的假的,比redis都高?
牛呀
如果只作为单纯的进程间共享变量函数以及类,可说是非常完美,高Redis百倍性能。
如果作为kv缓存的话有一些特殊情况,大致根据自己项目情况来选择,
第一种:对单key的过期时间有要求的,在遍历全部key会包含已过期的key且无法分辨过期和未过期。
第二种:对遍历全部key只要有效key的话,单key无法自动过期。
对于key未设置过期的不影响,这些是运行在cli下才有的问题,fpm下不存在能完美Redis特性并秒杀百倍性能。
对于以上的问题已解决,用git仓库源码编译(pecl.php.net下载的不行),需配置use_request_time=0,灰常的奈斯
您好,上面说的问题,在fpm模式下都不存在是吗,我们现在还是在fpm模式,最近我想着用apcu,至于无法共享cache的问题,我想着定时十分钟淘汰就好了,每隔十分钟去mysql或者redis拿一次数据
@贾亭西 从git拉取编译不管是fpm还是cli都没问题了
好的,谢了
apcu实际上已经边缘化了,只能单进程,整体的设计更适合fpm,在cli下面有挺多问题的,包括过期时间等,甚至不如shmop,早期shmop是为cli下设计的;
不考虑分布式、持久化容错的话,用shmop会更好,memcache也可以,性能都比redis更好;毕竟webman/workerman都常驻内存了,连接可以持久化。
早期实现过shmop/apcu + 边车定时器进程进行过期时间处理,实话实说,那个性能没法看,尤其是堆积数据过大的时候,时间复杂度毕竟是O(n),而且单进程的定时器在堆积数据过大的情况下也存在延时。
当然,这样玩一玩,了解实践一些服务层的知识肯定是好处,有益的
而且其实这里面我们还得出一个实践,用持久化连接的SQLite3的memory也非常快,和缓存是类似的,用file也可以达到redis的持久化效果,而且还带事务,当然肯定和纯内存是没办法比的;我们后续的很多小组件都是通过RPC + SQLite3进行的数据存储和缓存处理,相当稳定。
最主要的是SQLite3非常稳定,切具备工业化标准,PHP自带该拓展库无需额外安装及配置
cli下和fpm使用一致,并没有过期时间的问题,pecl.php.net下载的存在时间过期问题,从仓库拉源码编译是正常的,当然我们修改了源码增加了一个时间字段来遍历时更好的过滤失效key,我们已应用在生产环境。不考虑分布式以及持久化,只用简单的kv缓存的话,apcu就是神一样的存在。
在单机下,apcu可以完美替代workerman提供的globalData 实现多进程变量共享,且是globalData的几十倍效率。
不考虑持久化和分布式的话,其实常驻内存框架可以直接用php的静态变量,其中还可以使用Spl的一些数据结构,比如堆、优先队列,在处理包含资源类型的缓存的时候比apcu更有竞争力,而且性能更好,你们可以在8.0及以上的php版本测试一下,效果是比apcu更好的
都用php了还考虑啥性能,要性能用golang吧!
下图是我们生产环境中的各种方案,起初我们把后端用go+redis和go+自写cache,并发上来后都不如fpm+apcu,后来又使用了workerman+redis,起初也一样,无法完美使用apcu,导致Redis有些扛不住,又切换到了fpm+apcu,然后我们前端APP要websocket,没办法又用回了workerman+redis,不死心去研究了下apcu源码,最后就是workerman+apcu,终于比fpm+apcu强了。
go有两个节点飚的好高
Redis 为 127.0.0.1 本机,该服务端的Redis还存在IO瓶颈吗? 手动狗头
有道理啊,换成本机redis呢,,或者直接存workman 进程内存。。岂不是更快
redis只要走网络就会存在内核态和用户态的拷贝,就会被redis的epoll影响,apcu是走的共享内存,并不会存在内核态和用户态的拷贝问题,是比redis更快的
NB关注一下
关注一下
谢谢作者和
chaz6chez的分享.
是这个源码地址吗?有谁教教我怎么编译。https://github.com/krakjoe/apcu
拉取到服务器,cd到源码目录,依次执行以下几个步骤
/server/php/bin/phpize (你的phpize路径)
./configure --with-php-config=/server/php/bin/php-config (你的php-config路径)
make && make install
最后在php.ini里添加apcu的配置
[apcu]
apc.enabled=1
apc.shm_segments=1
apc.shm_size=512M
apc.entries_hint=0
apc.ttl=0
apc.gc_ttl=5
apc.mmap_file_mask=
apc.slam_defense=0
apc.enable_cli=1
apc.use_request_time=0
apc.serializer="php"
apc.coredump_unmap=0
apc.preload_path=
;
;
extension=apcu.so
赞!
这是安装方法吗?
workerman本身已经常驻内存了,一步到位,直接使用全局变量岂不是更快!
程序启动时初始化全局变量:$a=[];
写入/修改:
$a["key"]="value";
$a["key2"]="value2";...
读取:
$b=$a["key"];...
删除:
unset($a["key"]);
全局变量的话进程与进程之间无法共享,主进程的变量子进程可读不可写
apcu本身也是单进程单线程设计的,多个主进程间或者多个子进程间apcu缓存不支持共享,每个进程都有独立的apcu缓存
如果仅提供单进程单线程服务,高并发且频繁读写增删缓存,例如实时游戏等追求极致性能的实例,不用想,使用全局变量肯定比其他外部扩展快
例如:
<?php
//error_reporting(0);
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Timer;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker("websocket://0.0.0.0:10001"); // 创建websocket
$worker->name = "桀桀桀桀";
$worker->count = 1; // 启动1个进程 如果开启多个进程,则每个进程都会产生独立的缓存,业务中如果提供向指定用户推送消息且开启多进程,同进程推送成功否则失败,进程间缓存不共享(在A鱼塘钓不了B鱼塘的罗飞鱼)
//$worker启动时
$worker->onWorkerStart = function () {
//初始化MySQL redis等
global $test; //桀桀桀桀桀 全局变量
$test=[]; //初始化全局变量
}
//用户握手连接时初始化用户数据
$worker->onConnect = function (TcpConnection $connection) {
$connection->onWebSocketConnect = function (TcpConnection $connection, $request) {
global $test;
//从连接参数中获取用户数据 各种逻辑 然后操作$test
};
};
//收发数据
$worker->onMessage = function (TcpConnection $connection, $data) {
global $test;
//业务逻辑 操作$test
}
// 用户连接断开时
$worker->onClose = function (TcpConnection $connection) {
global $test;
//各种逻辑 然后操作$test
}
Worker::runAll();
?>
看业务情况只需修改下cli的php.ini脚本内存限制,如:memory_limit=256M
workerman主进程下不管多少个子进程,都是共享的,均可读写,并发能力也超棒
apcu可以开启mmap内存映射,非亲缘进程也可以共享,底层使用的是mmap()函数实现的
向大佬们学习!
最近在研究这个,https://www.workerman.net/plugin/133 ,可以看看,应该能用得上
他这个加锁我没搞明白,apcu内部已经有读锁机制,他再套一层是为啥?
对于map的操作,需要原子性加锁,因为是二级内容的修改,比如update
我觉得没这个必要,直接apcu_store就可以了,加锁apcu已经在内部有操作,我们只管用,随意并发写。
很多时候上层需要一个原子性操作来保证业务原子性,apcu底层提供了锁来保证apcu自己的函数具备原子性,但是上层的封装需要多次调用一个或N个apcu函数,需要保证上层业务的原子性,所以需要用到apcu提供的原子性锁,有些地方需要阻塞等待式调用,所以需要自行在此基础上实现抢占式锁来保证原子性和业务完整性
多进程下,AB两个进程在每一个apcu的函数上肯定是原子性的,但是有些时候A上面有多个apcu操作,B也有多个apcu函数操作,相互之间需要各自的原子性,如果不加锁,这些apcu函数会在apcu实际执行中穿插执行,并不能保证原子性,这个是测试后得到的结果,毕竟apcu只保证自己函数的原子性
apc在写入的时候,内部总是以 锁->写->释放的过程,不管你的有多少个进程同时操作,这不会变,也可以理解为阻塞写入,在业务代码上再套一层锁,真有必要吗? 麻烦分享下测试过程,这对我们很重要,目前没遇到过并发安全性问题,我们项目深度依赖apcu 希望能涨些知识 🙏
你没懂我的意思
假设两个进程同时执行以下操作:
{
1.apuc_get('a',一些数据);
其他的一些业务结果a
2.apuc_store('a', a);
另一些业务
}
业务是一次原子性操作,业务逻辑是依赖apcu的数据
在apuc层面每一次调用都是原子性带锁的没错,会互斥没错,但是两个进程同时进行的时候,1/2的数据可能被另一个进程的操作污染,因为业务不具备原子性,那么我在上层增加atomic原子性操作,依赖自己实现的锁就可以避免这个问题,但代价就是当多个相同的进程同时执行对相同数据进行操作的时候,会互斥并排队;原理和redis的nx 或者 xx是类似的
由于apcu是非常高效的微妙级别的操作,通常来说上下文执行的过程是比apcu处理会慢的,所以很多时候感受不到这个问题,当并发量达到一定程度,或者其中业务存在一定执行时间的时候,这样的问题就会暴露出来;这样的问题就跟使用数据库对数据进行处理的时候不加事务一样,原则上是为了保证整个业务的完整性和原子性
具体场景就是当apcu储存了一个map,我需要对map里面的一个键值进行自增,那么我需要先读取出来,比如a=>1,当我利用apcu读取的时候可能是1,在我自增写入的时候我期望是2,但在这时候其他亲缘进程可能已经先于我操作了,就在我读取并自增的间隙内,已经自增到了2,那么最后我的结果还是2,不能保证是3;因为apcu的每次函数调用是一个原子性操作,但是很多业务是需要多次apcu的函数调用来完成一次原子性操作的
a进程map自增,b进程map也自增,存在ab进程都读取的是1,ab进程最后的结果都是2,而不是一个是2一个是3
原来存入的map,这下就明白了,我们用不到这种场景,我们都是直接存单key,比如5万台车需要实时上传位置,每台车一个key,这种场景下用map效率比单key低。
还有这个插件支持redis的nx和xx这两个场景,都需要锁+抢占式循环来进行实现,其他的业务实际上没有用到锁;包括后续的业务需要对mmap进行支持,我也在想办法如何对内存的变动进行有效的监听
老哥说的很详细,给我就解惑了。我现在有的项目是接收大量的http报文请求,然后分析以后存储到当前进程的内存变量二维数组中,通过条数积累到一定数量后统一上报给后端某个服务中。但是过程中出现了一些问题,比如由于要保证接收http报文的性能,所以开了多个进程,而进程隔离导致无法判断两个进程中的两份报文是否有重复的。所以打算优化这个操作,使用apcu共享内存,但是新的问题又出现了,每个进程中都要判断当前报文的条数是否符合上报的要求,然后符合要求的进行上报,上报完成后清空当前key,重复下一次报文积累,这样一定会出现并发的问题,比如上报了两次,或者上报的条数超过了等等并发问题。 我现在使用上述包试试,使用对上报、apcu的查询、存储进行加锁试试。
@xiaopi 不要存数组,存单key,给个识别的前缀标识,单开一个进程定时去遍历key进行上报即可,存数组这种形式明显不合理且性能低。
@army 感谢提醒,不过我这个项目对上报的时效性要求比较高,原先的逻辑是存在静态数组中,然后如果数组长度达到100或者10s中后,自动上报,所以是放在Timer定时器中的。改成apcu全局缓存后,请教一下老哥,如果单进程上报会不会效率比较低啊,如果多进程还是要加锁的吧? 还有一个是存单key的话,不太好获取上报的条数吧? 用的是这个扩展 https://www.workerman.net/plugin/133
不过里面提供了search方法,还不知道效率如何
默认正则匹配 - 以50条为一次分片查询
@xiaopi 我认为跟我的场景差不多,我说下我的场景是怎么实现的,应该对你有帮助。
我这有5万台车实时上报位置,我将每台车的位置信息用apcu去缓存,set key为:“AAA:LINE:DRIVER:车牌号:经纬度“,也就是不断的向缓存中去set,单开了一个进程专门跑timer定时器,每2秒遍历一次apcu的key,组合成inert语句入库,整体下来5万台车的实时位置只需要每2秒执行一次sql,效率高得离谱,存单key不用考虑抢占锁什么的问题,希望能给到你思路。
@army 感谢,单进程遍历5万个key,这个过程是阻塞的吧,包括插入数据库的IO都是阻塞的,那么2秒钟之内可以完成这些操作吗,会不会导致Timer不准了啊,实际上不是间隔2s了。 还有单开进程指的是在worker进程中指定某个进程专门用来上报的,还是process中自定义的task进程啊? 如果自定义的task进程可以访问到worker进程set的全局缓存吗,我记得有亲缘性的进程才可以访问吧,我没测试过
感谢
@xiaopi 是阻塞的,所以要单开进程去跑,但是遍历+入库几百毫秒就完成了。单开进程是指 new 一个或多个Worker专门用来跑task
应该对比一下memcache,看看是不是还是这个情况。
memcache也涉及到网络和事件驱动库,效率还是没有apcu高
单走一个6