首页 数据库 mysql教程 CodeIgniter框架中关于DB事务处理的设计缺陷

CodeIgniter框架中关于DB事务处理的设计缺陷

Jun 07, 2016 pm 04:33 PM
codeigniter 事务处理 关于 框架 缺陷 设计

起因: 在我们线上的某个业务中,使用较老版本的CodeIgniter框架,其中的DB类中,对DB事物处理部分存在着一个设计上的缺陷,或许也算不上缺陷吧。但他却影响了我们生产环境,导致连锁反应。对业务产生较大影响,且不容易排查。这个问题,我在今年的3月中旬,

起因:
在我们线上的某个业务中,使用较老版本的CodeIgniter框架,其中的DB类中,对DB事物处理部分存在着一个设计上的缺陷,或许也算不上缺陷吧。但他却影响了我们生产环境,导致连锁反应。对业务产生较大影响,且不容易排查。这个问题,我在今年的3月中旬,曾向http://codeigniter.org.cn/的站长Hex 报告过,之后,我也忘记这件事情了。直到今天,我们线上业务又一次以为这个问题,害的我又排查一次。具体原因,各位且先听我慢慢说完。(这个问题同样存在于最新版本Version 2.1.0中)

分析:
以CodeIgniter框架Version 2.1.0为例,在system\database\DB_driver.php的CI_DB_driver类中第58行有个$_trans_status属性。

//system\database\DB_driver.php
var $trans_strict	= TRUE;
var $_trans_depth	= 0;
var $_trans_status	= TRUE; // Used with transactions to determine if a rollback should occur
var $cache_on		= FALSE;
登录后复制

同时,这个类的query方法中,有赋值此属性的代码,见文件306、307行

// This will trigger a rollback if transactions are being used
$this->_trans_status = FALSE;
登录后复制

这里也给了注释,告诉我们,如果使用了事物处理,那么这属性将成为一个回滚的决定条件。

在520行的事物提交方法trans_complete中,如下代码

/**
 * Complete Transaction
 *
 * @access	public
 * @return	bool
 */
function trans_complete()
{
	if ( ! $this->trans_enabled)
	{
		return FALSE;
	}
	// When transactions are nested we only begin/commit/rollback the outermost ones
	if ($this->_trans_depth > 1)
	{
		$this->_trans_depth -= 1;
		return TRUE;
	}
	// The query() function will set this flag to FALSE in the event that a query failed
	if ($this->_trans_status === FALSE)
	{
		$this->trans_rollback();
		// If we are NOT running in strict mode, we will reset
		// the _trans_status flag so that subsequent groups of transactions
		// will be permitted.
		if ($this->trans_strict === FALSE)
		{
			$this->_trans_status = TRUE;
		}
		log_message('debug', 'DB Transaction Failure');
		return FALSE;
	}
	$this->trans_commit();
	return TRUE;
}
登录后复制

在535行中,如果_trans_status属性如果是false,那么将发生回滚,并且返回false。

在我们的业务代码中,由于程序员疏忽,没有判断trans_complete()方法是否正确执行,直接告诉用户操作成功,但实际上,程序已经向DB下达回滚指令,并未成功更新DB记录。当用户执行下一步操作时,程序又发现相应记录并未更新,又提醒用户上个操作没有完成,通知用户重新执行。如此反复…

CodeIgniter框架的设计缺陷

排查的过程,也是挺有意思的,起初从PHP代码中,总是不能确定问题所在,并没有把焦点放到trans_complete()方法的返回上。直到后来strace抓包分析,才知道是因为此属性而导致了回滚。

