[php] PHP를 이용한 MySQL에 가장 적합한 데이터 정렬은 무엇입니까? [닫은]

입력 할 내용이 100 % 확실하지 않은 일반 웹 사이트에 대해 MySQL에서 데이터 정렬에 “최상의”선택이 있는지 궁금합니다. MySQL, Apache, HTML 및 PHP 내부의 모든 인코딩과 같은 모든 인코딩이 동일해야한다는 것을 이해합니다.

과거에는 PHP를 “UTF-8″로 출력하도록 설정했지만 MySQL에서 어떤 데이터 정렬이 일치합니까? 나는 그것이 UTF-8 것들 중 하나입니다 생각 해요,하지만 난 사용하고 utf8_unicode_ci, utf8_general_ci그리고 utf8_bin전에.



답변

가장 큰 차이점은 정렬 정확도 (언어의 문자를 비교할 때)와 성능입니다. 유일한 특별한 것은 이진 형식의 문자를 비교하기위한 utf8_bin입니다.

utf8_general_ci보다 약간 빠르지 utf8_unicode_ci만 정확도가 떨어집니다 (정렬). 특정 언어 UTF8 인코딩 (예 :가 utf8_swedish_ci) 그 가장 정확한 그 언어에 대한 정렬 할 수 있도록 추가 언어 규칙이 포함되어 있습니다. utf8_unicode_ci특정 언어를 선호해야 할 충분한 이유가없는 한, 대부분의 경우 (작은 성능 향상보다 정확성을 선호합니다)를 사용 합니다.

당신은 MySQL의 설명서에보다 구체적인 유니 코드 문자 집합에 읽을 수 있습니다 – http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html


답변

실제로, utf8_unicode_ci또는 을 사용하고 싶을 것입니다 utf8_general_ci.

  • utf8_general_ci 모든 악센트를 제거하고 마치 ASCII 인 것처럼 정렬하여 정렬
  • utf8_unicode_ci 유니 코드 정렬 순서를 사용하므로 더 많은 언어로 올바르게 정렬

그러나 영어 텍스트 만 저장하기 위해 이것을 사용하는 경우에는 다르지 않습니다.


답변

를 사용할 때 발생할 수있는이 문제에 대해 잘 알고 있어야 utf8_general_ci합니다.

utf8_general_ci데이터 정렬이 사용되는 경우 MySQL은 select 문의 일부 문자를 구분하지 않습니다 . 이로 인해 특히 버그가 많은 버그가 발생할 수 있습니다 (예 : 사용자 이름이 관련된 경우). 데이터베이스 테이블을 사용하는 구현에 따라이 문제로 인해 악의적 인 사용자가 관리자 계정과 일치하는 사용자 이름을 만들 수 있습니다.

이 문제는 5.x 초기 버전에서는 최소한 노출됩니다. 나중에이 동작이 변경되었는지 확실하지 않습니다.

나는 DBA는 아니지만이 문제를 피하기 위해 utf8-bin대소 문자를 구분하지 않고 항상 사용 합니다.

아래 스크립트는 문제를 예로 설명합니다.

-- first, create a sandbox to play in
CREATE DATABASE `sandbox`;
use `sandbox`;

-- next, make sure that your client connection is of the same 
-- character/collate type as the one we're going to test next:
charset utf8 collate utf8_general_ci

-- now, create the table and fill it with values
CREATE TABLE `test` (`key` VARCHAR(16), `value` VARCHAR(16) )
    CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO `test` VALUES ('Key ONE', 'value'), ('Key TWO', 'valúe');

-- (verify)
SELECT * FROM `test`;

-- now, expose the problem/bug:
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get BOTH keys here! MySQLs UTF8 collates that are 
-- case insensitive (ending with _ci) do not distinguish between 
-- both values!
--
-- collate 'utf8_bin' doesn't have this problem, as I'll show next:
--

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get just one key now, as you'd expect.
--
-- This problem appears to be specific to utf8. Next, I'll try to 
-- do the same with the 'latin1' charset:
--

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_general_ci

-- next, convert the values that we've previously inserted
-- in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_general_ci;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected). This shows 
-- that the problem with utf8/utf8_generic_ci isn't present 
-- in latin1/latin1_general_ci
--
-- To complete the example, I'll check with the binary collate
-- of latin1 as well:

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected).
--
-- Finally, I'll re-introduce the problem in the exact same 
-- way (for any sceptics out there):

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_generic_ci

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

