この記事では主に CodeIgniter の読み取り/書き込み分離の実装方法を紹介し、CodeIgniter の読み取り/書き込み分離に関連する構成と機能の実装スキルをサンプルの形式で詳細に分析します。必要な方はこの例を参照してください。記事
CodeIgniterの読み書き分離の実装方法について説明します。参考までに皆さんと共有してください。詳細は次のとおりです。
現在のサーバーはマスター/スレーブのみであり、読み取り/書き込み分離の機能は でのみ実装できます。ここでは主に、Codeigniter がそれを実装する方法について説明します。読み取りと書き込みは次の 2 つの点を満たす必要があります。
1. 読み取りと書き込みの分離は開発に対して透過的である必要があります。
インターネット上には、複数の DB を手動でロードすることで読み取りと書き込みの分離を実現するソリューションがあります。このような分離はビジネスと密接に関係しており、開発の難易度が高くなり、メンテナンスに役立ちません。必要なのは、デフォルトで読み取り/書き込みライブラリを使用し、メイン ライブラリに書き込みを行うことです。読み取りと書き込みの分離は開発者にとって透過的です。
2。
既存の構成方法を保持し、元の使用方法に影響を与えることなく、配列を追加して読み取り/書き込み分離を構成します。
アイデア
#1. 読み取りと書き込みの分離を実現するための最も単純なアイデアは、以下に基づいてメイン ライブラリに挿入するかスレーブ ライブラリから読み取るかを決定することです。クエリが最終的に実行されるクエリ ステートメントなので、この関数を見つける必要があります。
2. データベースは 1 回だけ接続し、リンクは次の操作で再利用できる必要があります。つまり、データベースを再構築した後でもすべての読み取り操作が可能であり、メイン データベースにも再度接続する必要はありません。したがって、CI スーパー オブジェクトにリンクを置くことができます。
3. マスター/スレーブの判断は、最後に実行された SQL ステートメントに基づいて行われるため、デフォルトで接続されている場合、データベース構成の自動リンク autoinit パラメーターを true に設定する必要はありません。ライブラリを操作する必要はありません。リソースの無駄です。
4. モデル内で $this->db を使用すると、他の調整を行わずにクエリを直接操作できます。
5. システム配下のファイルを直接変更しないでください。
読み取りと書き込みの分離を実現します。
CI の DB クラスは、システム配下のファイルを読み取るように固定されています。システムを適切に書き換えることでこれを実現できます。 1 つ目は Loader.php で、データベース メソッドを使用してデータベース オブジェクトをロードします。これは、system/database/DB.php ファイルを参照し、カスタム DB.php ファイルがあるかどうかを判断し、存在する場合はインポートします。
Loader.phpを書き換えます
public function database($params = '', $return = FALSE, $active_record = NULL) { $CI =& get_instance(); if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db)) { return FALSE; } if(file_exists(APPPATH.'core/database/DB.php')) { require_once(APPPATH.'core/database/DB.php'); } else { require_once(BASEPATH.'database/DB.php'); } if ($return === TRUE) { return DB($params, $active_record); } $CI->db = ''; $CI->db =& DB($params, $active_record); } /* End of file MY_Loader.php */ /* Location: ./application/core/MY_Loader.php */
次に、application/coreの下にdatabase/DB.phpを作成します。このファイルDB メソッドは 1 つだけあり、構成ファイルを読み取って初期化作業を実行するために使用されます。また、書き換える必要がある場所が 2 か所あります:
DB.php
//DB_driver.php为所有驱动方式的父类,最终执行查询的方法在该文件中 //第一处修改为判断自定义的DB_driver.php是否存在,存在则引入 if(file_exists(APPPATH.'core/database/DB_driver.php')) { require_once(APPPATH.'core/database/DB_driver.php'); } else { require_once(BASEPATH.'database/DB_driver.php'); } //第二处 $params['dbdriver'].'_driver.php' 该文件可不调整,实际未修改该文件,为了方便调试也加了 //mysql驱动对应system/database/drivers/mysql/mysql_driver.php,mysql的最后执行方法在这里, //包括数据库打开和关闭、查询等,可以该文件增加相应日志查看读写分离是否有效 if(file_exists(APPPATH.'core/database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php')) { require_once(APPPATH.'core/database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php'); } else { require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php'); } //将当前group name赋值给param,方便判断 $params['group_name'] = $active_group; /* End of file DB.php */ /* Location: ./application/core/database/DB.php */
DB 全体を書き換えます。 php 調整は基本的にファイルの導入です。グループ名を導入しない場合は、ホスト名とデータベース名で設定できます。 autoint を強制的にオフにしたい場合は、DB.php 内の次の段落を削除できます。
if ($DB->autoinit == TRUE) { $DB->initialize(); }
次のステップは核心部分です。読み取りと書き込みの分離はクエリ ステートメントに基づいて実現されます。
DB_driver.php の simple_query メソッドは、SQL ステートメントを実行する最終メソッドとして理解でき、ここでデータベース リンクを判断できます。
DB_driver.phpを書き換える
//增加属性,表示当前组 var $active_group; //增加属性,使用强制使用主库 var $db_force_master; //该方法为执行查询的必经之地,我们可以在这里根据类型判断使用哪个链接。 function simple_query($sql) { //load_db_proxy_setting方法这里写在helper中,也可以直接写在该类中,写在helper中则需要在自动加载中加载该helper //该方法的作用是根据当前链接group name 和sql读写类型,以及是否强制使用主库判断使用哪个链接。使用主库 OR 重库? //主重库的负载均衡,单点故障都可以在这里考虑。也就是根据3个参数返回一个可用的配置数组。 $proxy_setting = load_db_proxy_setting($this->group_name, $this->is_write_type($sql), $this->db_force_master); if(is_array($proxy_setting) && ! empty($proxy_setting)) { $proxy_setting_key = key($proxy_setting); $this->group_name = $proxy_setting_key; //将当前配置重新赋值给类的属性,如果database.php配置的是DSN字符串,则需要在load_db_proxy_setting中做处理 foreach($proxy_setting[$proxy_setting_key] as $key => $val) { $this->$key = $val; } //定义链接ID为conn_前缀 $proxy_conn_id = 'conn_'.$proxy_setting_key; $CI = & get_instance(); //赋值给CI超级对象或者直接从CI超级对象中读取 if(isset($CI->$proxy_conn_id) && is_resource($CI->$proxy_conn_id)) { $this->conn_id = $CI->$proxy_conn_id; } else { $this->conn_id = false; $this->initialize(); $CI->$proxy_conn_id = $this->conn_id; } //强制只一次有效,下次查询失效,防止一直强制主库 $this->reset_force_master(); } if ( ! $this->conn_id) { $this->initialize(); } return $this->_execute($sql); } //某些情况会强制使用主库,先执行该方法即可 public function force_master() { $this->db_force_master = TRUE; } public function reset_force_master() { $this->db_force_master = FALSE; } /* End of file DB_driver.php */ /* Location: ./application/core/database/DB_driver.php */
ここで基本的に読み書きの分離が実現されていますが、難しいです。最初から最後まで、リンクされたデータベース オブジェクトを閉じる必要があり、パブリック コントローラーでの実行後に接続を閉じることができます。
DB_driver.php にも close メソッドがありますが、このメソッドで閉じることができるかどうかを検討できますか?ここではそれは不可能だと思います。
データベース リンクを閉じます
class MY_Controller extends CI_Controller { public function __construct() { parent::__construct(); $this->load->service('common/helper_service', NULL, 'helper'); //下面这段为关闭CI超级对象中的数据库对象和数据库链接,db的对象Codeigniter.php中会关闭 register_shutdown_function(function(){ foreach(get_object_vars($this) as $key => $val) { if(substr($key, 0, 3) == 'db_' && is_object($this->{$key}) && method_exists($this->{$key}, 'close')) { $this->{$key}->close(); } if(substr($key, 0, 5) == 'conn_' && is_resource($this->{$key})) { $this->db->_close($val); unset($this->{$key}); } } }); } } /* End of file MY_Controller.php */ /* Location: ./application/core/MY_Controller.php */
$this を各モデルで利用できるようにするために、モデルで使用します。 ->db、データベースに複数回接続しないでください。ここでは、リンクも CI スーパー オブジェクトに配置されます。これは、読み取りと書き込みが分離されていない場合でも、コンストラクターでグループ名を渡すだけで、複数の DB を簡単に接続できます。
モデルの調整
public function __construct($group_name = '') { parent::__construct(); $this->initDb($group_name); } private function initDb($group_name = '') { $db_conn_name = $this->getDbName($group_name); $CI = & get_instance(); if(isset($CI->{$db_conn_name}) && is_object($CI->{$db_conn_name})) { $this->db = $CI->{$db_conn_name}; } else { $CI->{$db_conn_name} = $this->db = $this->load->database($group_name, TRUE); } } private function getDbName($group_name = '') { if($group_name == '') { $db_conn_name = 'db'; } else { $db_conn_name = 'db_'.$group_name; } return $db_conn_name; } /* End of file MY_Model.php */ /* Location: ./application/core/MY_Model.php */
最終的なデータベース構成方法は、元のベースで配列を構成するだけです。 。デュアルマスターを使用するか、1 つのマスターと複数のスレーブを使用するかは、ここでの構成によって異なります。最初はキー名を元の設定に直接追加することを考えましたが、マスターとスレーブの対応関係がまだ明確ではありません。ここでの定義によって、load_db_proxy_setting の実装が決まります。
database.php 構成
$_master_slave_relation = array( 'default_master' => array('default_slave1', 'default_slave2', 'default_slave3'), ); /* End of file database.php */ /* Location: ./application/config/database.php */
最初のデータベース リンクは CI スーパー オブジェクトに配置されていませんでした。複数のモデルをロードすると、毎回リンクが開かれるため、読み取りと書き込みの分離が完了した後に、データベース リンクが開いているか閉じているかを確認して、期待どおりに実行されるかどうかを確認する必要があることがわかりました (メソッドは対応しています)。 php の db_connect および _close に application/core/database/drivers/mysql/mysql_driver を追加します。調整プロセス全体で最も重要な 2 つのポイントは、simple_query メソッドとコンストラクターでのデータベース接続の終了です。モデル内の調整は、複数のライブラリをリンクしやすくするためであり、よく使用されるメソッドがファイルに分離され、MY_Model がそれを継承する場合にもこのように調整されます。
MYSQL の読み取りと書き込みの分離を実装するミドルウェアは数多くありますが、これらを使用しない場合は、プログラム制御によって読み取りと書き込みの分離を実現できます。もちろん、これは読み取りと書き込みの分離を実装するだけであり、メイン ライブラリの使用を強制することもできます。より良い割り当て方法が必要な場合は、load_db_proxy_setting での割り当て方法を検討できます。
以上がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。
関連する推奨事項:
CI フレームワークの無限分類と再帰について成し遂げる###########################
以上がCodeIgniter が読み取りと書き込みの分離を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。