Sự khác biệt giữa utf8_general_ci và utf8_unicode_ci là gì?


1063

Giữa utf8_general_ciutf8_unicode_ci, có sự khác biệt nào về hiệu suất?



6
Nếu bạn thích utf8[mb4]_unicode_ci, bạn có thể thích utf8[mb4]_unicode_520_cinhiều hơn nữa.
Rick James

8
Tôi không biết tôi cảm thấy thế nào về điều đó - thay vì sửa lỗi triển khai theo tiêu chuẩn Unicode mới nhất, họ vẫn giữ phiên bản lỗi thời như mặc định và mọi người phải thêm "520" để sử dụng phiên bản phù hợp ngay bây giờ. Và nó không tương thích về phía trước và ngược vì bạn không thể sử dụng phiên bản "520" trên các phiên bản MySQL cũ hơn. Tại sao họ không thể cập nhật đối chiếu hiện tại của họ? Tương tự với "mb4", thực sự. Mã nào thực sự phụ thuộc vào hành vi cũ, bị giới hạn / lỗi thời để biện minh cho việc giữ đó là mặc định?
thomasrutter

7
Vẫn tốt hơn là mặc định của 8.0 utf8mb4_0900_ai_ci.
Rick James

Câu trả lời:


1591

Cả hai bộ sưu tập này đều dành cho mã hóa ký tự UTF-8. Sự khác biệt là cách văn bản được sắp xếp và so sánh.

Lưu ý: Trong MySQL bạn phải sử dụng utf8mb4chứ không phải utf8. Một cách khó hiểu, utf8là một triển khai UTF-8 còn thiếu sót từ các phiên bản MySQL ban đầu chỉ còn để tương thích ngược. Phiên bản cố định đã được đặt tên utf8mb4.

Lưu ý: Các phiên bản mới hơn của MySQL đã cập nhật các quy tắc sắp xếp Unicode, có sẵn dưới các tên như utf8mb4_0900_ai_ci các quy tắc tương đương dựa trên Unicode 9.0 - và không có _general biến thể tương đương . Những người đọc nó bây giờ có lẽ nên sử dụng một trong những bộ sưu tập mới hơn thay vì _unicode hoặc _general . Phần lớn những gì được viết dưới đây không còn được quan tâm nữa nếu bạn có thể sử dụng một trong những bộ sưu tập mới hơn để thay thế.

Sự khác biệt chính

  • utf8mb4_unicode_ci được dựa trên các quy tắc Unicode chính thức để sắp xếp và so sánh phổ quát, sắp xếp chính xác trong một loạt các ngôn ngữ.

  • utf8mb4_general_cilà một bộ quy tắc sắp xếp đơn giản hóa nhằm mục đích thực hiện tốt nhất có thể trong khi thực hiện nhiều thao tác rút gọn được thiết kế để cải thiện tốc độ. Nó không tuân theo các quy tắc Unicode và sẽ dẫn đến việc sắp xếp hoặc so sánh không mong muốn trong một số tình huống, chẳng hạn như khi sử dụng các ngôn ngữ hoặc ký tự cụ thể.

    Trên các máy chủ hiện đại, hiệu suất tăng này sẽ là tất cả nhưng không đáng kể. Nó đã được nghĩ ra trong một thời gian khi các máy chủ có một phần rất nhỏ hiệu năng CPU của các máy tính ngày nay.

Lợi ích của utf8mb4_unicode_cihơnutf8mb4_general_ci

utf8mb4_unicode_ci, sử dụng các quy tắc Unicode để sắp xếp và so sánh, sử dụng một thuật toán khá phức tạp để sắp xếp chính xác trong một loạt các ngôn ngữ và khi sử dụng một loạt các ký tự đặc biệt. Các quy tắc này cần phải tính đến các quy ước cụ thể về ngôn ngữ; không phải ai cũng sắp xếp các nhân vật của mình theo cái mà chúng ta gọi là "thứ tự chữ cái".

