Использовать ли «SET NAMES» - PullRequest
       38

Использовать ли «SET NAMES»

58 голосов
/ 30 октября 2009

Читая «Высокопроизводительный MySQL» от О'Рейли, я наткнулся на следующее

Другим распространенным мусорным запросом является SET Имена UTF8, который является неправильным способом делать все равно (это не меняет набор символов клиентской библиотеки; Это влияет только на сервер).

Я немного запутался, потому что я использовал "SET NAMES utf8" в верхней части каждого скрипта, чтобы БД знала, что мои запросы кодируются в utf8.

Может ли кто-нибудь прокомментировать приведенную выше цитату или, если выразиться более формально, каковы ваши предложения / лучшие практики для обеспечения того, чтобы мой рабочий процесс базы данных учитывал юникод?

Мои целевые языки - php и python, если это актуально.

Ответы [ 3 ]

30 голосов
/ 30 октября 2009

mysql_set_charset() будет вариант, но вариант ограничен ext/mysql. Для ext/mysqli это mysqli_set_charset, а для PDO::mysql необходимо указать параметр соединения.

Поскольку использование этой функции приводит к вызову MySQL API, его следует рассматривать намного быстрее, чем отправлять запрос.

Что касается производительности, самый быстрый способ обеспечить связь на основе UTF-8 между вашим сценарием и сервером MySQL - это правильно настроить сервер MySQL. Поскольку SET NAMES x равно эквивалентно

SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;

, тогда как SET character_set_connection = x внутренне также выполняет SET collation_connection = <<default_collation_of_character_set_x>>, вы также можете статически установить эти серверные переменные в вашем my.ini/cnf.

Обратите внимание на возможные проблемы с другими приложениями, работающими на том же экземпляре сервера MySQL и требующими некоторого другого набора символов.

26 голосов
/ 03 января 2013

TLDR

// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');

В этом ответе акцент делается на pdo-библиотеке php, потому что она такая вездесущая.

Краткое напоминание - mysql - это клиент-серверная архитектура. Это важно, потому что есть не только сервер mysql, где находится настоящая база данных, но также есть отдельный клиентский клиент mysql, который общается с сервером mysql (это отдельные объекты). Можно сказать, что клиент mysql и pdo смешаны.

Когда вы используете set names utf8, вы отправляете стандартный запрос sql для mysql. В то время как запрос sql проходит через pdo, а затем через клиентскую библиотеку mysql и, наконец, достигает сервера mysql, ТОЛЬКО сервер mysql анализирует и интерпретирует этот запрос sql. Это важно, потому что сервер mysql не отправляет никаких сообщений обратно pdo или клиенту mysql, сообщая ему, что набор символов и кодировка изменились, и поэтому клиент mysql и pdo абсолютно не знают, что это произошло.

Важно не делать этого, потому что клиентская библиотека не может правильно обрабатывать строки, если она не знает о текущем наборе символов. Наиболее распространенные операции будут работать правильно, если клиент не будет знать правильный набор символов, но не будет экранирования строки, например PDO :: quote . Вы можете подумать, что вам не нужно беспокоиться о ручном экранировании примитивной строки, потому что вы используете подготовленные операторы, но на самом деле подавляющее большинство пользователей pdo: mysql неосознанно используют эмулированные подготовленные операторы , потому что это было по умолчанию установка для драйвера pdo: mysql уже очень давно. Эмулируемый подготовленный оператор не использует настоящие нативные подготовленные операторы mysql, как это предусмотрено mysql api; вместо этого php эквивалентен вызову PDO::quote() для всех ваших значений и str_replacing'у для всех ваших заполнителей с указанными для вас значениями.

Поскольку вы не можете правильно экранировать строку, если не знаете используемый набор символов, эти эмулированные подготовленные операторы уязвимы для внедрения SQL, если вы переключились на определенные наборы символов с помощью set names. Независимо от возможности внедрения SQL, вы все равно можете разорвать ваши строки, если используете схему перехода, предназначенную для другого набора символов.

Для драйвера pdo mysql вы можете указать набор символов при подключении, указав в DSN . Если вы это сделаете, клиентская библиотека и сервер будут знать о наборе символов, и поэтому все будет работать так, как должно.

// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');

Но неправильное экранирование строки - не единственная проблема. Например, у вас также могут возникнуть проблемы с использованием PDO :: bindColumn , поскольку имена столбцов указываются в виде строк, и поэтому снова имеет значение кодировка. Примером может быть имя столбца с именем ütube (обратите внимание на умляут), и вы переключаетесь с latin на utf8 с помощью набора имен, а затем вы пытаетесь $stmt->bindColumn('ütube', $var); с ütube, являющимся строкой в ​​кодировке utf8, потому что Ваш php файл имеет кодировку utf8. Это не сработает, вам нужно будет закодировать строку как вариант latin1 ... и теперь у вас происходит все виды сумасшествия.

9 голосов
/ 30 октября 2009

Не уверен насчет py, но php теперь имеет mysql_set_charset, что говорит о том, что это «предпочтительный способ изменить кодировку [и] с помощью mysql_query () для выполнения SET NAMES». " Обратите внимание, что эта функция была введена для MySQL 5.0.7, поэтому она не будет работать с более ранними версиями.

mysql_set_charset('utf8', $link);

Где $ link - это соединение, созданное с помощью mysql_connect

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...