多进程共享数据

由于PHP语言不支持多线程,因此Swoole使用多进程模式。在多进程模式下存在进程内存隔离,在工作进程内修改global全局变量和超全局变量时,在其他进程是无效的。

进程隔离

$fds = array();
$server->on('connect', function ($server, $fd){
    echo "connection open: {$fd}\n";
    global $fds;
    $fds[] = $fd;
    var_dump($fds);
});

$fds 虽然是全局变量,但只在当前的进程内有效。Swoole服务器底层会创建多个Worker进程,在var_dump($fds)打印出来的值,只有部分连接的fd

对应的解决方案就是使用外部存储服务:

  • 数据库,如:MySQLMongoDB
  • 缓存服务器,如:RedisMemcache
  • 磁盘文件,多进程并发读写时需要加锁

普通的数据库和磁盘文件操作,存在较多IO等待时间。因此推荐使用:

  • Redis 内存数据库,读写速度非常快
  • /dev/shm 内存文件系统,读写操作全部在内存中完成,无IO消耗,性能极高

除了使用存储之外,还可以使用共享内存来保存数据

共享内存

PHP提供了多套共享内存的扩展,但实际上真正在实际项目中可用的并不多。

shm 扩展

提供了shm_put_var/shm_get_var共享内存读写方法。但其底层实现使用链表结构,在保存大量数值时时间复杂度为O(N),性能非常差。并且读写数据没有加锁,存在数据同步问题,需要使用者自行加锁。

不推荐使用

shmop 扩展

提供了shmop_read/shmop_write共享内存读写方法。仅提供了基础的共享内存操作指令,并未提供数据结构和封装。不适合普通开发者使用。

不推荐使用

apcu 扩展

提供了apc_fetch/apc_store可以使用Key-Value方式访问。APC扩展总体上是可以用于实际项目的,缺点是锁的粒度较粗,在大量并发读写操作时锁的碰撞较为密集。

yac扩展,不适合用于保存数据,其设计原理导致存在一定的数据miss率,仅作为缓存,不可作为存储

swoole_table

Swoole官方提供的共享内存读写工具,提供了Key-Value操作方式,使用非常简单。底层使用自旋锁实现,在大量并发读写操作时性能依然非常强劲。推荐使用。swoole_table仍然存在一个两个缺点,使用时需要根据实际情况来选择。

  • 提前申请内存,swoole_table在使用前就需要分配好内存,可能会占用较多内存
  • 无法动态扩容,swoole_table内存管理是静态的,不支持动态申请新内存,因此一个Table在设置为N行之后,不能超过限制

  • Garfield

    多进程共享数据使用类的成员变量是否可以?

  • 兵者

    @Garfield 不可以 注意是‘外部存储服务’

  • janray

    如果只想调试一下,又不想引入外部存储,可以把 worker 进程设置为1个即可