Em Setembro escrevi sobre as consequências de deixar ao acaso o tamanho e character set de campos VARCHAR. Entretanto gostaria de complementar as observações com o que escrevo abaixo.
A mistura de charsets — como ter uma tabela num charset e um determinado campo dessa tabela com outro charset diferente, por exemplo — deve ser cuidadosamente analisada. Existe alguma probabilidade de ocorrerem complicações, caso se trate esta questão levianamente.
À parte do que se deve ou não fazer, o que eu gostaria era que se compreendesse o benefício que se pode atingir no caso particular da utilização de UUIDs. Já vimos que UTF-8 para este caso particular é um desperdício, mas o que gostaria de demonstrar são se, de facto, compensa alterar campos específicos quando nem todos podem ser alterados.
Vejamos: vão ser criadas 2 tabelas MyISAM em UTF-8, uma com um campo Latin1 e outra com o campo natural, em UTF-8. Vou usar uma tabela que tinha para aqui com 2M de UUIDs apenas para acelerar o processo. Essa tabela, table_uuid, é original Latin1, embora o foco não seja sobre o carregamento dos dados, mas sim do seu cruzamento entre as duas primeiras:
MySQL:
-
mysql> CREATE TABLE charset_latin1 ( id CHAR(36) CHARSET latin1, PRIMARY KEY (id) ) CHARSET utf8;
-
Query OK, 0 rows affected (0.01 sec)
-
-
mysql> CREATE TABLE charset_utf8 ( id CHAR(36) CHARSET utf8, PRIMARY KEY (id) ) CHARSET utf8;
-
Query OK, 0 rows affected (0.01 sec)
-
-
mysql> INSERT INTO charset_utf8 SELECT id FROM table_uuid ;
-
Query OK, 2097152 rows affected (2 min 29.77 sec)
-
Records: 2097152 Duplicates: 0 WARNINGS: 0
-
-
mysql> INSERT INTO charset_latin1 SELECT id FROM table_uuid ;
-
Query OK, 2097152 rows affected (50.15 sec)
-
Records: 2097152 Duplicates: 0 WARNINGS: 0
Agora, suponhamos que a tabela charset_latin1 é uma tabela originalmente em UTF-8, para a qual já fizemos a transformação do campo (que vai albergar UUIDs) para Latin1. O que pretendemos demonstrar são os ganhos/prejuízos do cruzamento de tabelas com as mesmas características, e com diferentes:
MySQL:
-
mysql> SELECT count(1) FROM charset_utf8 a, charset_latin1 b WHERE a.id = b.id;
-
+----------+
-
| count(1) |
-
+----------+
-
| 2097152 |
-
+----------+
-
1 row in SET (1 min 8.82 sec)
-
-
mysql> SELECT count(1) FROM charset_utf8 a, charset_utf8 b WHERE a.id = b.id;
-
+----------+
-
| count(1) |
-
+----------+
-
| 2097152 |
-
+----------+
-
1 row in SET (58.00 sec)
-
-
mysql> SELECT count(1) FROM charset_latin1 a, charset_latin1 b WHERE a.id = b.id;
-
+----------+
-
| count(1) |
-
+----------+
-
| 2097152 |
-
+----------+
-
1 row in SET (24.43 sec)
Ou seja, como já se disse, fazer JOINs com Latin1 é muito rápido, relativamente às outras opções. Mas repare-se quando se cruzam diferentes charsets (1º JOIN): ocorre uma degradação de 19% face ao pior caso.
Ou seja, tendo em conta que estas tabelas podem ser apenas tabelas de relação [quero com isto dizer que não contêm dados que não sirvam senão para cruzamentos/JOINs] é expectável que a performance se degrade sempre que sejam cruzadas com outras cuja chave não esteja no mesmo charset. Deduz-se daqui que existe, portanto, overhead relativo às conversões de charset durante o JOIN.
Apesar de estarmos perante um index scan (ie, com os dados todos em cache), ocorreu-me também experimentar com InnoDB.
MySQL:
-
mysql> ALTER TABLE charset_utf8 engine=INNODB;
-
Query OK, 2097152 rows affected (48.18 sec)
-
Records: 2097152 Duplicates: 0 WARNINGS: 0
-
-
mysql> ALTER TABLE charset_latin1 engine=INNODB;
-
Query OK, 2097152 rows affected (39.43 sec)
-
Records: 2097152 Duplicates: 0 WARNINGS: 0
Convém ressalvar aqui que fiz uns SELECTs primeiro para ter a certeza que os datafiles estavam carregados na buffer pool.
MySQL:
-
mysql> SELECT count(1) FROM charset_utf8 a, charset_latin1 b WHERE a.id = b.id;
-
+----------+
-
| count(1) |
-
+----------+
-
| 2097152 |
-
+----------+
-
1 row in SET (21.65 sec)
-
-
mysql> SELECT count(1) FROM charset_utf8 a, charset_utf8 b WHERE a.id = b.id;
-
+----------+
-
| count(1) |
-
+----------+
-
| 2097152 |
-
+----------+
-
1 row in SET (12.61 sec)
-
-
mysql> SELECT count(1) FROM charset_latin1 a, charset_latin1 b WHERE a.id = b.id;
-
+----------+
-
| count(1) |
-
+----------+
-
| 2097152 |
-
+----------+
-
1 row in SET (8.25 sec)
Ou seja, a conclusão que se pode tirar daqui é que: não só devemos pensar na optimização que se ganha com a escolha do charset, mas também devemos analisar o impacto de não podermos levar a tarefa de reconversão avante na totalidade. A necessidade de cruzar campos com charsets diferentes torna-se, efectivamente, mais penoso. Resta comparar, caso a caso, o benefício que se obtém em queries pouco/nada optimizadas versus o prejuízo de performance que poderá reflectir-se em cruzamentos/JOINs com campos em charsets diferentes.