欢迎加入QQ讨论群258996829
麦子学院 头像
苹果6袋
6
麦子学院

 PHP 共享内存使用场景及注意点

发布时间:2016-11-16 16:23  回复:0  查看:2474   最后回复:2016-11-16 16:23  

本文和大家分享的是PHP开发单机共享内存的使用场景和分析,希望对大家有帮助,一起来看看吧。

 

  使用场景

 

  监控汇总

 

  目前正在用的一个场景,针对某一台机器上的错误进行汇总并报警,我们把一分钟之内的相同报警合并成一条,用共享内存来暂存,非常实用且高效。

 

  PHP SESSION

 

  如果你是单机的服务,且又启用了session,那么可以把session换成共享内存的来存储,会比文件要快上不少,这里还要强调是单机,这是最大的软肋,但就功能上来讲没有memcache方便。

 

  什么是共享内存

 

  共享内存是一种在同一台机器的不同进程(应用程序)之间交换数据的方式。一个进程可创建一个可供其他进程访问的内存段,并赋予它相应的权限。每个内存段拥有一个惟一的ID,我们通常称之为shmid,这个ID指向一个物理内存区域,其他进程可通过此ID来操作这块内存包扩读取、写入以及删除。

 

  共享内存的使用是一种在进程之间交换数据的快速方法,主要因为在创建内存段之后传递数据,不会涉及内核。这种方法常常称为进程间通信 (IPC)。其他 IPC 方法包括管道、消息队列、RPC 和套接字。

 

  PHP 中几种常见的共享内存使用方式

 

  APC 可以缓存 PHP 的 opcode 提高应用的性能,可以在同个 PHP-FPM 进程池的进程间共享数据,常用功能如下:

 

  apc_store,apc_fetchapc_addapc_deleteapc_inc apc_decapc_casapc_clear_cacheapc_sma_info

 

  Shmop Unix 系统共享内存使用接口常用功能:

 

  shmop_open,shmop_closeshmop_readshmop_writeshmop_delete

 

  ipcs -m  查看本机共享内存的状态和统计。

 

  ipcrm -m shmid 或 ipcrm -M shmkey 清除共享内存中的数据。

 

  SystemV Shm常用功能:

 

  ftok, shm_attach, shm_detach, shm_put_var, shm_get_var, shm_remove_var

 

  使用共享内存需要考虑操作的原子性和锁、并行和互斥。

 

  sem 信号量相关函数:sem_getsem_removesem_acquiresem_release

 

  PHP 提供的 IPC 机制。

 

  --enable-shmop 共享内存,只能按字节操作

 

  --enable-sysvsem 信号量

 

  --enable-sysvshm 共享内存,和 shmop 的差别是提供的操作函数不同,支持 keyvalue操作

 

  --enable-sysvmsg 消息队列

 

  本文主讲

 

  如何使用  PHP shmop 创建和操作共享内存段,使用它们存储可供其他应用程序使用的数据。

 

  1. 创建内存段

 

  共享内存函数类似于文件操作函数,但无需处理一个流,您将处理一个共享内存访问 ID。第一个示例就是 shmop_open 函数,它允许您打开一个现有的内存段或创建一个新内存段。此函数非常类似于经典的 fopen 函数,后者打开用于文件操作的流,返回一个资源供其他希望读取或写入该打开的流的函数使用。让我们看看 shmop_open的用法:

 

  <?php $key = ftok(__FILE__, 'h'); $mode = 'c'; $permissions = 0644; $size = 1024; $shmid = shmop_open($key, $mode, $permissions, $size); ?>

 

  第一个参数($key):

 

  系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个key值。通常情况下,该key值通过ftok函数得到, * *key是一个我们逻辑上表示共享内存段的标识。不同进程只要选择同一个Key值就可以共享同一段存储段。

 

  第二个参数($mode):

 

  访问模式,它类似于fopen的访问模式,有以下几种

 

  模式 “a”,它允许您访问只读内存段

 

  模式 “w”,它允许您访问可读写的内存段

 

  模式 “c”,它创建一个新内存段,或者如果该内存段已存在,尝试打开它进行读写

 

  模式 “n”,它创建一个新内存段,如果该内存段已存在,则会失败,返回 false,并伴随有warning: unable to attach or create shared memory segment

 

  第三个参数($permissions):

 

  内存段的权限。您必须在这里提供一个八进制值,它类似于UNIX操作系统文件和目录的操作权限。

 

  第四个参数($size):

 

  内存段大小,以字节为单位。在写入一个内存段之前,您必须在它之上分配适当的字节数。

 

  返回结果:

 

  此函数返回一个 ID 编号,其他函数可使用该 ID 编号操作该共享内存段。这个 ID 是共享内存访问 ID,与系统 ID 不同,它以参数的形式传递。请注意不要混淆这两者。如果失败,shmop_open 将返回 FALSE

 

  shmop_open成功后,使用ipcs -m, 可以查看到刚刚创建的内存段,注意 申请的内存段有严格的权限,比如用root用户申请的,普通用户就无权访问

 

  [[test@test]~/temp\\]$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00924660 0 root 666 8 104 0x00000000 294915 root 666 787528456 1 dest 0x68410e9f 753668 test 644 1024 0

 

  2. 向内存段写入数据

 

  使用 shmop_write 函数向共享内存块写入数据。此函数的使用很简单,它仅接受 个参数,如下所示。

 

  <?php

 

  //这里shmid可以延用上一段代码返回的shmid

 

  $shmid = shmop_open(ftok(__FILE__,'h), 'c', 0644, 1024); shmop_write($shmid, "Hello World!", 0); ?>

 

  这个函数类似于 fwrite 函数, 在这里有三个参数。

 

  第一个参数($shmid):是 shmop_open 返回的 ID,它识别您操作的共享内存块。

 

  第二个参数($data):是您希望存储的数据。

 

  第三个参数($offset):是您希望开始写入的位置。默认情况下,我们始终使用 来表示开始写入的位置。

 

  返回结果:此函数在失败时会返回 FALSE,在成功时会返回写入的字节数。

 

  3. 从内存段读取数据

 

  从共享内存段读取数据很简单。您只需要一个打开的内存段和 shmop_read 函数,它接受三个参数,如下所示:

 

  <?php $shmid = shmop_open(ftok(\\__FILE_\\_,'h), 'c', 0644, 1024); shmop_write($shmid, "Hello World\\!", 0); var_dump(shmop_read($shmid, 0, 11)); ?>

 

  第一个参数($shmid):是 shmop_open 返回的 ID,它识别您操作的共享内存块。

 

  第二个参数($start):是您希望从内存段读取的位置,这个参数可以始终为0, 表示数据的开头

 

  第三个参数($count):是您希望读取的字节数。一般情况下我们用shmop_size($shmid),以便完整的读取它。

 

  4. 删除内存段

 

  shmop_delete 该函数只接收一个参数,如下所示:

 

  <?php

 

  $shmid = shmop_open(ftok(\\__FILE_\\_,'h), 'c', 0644, 1024);

 

  shmop_delete($shmid);

 

  ?>

 

  其实这个函数不会实际删除该内存段。它将该内存段标记为删除状态,因为共享内存段在有其他进程正在使用它时无法被删除。shmop_delete 函数将该内存段标记为删除,阻止任何其他进程打开它。要删除它,我们需要关闭该内存段。

 

  5. 关闭内存段

 

  打开一个共享内存段会 “附加” 到它。附加该内存段之后,我们可在其中进行读取和写入,但完成操作后,我们必须从它解除。

 

  <?php $shmid = shmop_open(ftok(\\__FILE_\\_,'h), 'c', 0644, 1024); shmop_write($shmid, "Hello World\\!", 0); shmop_delete($shmid); shmop_close($shmid); ?>

 

  共享内存的原子操作 - 信号控制

 

  针对共享内存的写操作本身不是原子性的,那么当我们大量并发进行读写的时候,怎么保证原子性呢,这里要引入信号量进行控制。

 

  PHP 也提供了内置扩展 sysvsem ,其实我们在看sysvsem 提供的一系列sem_*的方法的时候,就会想到,这和上面提到的shmop_*有什么区别呢,我们来看官房文档中的这一个解释:PHP already had a shared memory extension (sysvshm) written by Christian Cartus, unfortunately this extension was designed with PHP only in mind and offers high level features which are extremely bothersome for basic SHM we had in mind.

 

  也就是说:sysvshm 扩展提供的方法在存储之前对用户的数据进行serialize处理,这里就导致这个存储的数据是无法与其它语言共享的,这一系列方法是php only的方法。

 

  引入信号控制之后的示例:

 

  <?php $key = ftok(_FILE_, 'h') $mode = "c";  $permissions = 0755;  $size = 1024; // 内存段的大小,单位是字节$key = ftok(\\__FILE_\\_, 'h'); $semid = sem_get($key); # 请求信号控制权if (sem_acquire($semid)) { $shmid = shmop_open($key, 'c', 0644, 1024); # 读取并写入数据 shmop_write($shmid, '13800138000', 0); # 关闭内存块 shmop_close($shmid); # 释放信号 sem_release($semid); }

 

  共享内存的操作是非常快的,在本地想要模拟实现写入冲突是非常困难的,但是本地想模拟实现写入冲突实际上是非常难的(考虑到计算机的执行速度)。在本地测试中,使用 for 循环操作时如果不使用shmop_close 关闭资源会出现无法打开共享内存的错误警告。这应该是因为正在共享内存被上一次操作占用中还没有释放导致。

 

  共享内存,memcache,文件的读写速度对比。

 

  以下是同时读写1k的数据读写100000次的时间对比:

 

   PHP&nbsp;共享内存使用场景及注意点  

共享内存的使用场景,想了很久,结合自己曾经做过的项目,发现能用到共享内存的地方比较少。

 

 

文章来源:公众账号

您还未登录,请先登录

热门帖子

最新帖子