PHP下通过系统信号量加锁方式获取递增序列ID


Posted in PHP onSeptember 25, 2009

在网上搜了搜,有两个办法但都不太好:一个是简单的以进程ID+时间戳,或进程ID+随机数来产生近似的唯一ID,虽简单但对于追求“完美”的我不愿这样凑合,再说Apache2以后进程会维持相当长得时间,生成的ID发生碰撞的几率还是比较大的;第二个思路是通过Mysql的自增字段,这个就更不能考虑了,效率低不说,我的设计里压根就没数据库。
递增ID的获取是个过程:
1. 从全局某个存储中读取ID
2. 给ID加1
3. 将ID重新存入全局存储
在多进程或线程的程序中需要将上述3步作为单步的原子操作,才能保证ID的唯一。
Java中很好解决,这是因为Java程序大多以多线程方式运行,每个线程都能共享Java进程中的变量,并能方便的加线程锁控制线程的运转同步。在PHP中ID全局存储没问题,可以放在session中,大不了放在文件中,但进程间同步就是问题了。
实际上进程调度、管理是操作系统内核必须实现的功能,今天介绍的信号量(也称为信号灯)就是在Unix/Linux上解决进程同步的一项技术。
信号灯原是用在铁路上的管理机制,我们今天看到的铁路大多是双线并行,但有的路段受山势、地形影响只有单条铁轨,必须保证同一时间只能有一列火车运行通过这些路段。早先铁路上就是用信号灯来管理的:没有火车经过时,信号等处于闲置状态,一旦有火车进入此路段,信号灯即变为在用状态,其他的火车经过时就需要等待,等待先前的火车驶出路段信号等变为闲置后,才能进入此路段,一旦又有火车进入,信号灯又变为繁忙......,以此来保障铁路运行的安全畅通。
Unix系统就像铁路管理局控制信号灯一样管理控制信号量的状态,因此也可以这样说信号量是由内核管理的,信号量不仅能控制进程间的同步,同样可以控制线程间的同步。
信号量属于系统进程间通讯技术(IPC),今天我们只从PHP角度介绍信号量的使用,有关IPC的技术细节可参考Stevens的权威著作《UNIX网络编程第二卷 进程间通信》。
先看最终的代码:

<?php 
// --------------------------------------------------- 
// 递增序列号ID(1~1000000000) 
// 
// ID存储在共享内存中(shared memory),通过信号灯(semaphore)同步 
// --------------------------------------------------- 
$IPC_KEY = 0x1234; //System V IPC KEY 
$SEQ_KEY = "SEQ"; //共享内存中存储序列号ID的KEY 
//创建或获得一个现有的,以"1234"为KEY的信号量 
$sem_id = sem_get($IPC_KEY); 
//创建或关联一个现有的,以"1234"为KEY的共享内存 
$shm_id = shm_attach($IPC_KEY, 64); 
//占有信号量,相当于上锁,同一时间内只有一个流程运行此段代码 
sem_acquire($sem_id); 
//从共享内存中获得序列号ID 
$id = @shm_get_var($shm_id, $SEQ_KEY); 
if ($id == NULL || $id >= 1000000000) 
{ 
$id = 1; 
} 
else 
{ 
$id++; 
} 
//将"++"后的ID写入共享内存 
shm_put_var($shm_id, $SEQ_KEY, $id); 
//释放信号量,相当于解锁 
sem_release($sem_id); 
//关闭共享内存关联 
shm_detach($shm_id); 
echo "序列号ID:{$id}"; 
?>