-- now, re-check for the problem/bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Two keys.
--

DROP DATABASE sandbox;


답변

utf8mb4데이터 정렬과 함께 문자 집합을 사용하는 것이 가장 좋습니다 utf8mb4_unicode_ci.

문자 세트 utf8는 가능한 문자의 약 6 % 인 소량의 UTF-8 코드 포인트 만 지원합니다. utf8BMP (Basic Multilingual Plane) 만 지원합니다. 16 개의 다른 비행기가 있습니다. 각 비행기는 65,536 자입니다. utf8mb4모든 17 비행기를 지원합니다.

MySQL은 4 바이트 UTF-8 문자를 자르고 데이터가 손상됩니다.

utf8mb4문자 집합 2010-03-24에서 MySQL 5.5.3에서 소개되었습니다.

새 문자 집합을 사용하기 위해 필요한 일부 변경 사항은 간단하지 않습니다.

  • 응용 프로그램 데이터베이스 어댑터에서 변경이 필요할 수 있습니다.
  • 문자 집합 설정, 데이터 정렬 및 innodb_file_format in 바라쿠다로의 전환 등 my.cnf를 변경해야합니다.
  • SQL CREATE 문에는 다음이 포함되어야합니다. ROW_FORMAT=DYNAMIC
    • DYNAMIC은 VARCHAR (192) 이상의 인덱스에 필요합니다.

참고 : 전환 Barracuda에서 Antelope, 두 번 이상 MySQL의 서비스를 다시 시작해야 할 수 있습니다. innodb_file_format_maxMySQL 서비스가 다음으로 다시 시작될 때까지 변경되지 않습니다 innodb_file_format = barracuda.

MySQL은 이전 AntelopeInnoDB 파일 형식을 사용 합니다. Barracuda문자 세트로 전환 한 후 인덱스 및 키를 작성하기 위해 SQL 오류를 발생시키지 않으려는 경우 필요한 동적 행 형식을 지원합니다.utf8mb4

  • # 1709-색인 열 크기가 너무 큽니다. 최대 열 크기는 767 바이트입니다.
  • # 1071-지정된 키가 너무 깁니다. 최대 키 길이는 767 바이트입니다.

다음 시나리오는 MySQL 5.6.17에서 테스트되었습니다. 기본적으로 MySQL은 다음과 같이 구성됩니다.

SHOW VARIABLES;

innodb_large_prefix = OFF
innodb_file_format = Antelope

MySQL 서비스를 중지하고 기존 my.cnf에 옵션을 추가하십시오.

[client]
default-character-set= utf8mb4

[mysqld]
explicit_defaults_for_timestamp = true
innodb_large_prefix = true
innodb_file_format = barracuda
innodb_file_format_max = barracuda
innodb_file_per_table = true

# Character collation
character_set_server=utf8mb4
collation_server=utf8mb4_unicode_ci

SQL CREATE 문 예제 :