22:54:08.380085 write(9, "_\0\0\0\3UPDATE `cfc4n_user_info` SET `cfc4n_user_lock` = 1\nWHERE `cfc4n_user_id` = \'6154\'\nAND `cfc4n_user_lock` = 0", 99) = 99    //执行更新命令
22:54:08.380089 read(9, ":\0\0\1\377\36\4#42S22Unknown column \'cfc4n_user_lock\' in \'where clause\'", 16384) = 62    //不存在字段,SQL执行错误
22:54:08.381791 write(9, "\21\0\0\0\3SET AUTOCOMMIT=0", 21) = 21    //禁止自动提交
22:54:08.381891 read(9, "\7\0\0\1\0\0\0\0\0\0\0", 16384) = 11
22:54:08.382186 poll([{fd=9, events=POLLIN|POLLPRI}], 1, 0) = 0
22:54:08.382258 write(9, "\v\0\0\0\2jv01_roles", 15) = 15
22:54:08.382343 read(9, "\7\0\0\1\0\0\0\0\0\0\0", 16384) = 11
22:54:08.382631 poll([{fd=9, events=POLLIN|POLLPRI}], 1, 0) = 0
22:54:08.382703 write(9, "\22\0\0\0\3START TRANSACTION", 22) = 22   //开始事务处理
22:54:08.401954 write(9, "\v\0\0\0\2database_demo", 15) = 15
22:54:08.402043 read(9, "\7\0\0\1\0\0\0\1\0\1\0", 16384) = 11
22:54:08.417773 write(9, "\v\0\0\0\2database_demo", 15) = 15
22:54:08.417872 read(9, "\7\0\0\1\0\0\0\1\0\0\0", 16384) = 11
22:54:08.418256 write(9, "[\0\0\0\3UPDATE `cfc4n_user_info` SET `silver` = CAST( silver + (5) as signed )\nWHERE `cfc4n_user_id` = \'6154\'", 95) = 95    //执行其他SQL语句
22:54:08.418363 read(9, "0\0\0\1\0\1\0\1\0\0\0(Rows matched: 1  Changed: 1  Warnings: 0", 16384) = 52    //成功更新,影响条数1.
22:54:08.430212 write(9, "\v\0\0\0\2database_demo", 15) = 15
22:54:08.430314 read(9, "\7\0\0\1\0\0\0\1\0\0\0", 16384) = 11
22:54:08.430698 write(9, "B\0\0\0\3UPDATE `cfc4n_user_info` SET `exp` = exp + 26\nWHERE `cfc4n_user_id` = \'6154\'", 70) = 70     //执行其他SQK语句
22:54:08.430814 read(9, "0\0\0\1\0\1\0\1\0\0\0(Rows matched: 1  Changed: 1  Warnings: 0", 16384) = 52    //成功更新,影响条数1.
22:54:08.432130 write(9, "\v\0\0\0\2database_demo", 15) = 15
22:54:08.432231 read(9, "\7\0\0\1\0\0\0\1\0\0\0", 16384) = 11
22:54:08.432602 write(9, "\244\0\0\0\3UPDATE `cfc4n_user_quest` SET `rew` = 1, `retable` = retable + 1, `re_time` = 1335797648\nWHERE `cfc4n_user_id` = \'6154\'\nAND `quest_id` = \'300001\'\nAND `rew` = 0", 168) = 168    //执行其他SQK语句
22:54:08.432743 read(9, "0\0\0\1\0\1\0\1\0\0\0(Rows matched: 1  Changed: 1  Warnings: 0", 16384) = 52    //成功更新,影响条数1.
22:54:08.433517 write(9, "\v\0\0\0\2database_demo", 15) = 15
22:54:08.433620 read(9, "\7\0\0\1\0\0\0\1\0\0\0", 16384) = 11
22:54:08.433954 write(9, "\t\0\0\0\3ROLLBACK", 13) = 13    //回滚事务 #注意看这里
22:54:08.434041 read(9, "\7\0\0\1\0\0\0\0\0\0\0", 16384) = 11
22:54:08.434914 write(9, "\v\0\0\0\2database_demo", 15) = 15
22:54:08.434999 read(9, "\7\0\0\1\0\0\0\0\0\0\0", 16384) = 11
22:54:08.435342 write(9, "\21\0\0\0\3SET AUTOCOMMIT=1", 21) = 21  //恢复自动提交
22:54:08.435430 read(9, "\7\0\0\1\0\0\0\2\0\0\0", 16384) = 11
22:54:08.436923 write(9, "\1\0\0\0\1", 5) = 5
登录后复制

可以看到,在22:54:08.380085时间点处,发送更新SQL语句指令,在22:54:08.380089时间读取返回结果,得到SQL执行错误,不存在字段”cfc4n_user_lock”;22:54:08.381791和22:54:08.382703两个时间点,PHP发送停止“自动提交”与“开始事务处理”指令,在 22:54:08.433954 发送“事务回滚”指令。