Theo như ngôn ngữ Latin (tức là "Châu Âu"), không có nhiều khác biệt giữa sắp xếp Unicode và utf8mb4_general_cisắp xếp đơn giản hóa trong MySQL, nhưng vẫn có một vài khác biệt:

  • Ví dụ: đối chiếu Unicode sắp xếp "ß" như "ss" và "" như "OE" như mọi người sử dụng các ký tự đó thường muốn, trong khi utf8mb4_general_cisắp xếp chúng thành các ký tự đơn (có lẽ giống như "s" và "e") .

  • Một số ký tự Unicode được định nghĩa là không thể biết được, điều đó có nghĩa là chúng không nên được tính theo thứ tự sắp xếp và thay vào đó nên chuyển sang ký tự tiếp theo. utf8mb4_unicode_cixử lý những điều này đúng.

Trong các ngôn ngữ phi Latinh, như ngôn ngữ châu Á hoặc ngôn ngữ có bảng chữ cái khác nhau, có thể có nhiều sự khác biệt hơn giữa sắp xếp Unicode và utf8mb4_general_cisắp xếp đơn giản hóa . Sự phù hợp của utf8mb4_general_ciý chí phụ thuộc rất nhiều vào ngôn ngữ được sử dụng. Đối với một số ngôn ngữ, nó sẽ khá bất cập.

Bạn nên dùng gì?

Gần như chắc chắn không có lý do để sử dụng utf8mb4_general_cinữa, vì chúng ta đã bỏ lại điểm mà tốc độ CPU đủ thấp để sự khác biệt hiệu năng sẽ là quan trọng. Cơ sở dữ liệu của bạn gần như chắc chắn sẽ bị giới hạn bởi các nút thắt khác hơn thế này.

Trước đây, một số người khuyên nên sử dụng utf8mb4_general_citrừ khi việc sắp xếp chính xác sẽ đủ quan trọng để biện minh cho chi phí hiệu suất. Ngày nay, chi phí hiệu năng đó đã biến mất và các nhà phát triển đang đối xử với quốc tế hóa nghiêm túc hơn.

Có một lập luận được đưa ra là nếu tốc độ quan trọng với bạn hơn độ chính xác, thì bạn cũng có thể không thực hiện bất kỳ sự sắp xếp nào cả. Thật là tầm thường để làm cho thuật toán nhanh hơn nếu bạn không cần nó chính xác. Vì vậy, utf8mb4_general_cilà một sự thỏa hiệp có lẽ không cần thiết vì lý do tốc độ và có lẽ cũng không phù hợp với lý do chính xác.

Một điều khác tôi sẽ nói thêm là ngay cả khi bạn biết ứng dụng của mình chỉ hỗ trợ ngôn ngữ tiếng Anh, thì vẫn có thể cần xử lý tên của mọi người, thường có thể chứa các ký tự được sử dụng trong các ngôn ngữ khác, trong đó việc sắp xếp chính xác cũng quan trọng. . Sử dụng các quy tắc Unicode cho mọi thứ giúp bạn yên tâm hơn rằng những người Unicode rất thông minh đã làm việc rất chăm chỉ để sắp xếp công việc đúng cách.

Các bộ phận có nghĩa là gì

Thứ nhất, cilà để phân loại và so sánh không phân biệt chữ hoa chữ thường . Điều này có nghĩa là nó phù hợp với dữ liệu văn bản và trường hợp không quan trọng. Các loại đối chiếu khác là cs(phân biệt chữ hoa chữ thường) cho dữ liệu văn bản trong trường hợp là quan trọng và binđối với trường hợp mã hóa cần khớp, bit cho bit, phù hợp với các trường thực sự được mã hóa dữ liệu nhị phân (ví dụ: Cơ sở64). Sắp xếp phân biệt chữ hoa chữ thường dẫn đến một số kết quả kỳ lạ và so sánh phân biệt chữ hoa chữ thường có thể dẫn đến các giá trị trùng lặp chỉ khác nhau trong trường hợp chữ cái, do đó, các đối chiếu phân biệt chữ hoa chữ thường không phù hợp với dữ liệu văn bản - nếu trường hợp đó có ý nghĩa quan trọng đối với bạn và như vậy có lẽ cũng rất quan trọng, và đối chiếu nhị phân có thể phù hợp hơn.

