PDO MySQL: PDO 사용:

지금까지 읽은 내용은 다음과 같습니다.

  1. MySQL의 네이티브 준비는 쿼리 캐시를 바이패스하므로 PDO의 준비 에뮬레이션이 성능에 더 좋습니다.
  2. MySQL의 기본 준비는 보안(SQL 주입 방지)에 더 적합합니다.
  3. MySQL의 기본 준비는 오류 보고에 더 적합합니다.

나는 이 진술들 중 어느 것도 얼마나 진실인지 더 이상 모르겠다.MySQL 인터페이스를 선택할 때 가장 큰 관심사는 SQL 주입을 막는 것입니다.두 번째 관심사는 성능입니다.

현재 어플리케이션에서는 절차적인 MySQLi(준비된 스테이트먼트 없음)를 사용하고 있으며 쿼리 캐시를 많이 사용하고 있습니다.단일 요청으로 준비된 스테이트먼트를 재사용하는 경우는 거의 없습니다.저는 명명된 파라미터와 준비된 스테이트먼트의 보안을 위해 PDO로의 이행을 시작했습니다.

사용하고 있다MySQL 5.1.61그리고.PHP 5.3.2

내가 떠날까PDO::ATTR_EMULATE_PREPARES활성화 여부를 확인합니다.쿼리 캐시의 성능과 준비된 스테이트먼트의 보안을 모두 확보할 수 있는 방법이 있습니까?



질문에 대한 답변



고객의 우려에 대한 답변:

  1. MySQL > = 5.1.17 (또는 > = 5.1.21)PREPARE그리고.EXECUTEstatements)는 쿼리 캐시에서 준비된 스테이트먼트를 사용할 수 있습니다.따라서 MySQL+PHP 버전에서는 쿼리 캐시와 함께 준비된 문을 사용할 수 있습니다.그러나 MySQL 문서에서 쿼리 결과를 캐싱할 때 주의해야 할 사항을 주의 깊게 기록해 두십시오.캐시할 수 없거나 캐시되어도 쓸모없는 쿼리의 종류가 많이 있습니다.제 경험상 쿼리 캐시는 그다지 큰 이익이 되지 않는 경우가 많습니다.쿼리 및 스키마는 캐시를 최대한 활용하기 위해 특수 구성이 필요합니다.대부분의 경우 애플리케이션 수준의 캐싱은 결국 필수적입니다.

  2. 원어민 준비는 보안에 영향을 주지 않습니다.유사 준비된 문은 여전히 쿼리 매개 변수 값을 피해 MySQL 서버에서 바이너리 프로토콜을 사용하는 대신 문자열을 사용하여 PDO 라이브러리에서 수행됩니다.즉, 동일한 PDO 코드가 주입 공격에 똑같이 취약(또는 취약하지 않음)할 수 있습니다.EMULATE_PREPARES설정.유일한 차이점은 파라미터 치환이 이루어지는 위치입니다.EMULATE_PREPARESPDO 라이브러리에서 발생합니다.EMULATE_PREPARESMySQL 서버에서 발생합니다.

  3. 없이.EMULATE_PREPARES실행 시간이 아닌 준비 시간에 구문 오류가 발생할 수 있습니다.EMULATE_PREPARESPDO에는 실행 시까지 MySQL에 제공할 쿼리가 없기 때문에 실행 시에만 구문 오류가 발생합니다.이것은, 기입하는 코드에 영향을 주는 것에 주의해 주세요.특히, 사용하시는 분은PDO::ERRMODE_EXCEPTION!

기타 고려사항:

  • 에는 고정 비용이 있습니다.prepare()(원어민 준비 스테이트먼트를 사용하여) 따라서prepare();execute()네이티브 prepared 스테이트먼트를 사용하면 에뮬레이트된 prepared 스테이트먼트를 사용하여 플레인텍스트 쿼리를 발행하는 것보다 약간 느릴 수 있습니다.많은 데이터베이스 시스템에서 쿼리 플랜은prepare()캐시도 되고 여러 연결과 공유할 수 있지만 MySQL은 이 기능을 하지 않는 것 같습니다.따라서 여러 쿼리에 대해 준비된 문 개체를 재사용하지 않으면 전체 실행 속도가 느려질 수 있습니다.

마지막으로 MySQL+PHP 이전 버전에서는 준비된 문장을 에뮬레이트해야 하지만 최신 버전에서는 에뮬레이션을 해제해야 합니다.

PDO를 사용하는 앱을 몇 개 쓰고 나서, 가장 좋은 설정이라고 생각되는 PDO 접속 기능을 만들었습니다.다음과 같은 기능을 사용하거나 원하는 설정에 맞게 조정해야 합니다.