配合如上的代码分析,可以清楚的知道,因为“UPDATE `cfc4n_user_info` SET `cfc4n_user_lock` = 1 WHERE `cfc4n_user_id` = ’6154′ AND `cfc4n_user_lock` = 0”这句SQL执行错误,导致$_trans_status属性被设置为FALSE,当代码提交事务时,被trans_complete()方法判断,认为“上一个事务处理”(下面将仔细分析)中存在SQL语句执行失败,决定回滚事务,不提交。

刚刚提到“上一个事务处理”,可能有些朋友不能理解,我们先继续回到代码中来,继续看该属性,同样在trans_complete方法中,542-545行:

// If we are NOT running in strict mode, we will reset
// the _trans_status flag so that subsequent groups of transactions
// will be permitted.
if ($this->trans_strict === FALSE)
{
	$this->_trans_status = TRUE;
}
登录后复制

也可以很容易的从注释中看明白,设置CI的设计者,为了更严谨的处理 同一个脚本中,存在多个事务时,事务间彼此关系重要,一荣俱荣,一损俱损。这里的trans_strict属性,是个开关,当 trans_strict为false,便是非严格模式,意味着多个事务之间,彼此关系不重要,不影响。当前一个事务中有SQL语句执行失败,影响不到自己。便将_trans_status 设置为TRUE。
毫无疑问,这是个非常周全的考虑。考虑了多个事务之间的关系,保证业务跑在更严谨的代码上。

可是,我们的代码中,错误的SQL语句是执行在事务处理以外的,并不是事务之内。按照我们对事务的认识,可以很清晰的知道,事务之外的SQL相比事务之内的SQL来说,事务之内的SQL更重要,之外的可以允许出错,但事务之内的,务必要正确,不受外界干扰。但CI的框架中,因为一个事务以外的语句执行失败,却导致整个事务回滚…当然,我们的程序员没有对事务提交方法的返回做判断,这也是个问题。

问题已经很清晰了,那么解决方法想必对你来说,是多么的简单。
比如在trans_start方法中,对_trans_status 属性赋值,设置为TRUE,不理会事务外的问题。

function trans_start($test_mode = FALSE)
{
	if ($this->trans_strict === FALSE)
	{
		$this->_trans_status = TRUE;    //在开始事务处理时,重新设定这个属性的值为TRUE
	}
    //2012/05/01 18:00 经过CI中文社区网友 http://codeigniter.org.cn/forums/space-uid-5721.html指正,这里修改为增加trans_strict 属性判断 ,在决定是否重设_trans_status 为好。
	if ( ! $this->trans_enabled)
	{
		return FALSE;
	}
	// When transactions are nested we only begin/commit/rollback the outermost ones
	if ($this->_trans_depth > 0)
	{
		$this->_trans_depth += 1;
		return;
	}
	$this->trans_begin($test_mode);
}
登录后复制

结束:
在不明白对方设计意图的情况下,不能盲目的定义对方的代码评价,不管程序作者的水平如何。比自己强,也不能盲目崇拜;比自己弱,更不能乱加指责;理解读懂设计意图,学习他人优秀的设计思路、代码风格、算法效率,这才是一个好习惯。当然codeigniter框架是优秀的。

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

荣耀Magic V3首发AI离焦护眼技术:有效缓解近视发展 荣耀Magic V3首发AI离焦护眼技术:有效缓解近视发展 Jul 18, 2024 am 09:27 AM

7月12日消息,荣耀MagicV3系列今日正式发布,搭载全新荣耀视力舒缓绿洲护眼屏,在屏幕本身具备高规格和高素质的同时,还开创性的引入AI主动式护眼技术。据悉,传统的缓解近视的方式是“近视镜”,近视眼镜度数均匀分布,保证了视线中心区域成像在视网膜之上,但周边区域成像在视网膜后,视网膜感应到成像在后,促进眼轴向后生长,从而使度数加深。目前主要的缓解近视发展的方式之一是“离焦镜”,其中心区域度数正常,周边区域通过光学设计分区调整,从而使周边区域成像落在视网膜前,

1399元起 荣耀X60i手机开售:视觉四等边OLED直屏 1399元起 荣耀X60i手机开售:视觉四等边OLED直屏 Jul 29, 2024 pm 08:25 PM