Tiếp theo, unicodehoặc generalđề cập đến các quy tắc sắp xếp và so sánh cụ thể - cụ thể, cách văn bản được chuẩn hóa hoặc so sánh. Có nhiều bộ quy tắc khác nhau cho mã hóa ký tự utf8mb4, unicodegenerallà hai bộ cố gắng hoạt động tốt trong tất cả các ngôn ngữ có thể thay vì một ngôn ngữ cụ thể. Sự khác biệt giữa hai bộ quy tắc này là chủ đề của câu trả lời này. Lưu ý rằng unicodesử dụng các quy tắc từ Unicode 4.0. Các phiên bản gần đây của MySQL thêm các unicode_520quy tắc sử dụng quy tắc từ Unicode 5.2 và 0900(bỏ phần "unicode_") bằng cách sử dụng quy tắc từ Unicode 9.0.

Và cuối cùng, utf8mb4tất nhiên là mã hóa ký tự được sử dụng trong nội bộ. Trong câu trả lời này, tôi chỉ nói về mã hóa dựa trên Unicode.


218
@KahWeeTeng Bạn nên không bao giờ, bao giờ sử dụng utf8_general_ci: nó chỉ đơn giản không làm việc. Đó là một sự trở lại với những ngày xưa tồi tệ của sự khập khiễng của ASCII từ năm mươi năm trước. Kết hợp không phân biệt chữ hoa chữ thường có thể được thực hiện mà không có bản đồ chữ cái từ UCD. Ví dụ, trong đó có ba sigmas khác nhau trong đó; hoặc cách viết thường của dòng chữ TSCHüẞ Điên là một chữ viết hoa, nhưng chữ hoa của chữ viết hoa là chữ TSCHÜSS. Bạn có thể đúng, hoặc bạn có thể nhanh chóng. Do đó, bạn phải sử dụng utf8_unicode_ci, bởi vì nếu bạn không quan tâm đến tính chính xác, thì việc làm cho nó nhanh chóng vô cùng.
tchrist

7
Sau khi đọc điều này, tôi cũng phát hiện ra rằng utf8_unicode_ci sẽ xem xét bất kỳ ký tự nào có cùng trọng số đối chiếu là như nhau cho mục đích so sánh bình đẳng. Điều này dẫn đến trường hợp "か" == "が"hoặc "ǽ" == "æ". Để sắp xếp điều này có ý nghĩa nhưng có thể gây ngạc nhiên khi lựa chọn thông qua các đẳng thức hoặc xử lý các chỉ số duy nhất - bug.mysql.com/orms.php?id=16526
Mat Schaffer

4
@DanHorvat Lý do thực tế duy nhất để giới hạn bản thân bạn trong tập hợp Unicode cũ hơn, hạn chế hơn của MySQL là nếu bạn có phiên bản MySQL cũ không hỗ trợ utf8mb4 hoàn chỉnh hơn. 5.5.3 là trên 5 tuổi. Tôi đánh giá cao Plesk chạy theo lịch trình MySQL khác, nhưng hầu hết các bản phân phối đều có trên MySQL 5.5 và Plesk 11.x hiện hỗ trợ MySQL 5.5 nếu bạn cập nhật các thành phần của nó.
thomasrutter

22
Tôi sẽ không đồng ý rằng việc sử dụng biến thể khiếu nại tiêu chuẩn hơn, mới hơn là một cách làm tồi tệ và tôi nghĩ rằng thật khó chịu khi gọi mọi người là những nhà phát triển tồi tệ hơn những thứ như thế này. Bạn cũng có thể muốn lưu ý rằng câu trả lời của tôi có nghĩa là " trong các phiên bản mới của MySQL sử dụng utf8mb4, thay vì utf8", tôi nhấn mạnh.
thomasrutter