/**
* Return PDO handle for a MySQL connection using supplied settings
*
* Tries to do the right thing with different php and mysql versions.
*
* @param array $settings with keys: host, port, unix_socket, dbname, charset, user, pass. Some may be omitted or NULL.
* @return PDO
* @author Francis Avila
*/ function connect_PDO($settings) {
$emulate_prepares_below_version = '5.1.17';
$dsndefaults = array_fill_keys(array('host', 'port', 'unix_socket', 'dbname', 'charset'), null);
$dsnarr = array_intersect_key($settings, $dsndefaults);
$dsnarr += $dsndefaults;
// connection options I like
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
// connection charset handling for old php versions
if ($dsnarr['charset'] and version_compare(PHP_VERSION, '5.3.6', '<')) {
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$dsnarr['charset'];
}
$dsnpairs = array();
foreach ($dsnarr as $k => $v) {
if ($v===null) continue;
$dsnpairs[] = "{$k}={$v}";
}
$dsn = 'mysql:'.implode(';', $dsnpairs);
$dbh = new PDO($dsn, $settings['user'], $settings['pass'], $options);
// Set prepared statement emulation depending on server version
$serverversion = $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
$emulate_prepares = (version_compare($serverversion, $emulate_prepares_below_version, '<'));
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate_prepares);
return $dbh; } 



아무도 에뮬레이션을 꺼야 하는 가장 큰 이유 중 하나를 언급하지 않은 것이 놀랍다.에뮬레이션을 켜면 PDO는 모든 정수를 반환하고 문자열로 플로트합니다.에뮬레이션을 끄면 MySQL의 정수와 플로트가 정수가 되어 PHP로 플로팅됩니다.

상세한 것에 대하여는, 다음의 질문에 대해서, 허가된 답변을 참조해 주세요.PHP + PDO + MySQL : 어떻게 하면 MySQL에서 정수 숫자 열을 PHP의 정수숫자로 반환할 수 있습니까?




비활성화 시 주의PDO::ATTR_EMULATE_PREPARES(네이티브를 켜면) PHP가pdo_mysql에 대해서 컴파일 되어 있지 않다.mysqlnd.

오래됐기 때문에libmysql는 일부 기능과 완전히 호환되지 않으므로 다음과 같은 이상한 버그가 발생할 수 있습니다.

  1. 64비트 정수의 경우 가장 중요한 비트가 손실됩니다.PDO::PARAM_INT( 0x12345678AB가 0x345678로 잘립니다.64비트 머신의 AB)
  2. 다음과 같은 간단한 쿼리를 작성할 수 없습니다.LOCK TABLES(그것은 던진다.SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet예외)
  3. 다음 쿼리 전에 결과에서 모든 행을 가져오거나 커서를 닫아야 합니다(with).mysqlnd또는 에뮬레이트된 준비는 자동으로 실행되며 mysql 서버와 동기화되지 않습니다.)

이 버그는 간단한 프로젝트로 다른 서버로 이행했을 때 발견되었으며,libmysql위해서pdo_mysql모듈.아마 벌레가 훨씬 더 많을 거예요, 잘 모르겠어요.또한 나는 신선한 64비트 debian jesse에 대해 테스트했는데, 나열된 모든 버그는 내가 이 버그에 대해apt-get install php5-mysql, 그리고 내가 사라지면apt-get install php5-mysqlnd.

언제PDO::ATTR_EMULATE_PREPARES는 true로 설정되어 있습니다(기본값). PDO는 이 모드에서는 준비된 문을 전혀 사용하지 않기 때문에 이러한 버그는 발생하지 않습니다.그래서 만약에pdo_mysql에 기반을 둔libmysql(“mysqlnd” 서브스트링이 의 “Client API version” 필드에 표시되지 않음)pdo_mysqlphpinfo의 섹션) – 회전하지 마십시오.PDO::ATTR_EMULATE_PREPARES쉬는.




5.1을 실행하고 있을 때 에뮬레이트 준비 기능을 끄겠습니다.즉, PDO는 네이티브로 준비된 스테이트먼트 기능을 활용합니다.

PDO_MYSql은 MySQL 4.1 이후에 탑재된 네이티브 준비 스테이트먼트 지원을 활용합니다.이전 버전의 mysql 클라이언트 라이브러리를 사용하는 경우 PDO가 이를 에뮬레이트합니다.

http://php.net/manual/en/ref.pdo-mysql.php

MySQLi for PDO는 준비된 이름 있는 문과 더 나은 API를 위해 삭제했습니다.

그러나 균형을 맞추기 위해 PDO는 MySQLi보다 성능이 매우 느리지만 주의해야 합니다.저는 이것을 선택했을 때 알고 있었습니다.또한 특정 엔진에 접속할 수 있는 매우 빠른 라이브러리를 사용하는 것보다 더 나은 API와 업계 표준을 사용하는 것이 더 중요하다고 판단했습니다.FWIW PHP 팀도 향후 MySQLi를 통한 PDO를 긍정적으로 검토하고 있다고 생각합니다.




실제 데이터베이스를 활성화하는 것이 좋습니다.PREPARE예를 들어 에뮬레이션이 모든 것을 포착하지 못하기 때문에 콜을 준비합니다.INSERT;!

var_dump($dbh->prepare('INSERT;')); $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); var_dump($dbh->prepare('INSERT;')); 

출력

object(PDOStatement)#2 (1) {
["queryString"]=>
string(7) "INSERT;" } bool(false) 

실제로 작동하는 코드는 성능 저하를 감수하겠습니다.

전원

PHP 버전: PHP 5.4.9-4ubuntu2.4 (cli)

MySQL 버전: 5.5.34-0ubuntu0