009行,定义了一个16进制的整形KEY,在PHP中只支持System V的IPC机制,需要通过一个KEY关联到指定的资源(消息队列、信号量、共享内存)。
010 行,定义了一个在共享内存中存储递增ID的KEY,这是PHP对System V共享内存的闲置:需要通过类似hashtable的KEY-VALUE方式存储变量。在上面的代码中使用共享内存做ID的存储容器,也可以换为 Session、文件等其他机制,本文重点是信号量,有关共享内存的知识以后在讲(别忘了前面推荐的那本书)。
013行,获得系统中的以1234为KEY的信号量,如果系统中没有就创建一个。
015行,同13行相似,获得系统中的以1234为KEY的共享内存,如果系统中没有就创建一个,第二个参数64表示创建64bytes大小的共享内存。
018~034 行,同步代码区,当一个进程或线程执行sem_acquire函数占有了信号量,到它调用sem_release函数释放信号量的过程内,其他进程或线程执行到sem_acquire会阻塞。021行从共享内存中获得ID,函数shm_get_var前缀"@"是为了屏蔽出错信息(第一次执行时,共享内存中并没有以"SEQ"为KEY的数据,会在页面上打印警告信息)。
其他语句非常简单,不需多讲。
程序编好后,访问这个PHP页面,会递增的输出数字。
我们可以通过系统命令ipcs查看在程序创建的信号量和共享内存:
$ ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00001234 1212443 www-data 666 64 0
------ Semaphore Arrays --------
key semid owner perms nsems
0x00001234 163841 www-data 666 3
------ Message Queues --------
key msqid owner perms used-bytes messages
前两段分别是共享内存和信号量,0x00001234既是我们创建的KEY。
也可以通过命令ipcrm删除:
$ ipcrm -M 0x00001234 #删除共享内存
$ ipcrm -S 0x00001234 #删除信号量
---------------------------------------------
PHP手册中关于IPC的资料非常少,这点也不难想象,Stevens已经在十几年前讲得透透的东东,在PHP中只是包装了一下,还有多少必要去深入说明呢?
文本只是借着ID说了说信号量的使用,如果您有更简单的生成自增ID的办法,还望赐教。
可能有朋友还想了解信号量的执行效率,我这里用一句过时的流行语总结: 相当的快。
PHP 相关文章推荐
dede全站URL静态化改造[070414更正]
Apr 17 PHP
PHP中防止SQL注入攻击和XSS攻击的两个简单方法
Apr 15 PHP
理解php Hash函数,增强密码安全
Feb 25 PHP
mysql 查询指定日期时间内sql语句实现原理与代码
Dec 16 PHP
PHP数组无限分级数据的层级化处理代码
Dec 29 PHP
解析php中array_merge与array+array的区别
Jun 21 PHP
PHP文件上传主要代码讲解
Sep 30 PHP
使用XHGui来测试PHP性能的教程
Jul 03 PHP
Yii2使用dropdownlist实现地区三级联动功能的方法
Jul 18 PHP
laravel5.4利用163邮箱发送邮件的步骤详解
Sep 22 PHP
PHP中递归的实现实例详解
Nov 14 PHP
laravel-admin自动生成模块,及相关基础配置方法
Oct 08 PHP
PHP 日常开发小技巧
Sep 23 #PHP
php程序之die调试法 快速解决错误
Sep 17 #PHP
火车采集器 免费版使出收费版本功能实现原理
Sep 17 #PHP
使用php来实现网络服务
Sep 15 #PHP
Discuz 6.0+ 批量注册用户名
Sep 13 #PHP
火车头discuz6.1 完美采集的php接口文件
Sep 13 #PHP
PHP 分页类(模仿google)-面试题目解答
Sep 13 #PHP
You might like
PHP在引号前面添加反斜杠(PHP去除反斜杠)
2013/09/28 PHP
Yii2-GridView 中让关联字段带搜索和排序功能示例
2017/01/21 PHP
php实现页面纯静态的实例代码
2017/06/21 PHP
PHP实现数组转JSon和JSon转数组的方法示例
2018/06/14 PHP
优秀js开源框架-jQuery使用手册(1)
2007/03/10 Javascript
由JavaScript中call()方法引发的对面向对象继承机制call的思考
2011/09/12 Javascript
利用div+jquery自定义滚动条样式的2种方法
2013/07/18 Javascript
js中通过父级进行查找定位元素
2014/06/15 Javascript
JQuery.get提交页面不跳转的解决方法
2015/01/13 Javascript
每天一篇javascript学习小结(基础知识)
2015/11/10 Javascript
javascript的正则匹配方法学习
2016/02/24 Javascript
javascript实现瀑布流动态加载图片原理
2016/08/12 Javascript
详解react如何在组件中获取路由参数
2017/06/15 Javascript
浅谈Vue-cli 命令行工具分析
2017/11/22 Javascript
webpack 4.0.0-beta.0版本新特性介绍
2018/02/10 Javascript
JavaScript引用类型Function实例详解
2018/08/09 Javascript
解决vue单页路由跳转后scrollTop的问题
2018/09/03 Javascript
node学习笔记之读写文件与开启第一个web服务器操作示例
2019/05/29 Javascript
[00:34]TI7不朽珍藏III——纯金地穴编织者饰品展示
2017/07/15 DOTA
Python实现比较两个文件夹中代码变化的方法
2015/07/10 Python
简单掌握Python的Collections模块中counter结构的用法
2016/07/07 Python
python爬虫获取淘宝天猫商品详细参数
2020/06/23 Python
python定向爬取淘宝商品价格
2018/02/27 Python
Python实用技巧之利用元组代替字典并为元组元素命名
2018/07/11 Python
python仿evething的文件搜索器实例代码
2019/05/13 Python
Python-Seaborn热图绘制的实现方法
2019/07/15 Python
Html5踩坑记之mandMobile使用小记
2020/04/02 HTML / CSS
Lancer Skincare官方网站:抗衰老皮肤护理
2020/11/20 全球购物
应用电子专业学生的自我评价
2013/10/16 职场文书
预备党员思想汇报
2014/01/08 职场文书
安全生产投入制度
2014/01/29 职场文书
党的群众路线教育实践活动个人承诺书
2014/05/22 职场文书
实习协议书范本
2014/09/25 职场文书
2015年元旦标语大全
2014/12/09 职场文书
2015庆祝七一建党节94周年活动总结
2015/03/20 职场文书
2015年老干部工作总结
2015/04/23 职场文书