24
@DanHorvat utf8mb4lựa chọn chính xác duy nhất . Với utf8bạn bị mắc kẹt trong một số biến thể UTF8 chỉ có 3 byte của MySQL mà chỉ MySQL (và MariaDB) biết phải làm gì với. Phần còn lại của thế giới đang sử dụng UTF8, có thể chứa tối đa 4 byte cho mỗi ký tự . Các nhà phát triển MySQL đã đặt tên sai cho mã hóa homebrew của họ utf8và để không phá vỡ tính tương thích ngược, giờ đây họ phải tham khảo UTF8 thực sự như utf8mb4.
Stijn de Witt

162

Tôi muốn biết sự khác biệt hiệu suất giữa việc sử dụng utf8_general_ciutf8_unicode_ci, nhưng tôi không tìm thấy bất kỳ điểm chuẩn nào được liệt kê trên internet, vì vậy tôi đã quyết định tự tạo điểm chuẩn.

Tôi đã tạo một bảng rất đơn giản với 500.000 hàng:

CREATE TABLE test(
  ID INT(11) DEFAULT NULL,
  Description VARCHAR(20) DEFAULT NULL
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;

Sau đó, tôi điền nó với dữ liệu ngẫu nhiên bằng cách chạy thủ tục được lưu trữ này:

CREATE PROCEDURE randomizer()
BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE random CHAR(20) ;
  theloop: loop
    SET random = CONV(FLOOR(RAND() * 99999999999999), 20, 36);
    INSERT INTO test VALUES (i+1, random);
    SET i=i+1;
    IF i = 500000 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END

Sau đó, tôi đã tạo các thủ tục được lưu trữ sau đây để điểm chuẩn đơn giản SELECT, SELECTvới LIKEvà sắp xếp ( SELECTvới ORDER BY):

CREATE PROCEDURE benchmark_simple_select()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description = 'test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_select_like()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description LIKE '%test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_order_by()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE ID > FLOOR(1 + RAND() * (400000 - 1))
    ORDER BY Description COLLATE utf8_general_ci LIMIT 1000;
    SET i = i + 1;
    IF i = 10 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

Trong các thủ tục lưu trữ trên utf8_general_ciđối chiếu được sử dụng, nhưng tất nhiên trong các thử nghiệm tôi đã sử dụng cả utf8_general_ciutf8_unicode_ci.

Tôi đã gọi mỗi thủ tục được lưu trữ 5 lần cho mỗi đối chiếu (5 lần cho utf8_general_civà 5 lần cho utf8_unicode_ci) và sau đó tính các giá trị trung bình.

Kết quả của tôi là:

benchmark_simple_select()

  • với utf8_general_ci: 9,957 ms
  • với utf8_unicode_ci: 10.271 ms

Trong tiêu chuẩn này sử dụng utf8_unicode_cilà chậm hơn utf8_general_ci3,2%.

benchmark_select_like()

  • với utf8_general_ci: 11,441 ms
  • với utf8_unicode_ci: 12.811 ms

Trong tiêu chuẩn này sử dụng utf8_unicode_cilà chậm hơn utf8_general_ci12%.

benchmark_order_by()

  • với utf8_general_ci: 11.944 ms
  • với utf8_unicode_ci: 12.887 ms

Trong điểm chuẩn này, sử dụng utf8_unicode_cichậm hơn utf8_general_ci7,9%.


16
Điểm chuẩn đẹp, cảm ơn đã chia sẻ. Tôi đang nhận được những con số tương tự hợp lý (MySQL v5.6.12 trên Windows): 10%, 4%, 8%. Tôi đồng tình: hiệu suất đạt được utf8_general_cilà quá tối thiểu để có giá trị sử dụng.
RandomSeed

10
1) Nhưng định nghĩa này không tạo ra kết quả tương tự cho hai đối chiếu theo định nghĩa? Ý tôi là CONV(FLOOR(RAND() * 99999999999999), 20, 36)chỉ tạo ASCII và không có ký tự Unicode nào được xử lý bằng thuật toán của các đối chiếu. 2) Description = 'test' COLLATE ...Description LIKE 'test%' COLLATE ...chỉ xử lý một chuỗi ("kiểm tra") trong thời gian chạy, phải không? 3) Trong các ứng dụng thực, các cột được sử dụng theo thứ tự có thể sẽ được lập chỉ mục và tốc độ lập chỉ mục trên các đối chiếu khác nhau với văn bản không phải ASCII thực có thể khác nhau.
Halil zgür

