>백엔드 개발 >PHP 튜토리얼 >CodeIgniter가 읽기-쓰기 분리를 구현하는 방법

CodeIgniter가 읽기-쓰기 분리를 구현하는 방법

不言
不言원래의
2018-06-19 16:59:101328검색

본 글은 주로 CodeIgniter 읽기-쓰기 분리 구현 방법을 소개하고, CodeIgniter 읽기-쓰기 분리 관련 구성 및 기능 구현 기술을 예제 형식으로 자세히 분석합니다. 필요한 친구들은 참고할 수 있습니다

이 문서에서는 CodeIgniter 읽기-쓰기 분리 구현 방법을 설명합니다. 참고용으로 모든 사람과 공유하세요. 세부 사항은 다음과 같습니다.

현재 서버는 마스터-슬레이브로만 구성되어 있으며 읽기-쓰기 분리 기능은 여기에서만 구현할 수 있습니다. 우리는 Codeigniter가 읽기-쓰기 분리를 어떻게 구현하는지에 대해 주로 이야기합니다. 그리고 다음 두 가지 사항이 충족되어야 합니다.

1. 읽기와 쓰기의 분리는 개발에 투명해야 합니다.

여러 DB를 수동으로 로드하여 읽기와 쓰기의 분리를 구현하려는 계획이 인터넷에 있습니다. 이러한 분리는 비즈니스와 너무 밀접하게 관련되어 있으며 개발의 어려움을 증가시키고 유지 관리에 도움이 되지 않습니다. 기본적으로 무거운 데이터베이스를 읽고, 메인 데이터베이스에 쓰는 것이므로 읽기와 쓰기의 구분이 개발자에게 투명합니다

2.

기존 구성 방법을 유지하고 원래 사용 방법에 영향을 주지 않고 어레이를 추가하여 읽기-쓰기 분리를 구성합니다.

Idea

1 읽기-쓰기 분리를 달성하는 가장 간단한 아이디어는 쿼리가 최종적으로 실행되는 쿼리 문을 기반으로 메인 라이브러리에 삽입할지 아니면 슬레이브 라이브러리에서 읽을지 결정하는 것이므로, 이 기능을 찾으세요.

2. 데이터베이스는 한 번만 연결해야 하며, 다음 번에 링크를 재사용할 수 있어야 합니다. 즉, Re-Database 이후에도 모든 읽기 작업이 가능하며, Main Database의 경우에도 다시 접속할 필요가 없습니다. 따라서 CI 수퍼 개체에 링크를 넣을 수 있습니다.

3. 마스터-슬레이브 판단은 최종 실행된 SQL 문을 기준으로 하므로 데이터베이스 구성의 자동 링크 자동 초기화 매개 변수는 기본적으로 연결되어 있으며 작동할 필요가 없습니다. 도서관, 그것은 자원 낭비가 될 것입니다.

4. 모델에서 $this->db를 사용하면 별도의 조정 없이 바로 쿼리를 실행할 수 있습니다.

5. 시스템 아래의 파일을 직접 수정하지 마세요.

읽기 및 쓰기 분리를 달성합니다.

CI의 DB 클래스는 시스템 아래의 파일을 읽도록 수정되어 있으므로 적절한 재작성을 통해 이를 달성할 수 있습니다. 첫 번째는 데이터베이스 메소드를 사용하여 데이터베이스 객체를 로드하는 Loader.php입니다. 이는 system/database/DB.php 파일을 참조하며, 사용자 정의 DB.php 파일이 있는지 확인하고 존재하는 경우 가져옵니다.

Rewrite 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 아래에 데이터베이스/DB.php를 생성합니다. 이 파일에는 구성 파일을 읽고 초기화 작업을 수행하는 데 사용되는 DB 메서드가 하나만 있습니다. 다시 작성해야 할 두 곳도 있습니다:

Rewrite 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 문을 실행하는 최종 메소드로 이해될 수 있습니다. 여기서 데이터베이스 링크를 판단할 수 있습니다.

Rewrite 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에도 닫기 방법이 있습니다. 이 방법으로 닫을 수 있는지 생각해 볼 수 있나요? 여기서는 불가능하다고 생각합니다.

데이터베이스 링크를 닫으세요

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 */

최종 데이터베이스 구성 방법은 원본을 기반으로 배열만 구성하면 됩니다. 이중 마스터를 사용할지, 하나의 마스터와 여러 슬레이브를 사용할지는 여기의 구성에 따라 다릅니다. 처음에는 원래 구성에 키 이름을 직접 추가하려고 생각했지만 마스터와 슬레이브 간의 해당 관계가 아직 명확하지 않습니다. 여기서 정의는 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 슈퍼오브젝트에 배치되어 있지 않습니다. 여러 모델을 로딩할 때마다 링크가 열리는 것으로 확인되었으니 반드시 테스트 후 확인하시기 바랍니다. 읽기-쓰기 분리가 완료되면 예상대로 실행된 곳에서 데이터베이스 링크가 열리고 닫히는지 확인할 수 있습니다. (해당 메소드는 application/core/database/drivers/mysql/mysql_driver.php의 db_connect 및 _close에 해당합니다.) . 전체 조정 프로세스에서 가장 중요한 두 가지 사항은 simple_query 메서드와 생성자에서 데이터베이스 연결을 닫는 것입니다. 모델에서의 조정은 여러 라이브러리를 보다 쉽게 ​​연결할 수 있도록 하기 위한 것입니다. 읽기와 쓰기의 분리가 구현되지 않은 경우에도 이와 같이 조정됩니다. 자주 사용되는 메소드를 파일로 분리하여 MY_Model이 상속합니다.

MYSQL 읽기 및 쓰기 분리를 구현하는 미들웨어가 많이 있습니다. 이를 사용하지 않을 경우 프로그램 제어를 통해 읽기 및 쓰기 분리가 가능합니다. 물론 이는 읽기와 쓰기의 분리만을 구현한 것이며, 메인 라이브러리를 강제로 사용할 수도 있습니다. 더 나은 할당 방법을 원한다면 load_db_proxy_setting에서 할당 방법을 생각해 볼 수 있습니다.

위 내용은 이 글의 전체 내용입니다. 모든 분들의 학습에 도움이 되었으면 좋겠습니다. 더 많은 관련 내용은 PHP 중국어 홈페이지를 주목해주세요!

관련 권장 사항:

CI 프레임워크에서 일반적으로 사용되는 작업 클래스 분석

CI 프레임워크에서 무한 분류 및 재귀 구현에 대해

위 내용은 CodeIgniter가 읽기-쓰기 분리를 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.