ホームページ バックエンド開発 PHPチュートリアル CodeIgniter框架数据库事务处理的设计缺陷和解决方案_php实例

CodeIgniter框架数据库事务处理的设计缺陷和解决方案_php实例

Jun 07, 2016 pm 05:17 PM
codeigniter トランザクション処理

起因:

在我们线上的某个业务中,使用较老版本的CodeIgniter框架,其中的DB类中,对DB事物处理部分存在着一个设计上的缺陷,或许也算不上缺陷吧。但他却影响了我们生产环境,导致连锁反应。对业务产生较大影响,且不容易排查。这个问题,我在今年的3月中旬,曾向codeigniter中国的站长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记录。当用户执行下一步操作时,程序又发现相应记录并未更新,又提醒用户上个操作没有完成,通知用户重新执行。如此反复…

排查的过程,也是挺有意思的,起初从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 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

FastAPIでデータベース接続とトランザクション処理を実装する方法 FastAPIでデータベース接続とトランザクション処理を実装する方法 Jul 30, 2023 am 11:45 AM

FastAPI でデータベース接続とトランザクション処理を実装する方法 はじめに: Web アプリケーションの急速な開発に伴い、データベース接続とトランザクション処理が非常に重要なトピックになっています。 FastAPI は、その速度と使いやすさで開発者に愛されている高性能 Python Web フレームワークです。この記事では、信頼性が高く効率的な Web アプリケーションを構築するために役立つ、FastAPI でデータベース接続とトランザクションを実装する方法を紹介します。パート 1: FastA でのデータベース接続

CodeIgniter でカスタムミドルウェアを実装する方法 CodeIgniter でカスタムミドルウェアを実装する方法 Jul 29, 2023 am 10:53 AM

CodeIgniter でカスタム ミドルウェアを実装する方法 はじめに: 最新の Web 開発では、ミドルウェアがアプリケーションで重要な役割を果たします。これらは、リクエストがコントローラーに到達する前または後に、共有処理ロジックを実行するために使用できます。 CodeIgniter は、人気のある PHP フレームワークとして、ミドルウェアの使用もサポートしています。この記事では、CodeIgniter でカスタム ミドルウェアを実装する方法を紹介し、簡単なコード例を示します。ミドルウェアの概要: ミドルウェアは一種のリクエストです

CodeIgniter ミドルウェア: アプリケーションの応答性とページのレンダリングを高速化します。 CodeIgniter ミドルウェア: アプリケーションの応答性とページのレンダリングを高速化します。 Jul 28, 2023 pm 06:51 PM

CodeIgniter ミドルウェア: アプリケーションの応答性とページ レンダリングの高速化 概要: Web アプリケーションの複雑さと対話性が増大し続ける中、開発者はアプリケーションのパフォーマンスと応答性を向上させるために、より効率的でスケーラブルなソリューションを使用する必要があります。 CodeIgniter (CI) は、多くの便利な機能を提供する軽量の PHP ベースのフレームワークであり、その 1 つがミドルウェアです。ミドルウェアは、リクエストがコントローラーに到達する前または後に実行される一連のタスクです。この記事では使い方を紹介します

CodeIgniter フレームワークでデータベース クエリ ビルダー (Query Builder) を使用する方法 CodeIgniter フレームワークでデータベース クエリ ビルダー (Query Builder) を使用する方法 Jul 28, 2023 pm 11:13 PM

CodeIgniter フレームワークでデータベース クエリ ビルダー (QueryBuilder) を使用する方法の紹介: CodeIgniter は、開発者が Web アプリケーション開発を容易にするための強力なツールとライブラリを多数提供する軽量の PHP フレームワークです。最も印象的な機能の 1 つは、データベース クエリ ビルダー (QueryBuilder) です。これは、データベース クエリ ステートメントを構築および実行するための簡潔かつ強力な方法を提供します。この記事ではCoの使い方を紹介します。

PHP 開発: CodeIgniter を使用して MVC パターンと RESTful API を実装する PHP 開発: CodeIgniter を使用して MVC パターンと RESTful API を実装する Jun 16, 2023 am 08:09 AM

Web アプリケーションは進化し​​続けるため、アプリケーションをより迅速かつ効率的に開発することが重要です。また、RESTful API は Web アプリケーションで広く使用されているため、開発者は RESTful API の作成および実装方法を理解する必要があります。この記事では、CodeIgniter フレームワークを使用して MVC パターンと RESTful API を実装する方法について説明します。 MVC パターン MVC (Model-Vie) の概要

MySQL と Oracle: トランザクション処理能力の比較 MySQL と Oracle: トランザクション処理能力の比較 Jul 12, 2023 am 09:42 AM

MySQL と Oracle: トランザクション処理機能の比較 データベース管理システムでは、トランザクション処理が重要な概念です。トランザクションは、すべて完了するかすべて失敗する一連のデータベース操作です。したがって、トランザクション処理機能はデータベースの安定性とデータの整合性にとって非常に重要です。この記事では、2 つの主流のリレーショナル データベース管理システムである MySQL と Oracle のトランザクション処理機能を比較し、コード例を通して説明します。 MySQL はオープンソースのリレーショナル データベース管理です

PHPでCodeIgniter5フレームワークを使用するにはどうすればよいですか? PHPでCodeIgniter5フレームワークを使用するにはどうすればよいですか? Jun 01, 2023 am 11:21 AM

CodeIgniter は、MVC アーキテクチャを使用して迅速な開発をサポートし、一般的なタスクを簡素化する軽量の PHP フレームワークです。 CodeIgniter5 はフレームワークの最新バージョンであり、多くの新機能と改善点が提供されています。この記事では、CodeIgniter5 フレームワークを使用して簡単な Web アプリケーションを構築する方法を紹介します。ステップ 1: CodeIgniter5 をインストールする CodeIgniter5 のダウンロードとインストールは非常に簡単で、次の手順に従うだけです。 最新バージョンをダウンロードします。

CodeIgniter ミドルウェア: 安全なファイルのアップロードおよびダウンロード機能を提供します。 CodeIgniter ミドルウェア: 安全なファイルのアップロードおよびダウンロード機能を提供します。 Aug 01, 2023 pm 03:01 PM

CodeIgniter ミドルウェア: 安全なファイルのアップロードおよびダウンロード機能を提供します はじめに: Web アプリケーション開発のプロセスにおいて、ファイルのアップロードとダウンロードは非常に一般的な機能です。ただし、セキュリティ上の理由から、ファイルのアップロードとダウンロードの処理には追加のセキュリティ対策が必要になることがよくあります。 CodeIgniter は、開発者による安全で信頼性の高い Web アプリケーションの構築をサポートする豊富なツールとライブラリを提供する人気の PHP フレームワークです。この記事では、CodeIgniter ミドルウェアを使用して安全なファイルを実装する方法を紹介します。

See all articles