2
@ HalilÖzgür - quan điểm của bạn là một phần sai. Tôi đoán đó không phải là về giá trị tiền mã hóa nằm ngoài ASCII (mà general_ci sẽ xử lý chính xác), mà là về các tính năng cụ thể, như xử lý các ô được viết là "Uml ea ute" hoặc một số phép tinh tế như vậy.
Tomasz Gandor

38

Bài đăng này mô tả nó rất độc đáo.

Tóm lại: utf8_unicode_ci sử dụng Thuật toán đối chiếu Unicode như được định nghĩa trong các tiêu chuẩn Unicode, trong khi utf8_general_ci là một thứ tự sắp xếp đơn giản hơn dẫn đến kết quả sắp xếp "kém chính xác" hơn.


1
cảm ơn. đó là ấn tượng của tôi tôi sẽ đạt hiệu suất cao :)
onassar

7
Nếu bạn không quan tâm đến tính chính xác, thì việc thực hiện bất kỳ thuật toán nào là vô cùng nhanh chóng. Chỉ cần sử dụng utf8_unicode_civà giả vờ cái khác không tồn tại.
tchrist

1
@tchrist nhưng nếu bạn quan tâm đến sự cân bằng nhất định giữa độ chính xác và tốc độ, utf8_general_cicó thể là dành cho bạn
Shelvacu

@tchrist Không bao giờ trở thành lập trình viên trò chơi;)
Stijn de Witt

1
@onassar - MySQL 8.0 tuyên bố đã cải thiện đáng kể hiệu suất của tất cả các đối chiếu.
Rick James

9

Xem hướng dẫn sử dụng mysql, phần Bộ ký tự Unicode :

Đối với bất kỳ bộ ký tự Unicode nào, các thao tác được thực hiện bằng cách sử dụng đối chiếu _general_ci sẽ nhanh hơn các thao tác đối với đối chiếu _unicode_ci. Ví dụ, so sánh cho đối chiếu utf8_general_ci nhanh hơn, nhưng ít chính xác hơn so với so sánh cho utf8_unicode_ci. Lý do cho điều này là utf8_unicode_ci hỗ trợ ánh xạ như mở rộng; nghĩa là, khi một nhân vật so sánh bằng với sự kết hợp của các nhân vật khác. Ví dụ, trong tiếng Đức và một số ngôn ngữ khác, ßvà bằng với ss ss. utf8_unicode_ci cũng hỗ trợ các cơn co thắt và các ký tự không thể biết được. utf8_general_ci là một đối chiếu kế thừa không hỗ trợ các bản mở rộng, các cơn co thắt hoặc các ký tự không thể biết được. Nó chỉ có thể thực hiện so sánh một-một giữa các nhân vật.

Vì vậy, để tóm tắt, utf_general_ci sử dụng một bộ so sánh nhỏ hơn và ít chính xác hơn (theo tiêu chuẩn) so với utf_unicode_ci sẽ thực hiện toàn bộ tiêu chuẩn. Bộ general_ci sẽ nhanh hơn vì có ít tính toán hơn.


18
Không có điều gì như là một chút ít đúng chính xác. Tính chính xác là một đặc tính boolean; nó không thừa nhận sửa đổi mức độ. Chỉ cần sử dụng utf8_unicode_civà giả vờ phiên bản lỗi bị lỗi không tồn tại.
tchrist

2
Tôi gặp vấn đề khi lấy 5.6.15 để thực hiện cài đặt collation_connection và hóa ra bạn phải vượt qua nó trong dòng SET như 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'. Tín dụng cho Mathias Bynens cho giải pháp, đây là hướng dẫn rất hữu ích của anh ấy: mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert

4
@tchrist Vấn đề với việc nói đúng là boolean là nó không tính đến các tình huống không dựa vào tính chính xác tuyệt đối. Điểm cơ bản của bạn không phải là không hợp lệ và tôi cũng không cố gắng tán thành lợi ích của general_ci, nhưng tuyên bố chung về tính đúng đắn của bạn rất dễ bị bác bỏ. Tôi làm điều đó hàng ngày trong nghề nghiệp của tôi. Hài kịch sang một bên, Stuart có một điểm tốt ở đây .
Anthony

5
Với định vị địa lý hoặc phát triển trò chơi, chúng tôi luôn trao đổi tính chính xác với hiệu suất. Và tất nhiên tính chính xác là một con số thực giữa 01, không phải là một bool. :) EG chọn các điểm địa lý trong hộp giới hạn là một xấp xỉ 'điểm gần đó' không tốt bằng tính toán khoảng cách giữa điểm và điểm tham chiếu và lọc theo đó. Nhưng cả hai đều là một xấp xỉ và trên thực tế, tính chính xác hoàn toàn hầu như không thể đạt được. Xem nghịch lý đường bờ biểnIEEE 754
Stijn de Witt

4
TL; DR : Vui lòng cung cấp chương trình in kết quả chính xác cho1/3
Stijn de Witt

7

Nói ngắn gọn:

Nếu bạn cần sắp xếp thứ tự tốt hơn - sử dụng utf8_unicode_ci (đây là phương pháp ưa thích),

nhưng nếu bạn hoàn toàn quan tâm đến hiệu suất - hãy sử dụng utf8_general_ci , nhưng biết rằng nó hơi lỗi thời.

Sự khác biệt về hiệu suất là rất nhẹ.


1
Cả hai đều lỗi thời - xem câu trả lời được chấp nhận để biết thêm
thomasrutter

OK, cảm ơn bạn @thomasrutter
simhumileco

6

Một số chi tiết (PL)

Như chúng ta có thể đọc ở đây ( Peter Gulutzan ) có sự khác biệt trong việc sắp xếp / so sánh chữ đánh bóng "" (L với đột quỵ - html esc Ł:) (chữ thường: "ł" - html esc ł:) - chúng ta có giả định sau:

utf8_polish_ci      Ł greater than L and less than M
utf8_unicode_ci     Ł greater than L and less than M
utf8_unicode_520_ci Ł equal to L
utf8_general_ci     Ł greater than Z

Trong thư ngôn ngữ đánh bóng Ł là sau thư Lvà trước M. Không ai trong số mã hóa này tốt hơn hay xấu hơn - nó phụ thuộc vào nhu cầu của bạn.


1

Có hai sự khác biệt lớn về cách sắp xếp và khớp ký tự:

Phân loại :

  • utf8mb4_general_ci xóa tất cả các dấu và sắp xếp từng điểm một có thể tạo ra kết quả sắp xếp không chính xác.
  • utf8mb4_unicode_ci sắp xếp chính xác.

Phù hợp với nhân vật

Họ phù hợp với các nhân vật khác nhau.

Ví dụ, trong utf8mb4_unicode_cibạn có i != ı, nhưng trong utf8mb4_general_cinó giữ ı=i.

Ví dụ, hãy tưởng tượng bạn có một hàng với name="Yılmaz". Sau đó

select id from users where name='Yilmaz';

sẽ trả về hàng nếu sắp xếp thứ tự utf8mb4_general_ci, nhưng nếu nó được sắp xếp chung utf8mb4_unicode_cithì nó sẽ không trả về hàng!

Mặt khác, chúng ta có điều đó a=ªß=sstrong utf8mb4_unicode_ciđó không phải là trường hợp utf8mb4_general_ci. Vì vậy, hãy tưởng tượng bạn có một hàng với name="ªßi", sau đó

select id from users where name='assi';

sẽ trả về hàng nếu collocation là utf8mb4_unicode_ci, nhưng sẽ không trả về một hàng nếu collocation được đặt thành utf8mb4_general_ci.

Một danh sách đầy đủ các trận đấu cho mỗi sắp xếp thứ tự có thể được tìm thấy ở đây .


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.