CREATE TABLE Contacts (
 id INT AUTO_INCREMENT NOT NULL,
 ownerId INT DEFAULT NULL,
 created timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 contact VARCHAR(640) NOT NULL,
 prefix VARCHAR(128) NOT NULL,
 first VARCHAR(128) NOT NULL,
 middle VARCHAR(128) NOT NULL,
 last VARCHAR(128) NOT NULL,
 suffix VARCHAR(128) NOT NULL,
 notes MEDIUMTEXT NOT NULL,
 INDEX IDX_CA367725E05EFD25 (ownerId),
 INDEX created (created),
 INDEX modified_idx (modified),
 INDEX contact_idx (contact),
 PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB ROW_FORMAT=DYNAMIC;
  • CREATE 문에서 INDEX contact_idx (contact)if ROW_FORMAT=DYNAMIC가 제거 되면 오류 # 1709가 생성 됩니다.

참고 : 색인을 처음 128 자로 제한하면 contact바라쿠다를 사용할 필요가 없습니다.ROW_FORMAT=DYNAMIC

INDEX contact_idx (contact(128)),

참고 : 필드의 크기가이라고 말하면 VARCHAR(128)128 바이트가 아닙니다. 128, 4 바이트 문자 또는 128, 1 바이트 문자를 사용할 수 있습니다.

INSERT명령문은 2 행에 4 바이트 ‘poo’문자를 포함해야합니다.

INSERT INTO `Contacts` (`id`, `ownerId`, `created`, `modified`, `contact`, `prefix`, `first`, `middle`, `last`, `suffix`, `notes`) VALUES
(1, NULL, '0000-00-00 00:00:00', '2014-08-25 03:00:36', '1234567890', '12345678901234567890', '1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678', '', ''),
(2, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '????????????????????????????????????????', '????????????????????????????????????????', '????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????', '', ''),
(3, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '????????????????????????????????????????', '????????????????????????????????????????', '123?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????', '', '');

last열에서 사용 된 공간의 양을 볼 수 있습니다 .

mysql> SELECT BIT_LENGTH(`last`), CHAR_LENGTH(`last`) FROM `Contacts`;
+--------------------+---------------------+
| BIT_LENGTH(`last`) | CHAR_LENGTH(`last`) |
+--------------------+---------------------+
|               1024 |                 128 | -- All characters are ASCII
|               4096 |                 128 | -- All characters are 4 bytes
|               4024 |                 128 | -- 3 characters are ASCII, 125 are 4 bytes
+--------------------+---------------------+

데이터베이스 어댑터에서 연결에 대한 문자 세트 및 데이터 정렬을 설정할 수 있습니다.

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'

PHP에서는 다음과 같이 설정됩니다. \PDO::MYSQL_ATTR_INIT_COMMAND

참고 문헌 :


답변

데이터 정렬은 데이터 정렬 방식과 문자열 비교 방식에 영향을줍니다. 즉, 대부분의 사용자가 기대하는 데이터 정렬을 사용해야합니다.

charset unicode 문서의 예제 :

utf8_general_ci‘ß’가 ‘s’가 아닌 ‘s’와 같다는 점을 제외하고는 독일어와 프랑스어 모두에 만족합니다. 이것이 응용 프로그램에 적합하면 utf8_general_ci더 빠르기 때문에 사용해야합니다
. 그렇지 않으면 utf8_unicode_ci더 정확 하기 때문에 사용 하십시오.

따라서 예상되는 사용자 기반과 올바른 정렬이 얼마나 필요한지에 따라 다릅니다 . 영어 사용자층의 경우 utf8_general_ci스웨덴어와 같은 다른 언어의 경우에는 특수한 데이터 정렬이 작성되어 있어야합니다.


답변

기본적으로 문자열을 어떻게 생각하는지에 달려 있습니다.

Guus가 강조한 문제 때문에 항상 utf8_bin을 사용합니다. 제 생각에는 데이터베이스와 관련하여 문자열은 여전히 ​​문자열입니다. 문자열은 많은 UTF-8 문자입니다. 문자는 이진 표현이므로 사용중인 언어를 알아야하는 이유는 무엇입니까? 일반적으로 사람들은 다국어 사이트 범위의 시스템을위한 데이터베이스를 구축 할 것입니다. 이것이 문자 세트로 UTF-8을 사용하는 요점입니다. 저는 약간 순수하지만 버그 위험이 색인 생성에서 얻을 수있는 약간의 이점보다 훨씬 크다고 생각합니다. 모든 언어 관련 규칙은 DBMS보다 훨씬 높은 수준에서 수행해야합니다.

내 책에서 “가치”는 백만 년 안에 “valúe”와 같아서는 안됩니다.

텍스트 필드를 저장하고 대 / 소문자를 구분하지 않는 검색을 수행하려면 LOWER () 및 php 함수 strtolower ()와 같은 PHP 함수와 함께 MYSQL 문자열 함수를 사용합니다.


답변

UTF-8 텍스트 정보의 경우 다음을 사용해야합니다 utf8_general_ci.

  • utf8_bin: 문자열을 각 문자열의 이진 값으로 비교

  • utf8_general_ci: 일반 언어 규칙을 사용하고 대소 문자를 구분하지 않는 비교를 사용하여 문자열 비교

일명 그것은 데이터를 더 빠르고 효율적으로 / 더 유용하게 검색하고 색인화해야합니다.