7月29日消息,荣耀X60i手机今日正式开售,首发1399元。设计上,荣耀X60i手机采用居中挖孔直屏设计,四边近乎无界的超窄边框,极大地拓宽了视野边界。荣耀X60i参数显示屏:6.7英寸高清显示屏电池:5000mAh大容量电池处理器:天玑6080处理器(台积电6nm,2x2.4G的A76+6×2G的A55)系统:MagicOS8.0系统其他功能:5G信号增强灵动胶囊屏下指纹双MIC降噪知识问答摄影能力:后置双摄系统:5000万像素主摄200万像素辅助镜头前置自拍镜头:800万像素价格:8GB

如何评估Java框架商业支持的性价比 如何评估Java框架商业支持的性价比 Jun 05, 2024 pm 05:25 PM

评估Java框架商业支持的性价比涉及以下步骤:确定所需的保障级别和服务水平协议(SLA)保证。研究支持团队的经验和专业知识。考虑附加服务,如升级、故障排除和性能优化。权衡商业支持成本与风险缓解和提高效率。

全新堆叠工艺!小米MIX Fold 4首搭金沙江'立体异形”电池 全新堆叠工艺!小米MIX Fold 4首搭金沙江'立体异形”电池 Jul 20, 2024 am 03:20 AM

7月19日消息,小米MIXFold4首旗舰折叠新机今晚正式发布,首次搭载“立体异形电池”。据介绍,小米MIXFold4在电池技术上实现了重大突破,专为折叠屏设计了创新的“立体异形电池”。传统折叠屏设备多采用常规方形电池,空间利用效率较低。为解决这一问题,小米没有采用常见的卷绕式电芯,而是全新开发叠片工艺,打造全新形态的电池,大幅提升了空间利用率。电池技术创新为了实现精确交替堆叠正负极片,确保锂离子安全嵌入,小米开发了新型超声焊接机和叠片机,提高了焊接和裁切精

小米百元机 Redmi 14C 设计规格曝光 将于 8 月 31 日发布 小米百元机 Redmi 14C 设计规格曝光 将于 8 月 31 日发布 Aug 23, 2024 pm 09:31 PM

小米的Redmi品牌正准备在其产品组合中增加另一款经济型手机——Redmi14C。该设备已确认将于8月31日在越南发布。然而,在发布之前,这款手机的规格已经通过越南零售商被披露。Redmi14CRedmi经常在新系列中带来全新的设计,Redmi14C也不例外。这款手机背面有一个大的圆形摄像头模块,与前代的设计完全不同。蓝色配色版甚至采用渐变设计,让它看起来感觉更加高端。不过,实际上Redmi14C是一款经济型手机。相机模组包括四个环;一个环内装有5000万像素主传感器,另一个可能装有用于深度信息

PHP 框架的学习曲线与其他语言框架相比如何? PHP 框架的学习曲线与其他语言框架相比如何? Jun 06, 2024 pm 12:41 PM

PHP框架的学习曲线取决于语言熟练度、框架复杂性、文档质量和社区支持。与Python框架相比,PHP框架的学习曲线更高,而与Ruby框架相比,则较低。与Java框架相比,PHP框架的学习曲线中等,但入门时间较短。

PHP 框架的轻量级选项如何影响应用程序性能? PHP 框架的轻量级选项如何影响应用程序性能? Jun 06, 2024 am 10:53 AM

轻量级PHP框架通过小体积和低资源消耗提升应用程序性能。其特点包括:体积小,启动快,内存占用低提升响应速度和吞吐量,降低资源消耗实战案例:SlimFramework创建RESTAPI,仅500KB,高响应性、高吞吐量

荣耀Magic V3成功挑战滚筒洗衣机15分钟快洗:折叠唯一通过2.5米防水认证 荣耀Magic V3成功挑战滚筒洗衣机15分钟快洗:折叠唯一通过2.5米防水认证 Jul 18, 2024 pm 01:58 PM

7月12日消息,荣耀MagicV3今日正式发布,将折叠屏手机厚度带入9.2毫米。尤为值得一提的是,荣耀MagicV3在追求极致轻薄的同时,更通过前沿科技的运用,实现了行业领先的防水性能。得益于其采用的10微米级精密填充技术,这款手机不仅达到了IPX8级别的防水标准,即便在湿润环境下也能保持触控灵敏,为用户带来无忧的使用体验。发布会现场,荣耀更是以一场大胆的实验,直接将MagicV3置于滚筒洗衣机中进行15分钟快洗测试,结果令人惊叹——手机不仅安然无恙,更彰显了其卓越的防水实力。荣耀

See all articles