php原子操作,文件锁flock,数据库事务

php没有继承posix标准支持的unix锁,只封装了一个linux系统调用flock(信号量也能做成锁),按理也是可以使用锁机制的,虽然效率低一点。
php脚本是运行在fastcgi容器中,而fastcgi是多进程的,所以如果php程序访问了临界资源,势必造成程序结果的不正确性。
估计还要考虑下fastcgi容器的问题
------------------------------------
问题描述:黑客用的工具刷我们的后台
取消订单时会有退款,黑客并发取消订单,导致多次退款
如果请求一个一个来,哪怕间隔100毫秒,也是没有问题的

一个PHP处理过程是: 读退款标志,发现没退款, 退款,然后设置已退款标志
问题是多个请求同时到了,读出来的退款标志都是未退款,所以多个请求都退款了
同一个php文件,被同时请求多次,是同一时刻

用php文件锁flock 我们试了不行,还是用C++队列
用C++监听了一个端口,直接接收HTTP包,然后返回HTTP格式的包,PHP程序中用curl访问我这个C程序.
相当于远程调用了,可以部署到其他服务器做分布式了

=============================================

很多时候,我们并没有考虑我们php代码的并行能力,尤其是在我们的php代码对某个资源可读可写的时候。但这并不是说php的所有操作就都是原子的,事务的,可并行的。由于php脚本是运行在fastcgi容器中,而fastcgi是多进程的,所以如果php程序访问了临界资源,势必造成程序结果的不正确性。

解决问题的办法是使用锁机制。php没有继承posix标准支持的unix锁:比如记录锁fcntl,线程锁等,而只封装了一个linux系统调用flock(信号量也能做成锁),flock形式为flock($fp,$type),其中$fp为文件句柄,而$type为:
/* 当一个文件的打开方式是可读可写的,通常需要向文件加入锁机制 */

1. LOCK_SH 共享锁:
通常为进程向文件请求读操作时需加共享锁。共享锁可支持任意个进程间的读操作,如果写一个加了共享锁的文件则进程阻塞进入SLEEP状态值到共享锁解锁

2. LOCK_EX 独占锁:
通常为进程向文件的写操作加独占锁,一旦文件加上了该锁,则其他任意进程访问该文件时都会阻塞,直到解锁为止。

3. LOCK_UN 解锁:
为加锁的文件句柄解锁

这样的加锁方式必然可以保证加锁程序块的原子性,但同时也牺牲了程序的效率,因此,我们实际的程序中应该在程序的加锁和解锁代码间嵌入尽量少的程序逻辑(尤其是独占锁),保证程序尽快解锁。

最后,附上加上锁机制以后的程序:

<?php
$usrinfo = isset($_GET["usrinfo"])?$_GET["usrinfo"]:exit(1);
$stinfo = isset($_GET["stinfo"])?$_GET["stinfo"]:exit(1);
echo $stinfo;
$pid = posix_getpid();
$fp = fopen(“usrinfo.txt”,”a+”);
$num = rand(0,100000);
flock($fp,LOCK_EX);
fwrite($fp,”user:”.$usrinfo.” stinfo:”.$stinfo.”–”.$pid.”–”.$num.”\n”);
fwrite($fp,”talking 1 — pid:$pid and num:$num\n”);
flock($fp,LOCK_UN);
fclose($fp);

普通情况运行该程序,产生正确的结果。

============================================

用什么方法可以在业务批量操作时保证原子性呢?
例如:删除多条文章,但在中间有一条已经被删除了,假设这里会出现错误,那如何让整个操作回滚,并定位错误信息呢?
数据库的事务保证原子性但不能定位错误信息,但遇到无法使用事务的场景,应该怎么做呢?
----------------------------------------
利用数据库的事务来做是最合理的,错误信息可以记录啊,有操作失败抛出错误。
应用逻辑来保证,就是每操作一次做下记录,成功失败都做下记录。中间出错,可以把成功的回滚。一般我们删除是假删除,所以很容易。如果真删除,记录时要记录完整信息。

========================================
PHP用文件锁模拟进程锁,实现原子操作
用PHP实现原子操作,而PHP本身并没有提供进程锁机制,用PHP文件锁机制,通过文件锁模拟进程锁实现原子操作。

原子操作的代码之前,使用排他锁打开某个文件,代码如下:

$fp = fopen( LOCK_FILE_PATH, "r" );

if (!$fp) {
echo "Failed to open the lock file!";
exit(1);//异常处理
}

flock ( $fp, LOCK_EX );

原子操作的代码之后,对该文件解锁,并关闭文件,代码如下:
flock ( $fp, LOCK_UN );
fclose ( $fp );

整体伪代码为:
define("LOCK_FILE_PATH", "/tmp/lock");
if( !file_exists(LOCK_FILE_PATH) ){
$fp = fopen( LOCK_FILE_PATH, "w" );
fclose ( $fp );
}

$fp = fopen( LOCK_FILE_PATH, "r" );

if (!$fp) {
echo "Failed to open the lock file!";
exit(1);//异常处理
}
flock ( $fp, LOCK_EX );
//此处添加原子操作代码
flock ( $fp, LOCK_UN );
fclose ( $fp );

以上便可实现PHP原子操作,避免冲突。

===========================================

php原子操作与mysql原子操作

原子操作常用的方法就是通过数据回滚来实现,用 PHP 来实现数据库回滚操作相当简单:
1, 建立数据库连接
2, mysql_query('BEGIN'); 开启事务
3, $SQL = "...";
mysql_query($SQL); 做相应的数据库操作
4, 判断回滚条件:
if(mysql_errno)
{
print mysql_error();
mysql_query('ROLLBACK'); 出错就回滚
exit();
}
5,可以重复上述步骤 3 及步骤 4 的操作, 开始的过程(中间可以加入其他操作,不局限于数据库更新,但是注意,最好不要让一个事务时间过长,因为它锁定所有你用到的表,会影响其他程序使用)
你也可以在几条正确的sql更新语句后故意写一句错误的,看看是否回滚了。
6, 结束回滚操作
mysql_query('COMMIT'); 能够到这里,代表上述数据库操作都没有错,正式提交执行

这就是用 PHP 实现原子操作的整个过程,需要特别注意的是建立支持数据回滚操作的表结构,
另外,除 commit 外也有其它办法可以结束回滚操作。

php原子操作,文件锁flock,数据库事务的更多相关文章

  1. MySQL 数据库事务与复制

    好久没有写技术文章了,因为一直在思考 「后端分布式」这个系列到底怎么写才合适. 最近基本想清楚了,「后端分布式」包括「分布式存储」和 「分布式计算」两大类. 结合实际工作中碰到的问题,以寻找答案的方式 ...

  2. 数据库事务中的隔离级别和锁+spring Transactional注解

    数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题.ACID首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(At ...

  3. (转)对SQLSERVER数据库事务日志的疑问

    本文转载自桦仔的博客http://www.cnblogs.com/lyhabc/archive/2013/06/10/3130856.html 对SQLSERVER数据库事务日志的疑问 摸不透SQLS ...

  4. Atitit 数据库事务实现原理

    Atitit 数据库事务实现原理   1.1. 自己在程序中实现事务操作. 如果只是需要事务的话,你自己给mongo操作加上事务功能就可以啦..数据库事务只不过是他自己实现了而已..如果数据库不支持事 ...

  5. 攻城狮在路上(壹) Hibernate(十六)--- Hibernate声明数据库事务

    一.数据库事务的概念: 数据库的ACID特征:Atomic.Consistency.Isolation.Durability.原子性.一致性.隔离性.持久性.不同的隔离级别引发的不同问题. 事务的AC ...

  6. Linux文件锁flock

    Linux文件锁flock 在多个进程同时操作同一份文件的过程中,很容易导致文件中的数据混乱,需要锁操作来保证数据的完整性,这里介绍的针对文件的锁,称之为“文件锁”-flock. flock,建议性锁 ...

  7. C#实现执行多条SQl语句,实现数据库事务

    C#实现执行多条SQl语句,实现数据库事务 在数据库中使用事务的好处,相信大家都有听过银行存款的交易作为事务的一个例子.事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的 ...

  8. oracle 10g 学习之数据进行增删改查、数据库事务、约束(8)

    目标 通过本章学习,您将可以: l  使用 DML 语句 l  向表中插入数据 l  更新表中数据 l  从表中删除数据 l  控制事务 l  描述约束 l  创建和维护约束 数据控制语言 l     ...

  9. 数据库事务隔离级ORACLE数据库事务隔离级别介绍

    本文系转载,原文地址:http://singo107.iteye.com/blog/1175084 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted.Read committ ...

随机推荐

  1. iOS中push视图的时候,屏幕中间会出现一条灰色的粗线的解决方案

    因为执行动画需要时间,所以在动画未完成又执行另一个切换动画,容易产生异常.所以在一个方法连续push不同的VC是没有问题的,但是不小心push同一个VC,并且animated为YES,可能多次跳转了 ...

  2. Sql Server自动备份数据库,定期删除备份

    //实现:每天自动备份数据库,定期删除备份 //步骤:[开始]--[所有程序]--[Microsoft SQL Server 2005]--[SQL Server Management Studio] ...

  3. volatile与synchronized的区别

    1.锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility). 互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一 ...

  4. angularJS中directive与directive 之间的通信

    上一篇讲了directive与controller之间的通信:但是我们directive与directive之间的通信呢? 当我们两个directive嵌套使用的时候怎么保证子directive不会被 ...

  5. LeetCode 【31. Next Permutation】

    Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...

  6. 使用Delphi收发GMail的邮件

    GMAIL的端口和连接方式比较特殊:SMTP端口为:456POP3端口为:995都采用安全连接(SSL)这些通过Indy组件就可以实现参考代码如下: object IdConnectionInterc ...

  7. web初学之request,session与application

    request (1)request的setAttribute()与getAttribute()方法一般都是成对出现,首先通过setAttribute()方法设置属性与属性值,然后通过getAttri ...

  8. NSDate 刚刚、几分钟、几小时

    - (NSString *)minutesAgo { NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; fmt.dateFormat = @ ...

  9. Eclipse 官方简体中文语言包下载地址及安装方法

    Eclipse 官方简体中文语言包下载地址及安装方法 打开Eclipse Babel Project 主页: http://www.eclipse.org/babel/downloads.php 根据 ...

  10. oracle之压缩表

    oracle压缩数据的处理基于数据库块,本质是通过消除在数据库中的重复数据来实现空间节约. 具体做法: 比较数据块中包含的所有字段或记录,其中重复的数据只在位于数据块开始部分的记号表(Symbol T ...