O que faz com a chave primária como a última coluna em um índice secundário composto em uma tabela do InnoDB?

8

Digamos que eu tenha uma relação de um-para-N (person_id, pet_id). Eu tenho uma tabela onde pet_idé a chave primária.

Entendo que um índice secundário do InnoDB é essencialmente uma árvore B, onde os valores são os valores da chave primária correspondentes para a linha.

Agora, suponha que uma pessoa possa ter milhares de animais de estimação e eu geralmente quero os animais de estimação em ordem de pet_id. Então seria importante se os registros no índice secundário forem classificados por (person_id, pet_id)ou apenas person_idcom os pet_idpara que person_idnão sejam classificados. Adivinhando o mais tarde.

Então, se person_idnão for único, os registros são classificados fisicamente por (person_id, pet_id)ou APENAS pet_id?

obrigado

user3391564
fonte
11
Suponho que a última pergunta seja realmente: "Então, se person_idnão for único, os registros são classificados fisicamente por (person_id, pet_id)ou APENAS person_id?"
usar o seguinte comando

Respostas:

7

Não. Se sua tabela possui o mecanismo InnoDB e o PRIMARY KEYé (pet_id), definir um índice secundário como (person_id)ou (person_id, pet_id)não faz diferença.

O índice também inclui a pet_idcoluna, para que os valores sejam classificados como (person_id, pet_id)nos dois casos.

Uma consulta como a sua:

SELECT pet_id FROM yourtable 
WHERE person_id = 127 
ORDER BY pet_id ;

precisará acessar apenas o índice para obter os valores e, mais ainda, não precisará fazer nenhuma classificação, pois os pet_idvalores já estão classificados no índice. Você pode verificar isso observando os planos de execução ( EXPLAIN):


Primeiro, tentamos com uma tabela MyISAM:

 CREATE TABLE table pets 
 ( pet_id int not null auto_increment PRIMARY KEY, 
   person_id int not null, 
   INDEX person_ix (person_id)
 ) ENGINE = myisam ;

INSERT INTO pets (person_id) 
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;

mysql> EXPLAIN SELECT pet_id FROM pets 
               WHERE person_id = 2  
               ORDER BY pet_id asc \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pets
         type: ref
possible_keys: person_ix
          key: person_ix
      key_len: 4
          ref: const
         rows: 3
        Extra: Using where; Using filesort
1 row in set (0.00 sec)

Observe a lista de arquivos!

Agora, MyISAM com índice composto:

 DROP TABLE IF EXISTS pets ;

 CREATE TABLE table pets 
 ( pet_id int not null auto_increment PRIMARY KEY, 
   person_id int not null, 
   INDEX person_ix (person_id, pet_id)            -- composite index
 ) ENGINE = myisam ;

INSERT INTO pets (person_id) 
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;


mysql> EXPLAIN SELECT pet_id FROM pets 
               WHERE person_id = 2  
               ORDER BY pet_id asc \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pets
         type: ref
possible_keys: person_ix
          key: person_ix
      key_len: 4
          ref: const
         rows: 3
        Extra: Using where; Using index
1 row in set (0.00 sec)

O filesort se foi , conforme o esperado.


Agora vamos tentar o mesmo com o mecanismo InnoDB:

 DROP TABLE IF EXISTS pets ;

 CREATE TABLE table pets 
 ( pet_id int not null auto_increment PRIMARY KEY, 
   person_id int not null, 
   INDEX person_ix (person_id)            -- simple index
 ) ENGINE = innodb ;                      -- InnoDB engine

INSERT INTO pets (person_id) 
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;

mysql> EXPLAIN SELECT pet_id FROM pets 
               WHERE person_id = 2  
               ORDER BY pet_id asc \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pets
         type: ref
possible_keys: person_ix
          key: person_ix
      key_len: 4
          ref: const
         rows: 3
        Extra: Using where; Using index
1 row in set (0.00 sec)

Nenhum arquivo sort também! Mesmo que o índice não tenha explicitamente a pet_idcoluna, os valores estão lá e classificados. Você pode verificar se, se definir o índice com (person_id, pet_id), o EXPLAINé idêntico.

Vamos realmente fazer isso, com o InnoDB e o índice composto:

 DROP TABLE IF EXISTS pets ;

 CREATE TABLE table pets 
 ( pet_id int not null auto_increment PRIMARY KEY, 
   person_id int not null, 
   INDEX person_ix (person_id, pet_id)    -- composite index
 ) ENGINE = innodb ;                      -- InnoDB engine

INSERT INTO pets (person_id) 
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;

mysql> EXPLAIN SELECT pet_id FROM pets 
               WHERE person_id = 2  
               ORDER BY pet_id asc \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: pets
         type: ref
possible_keys: person_ix
          key: person_ix
      key_len: 4
          ref: const
         rows: 3
        Extra: Using where; Using index
1 row in set (0.00 sec)

Planos idênticos ao caso anterior.


Para ter 100% de certeza, também executo os dois últimos casos (mecanismo InnoDB, com índices único e composto), permitindo a file_per_tableconfiguração e adicionando alguns milhares de linhas na tabela:

DROP TABLE IF EXISTS ... ;
CREATE TABLE ... ;

mysql> INSERT INTO pets (person_id) 
       VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;
Query OK, 12 rows affected (0.00 sec)
Records: 12  Duplicates: 0  Warnings: 0

mysql> INSERT INTO pets (person_id) 
       VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3),(127) ;
Query OK, 13 rows affected (0.00 sec)
Records: 13  Duplicates: 0  Warnings: 0

mysql> INSERT INTO pets (person_id) 
       VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3),(127) ;
Query OK, 13 rows affected (0.00 sec)
Records: 13  Duplicates: 0  Warnings: 0

mysql> INSERT INTO pets (person_id) 
       SELECT a.person_id+b.person_id-1 
       FROM pets a CROSS JOIN pets b CROSS JOIN pets c ;
Query OK, 54872 rows affected (0.47 sec)
Records: 54872  Duplicates: 0  Warnings: 0

Nos dois casos, a verificação do tamanho real do arquivo gera resultados idênticos :

ypercube@apollo:~$ sudo ls -la /var/lib/mysql/x/ | grep pets
-rw-rw----  1 mysql mysql     8604 Apr 21 07:25 pets.frm
-rw-rw----  1 mysql mysql 11534336 Apr 21 07:25 pets.ibd
ypercubeᵀᴹ
fonte
11
Assumindo InnoDB funciona de forma semelhante a este respeito para o MS SQL Server, não é uma diferença entre um índice em (<some_column>)e (<some_column>, <pk>)porque ON (<some_column>)é equivalente a ON (<some_column>) INCLUDE (<pk>)e não ON (<some_column>, <pk>). Na maioria das circunstâncias, isso tem um significado praticamente nulo, mas se sua PK for aleatória (ou seja, um UUID), ON (<s_c>,<pk>)poderá levar a uma fragmentação extra ou se sua PK for significativa além de ser uma chave e você poderá ORDER BY s_c, pkser mais rápido com o índice. já está totalmente em ordem.
David Spillett
@DavidSpillett Right. O MySQL não possui INCLUDE (columns)funcionalidade. Essa é outra razão pela qual concluí que o (s_c)índice é equivalente (s_c, pk).
precisa saber é o seguinte
Não consigo encontrar documentação para fazer o backup (portanto, posso estar me lembrando errado), mas tenho certeza de que li que o InnoDB não mantém a PK em ordem estável nos índices secundários, a menos que seja solicitado. Embora a diferença seja menor de qualquer maneira. Quando eu próxima tenho tempo para brincar com o MySQL eu vou ter que testar a teoria ...
David Spillett
@DavidSpillett - blog.jcole.us/2013/01/10/… a seção de índices secundários - "Há uma coisa a ser observada nas páginas não folhas do índice secundário: os campos-chave em cluster (PKV) estão incluídos no registro e são considerado parte da chave do registro, não seu valor ". portanto, solicita-os no mínimo no nível das páginas. Não sei exatamente como está dentro de uma única página a partir dessa descrição, mas mesmo se não estiver, isso é simplesmente resolvido por um pequeno buffer - leia PKs de uma página, classifique (no máximo ~ 500? Itens) e busque a ordem; irrelevante.
Jkavalik # 9/16
2

De acordo com a documentação do MySQL sobre os índices agrupados e secundários

Como os índices secundários se relacionam com o índice agrupado

Todos os índices diferentes do índice clusterizado são conhecidos como índices secundários. No InnoDB, cada registro em um índice secundário contém as colunas da chave primária da linha, bem como as colunas especificadas para o índice secundário . O InnoDB usa esse valor de chave primária para procurar a linha no índice em cluster.

Se a chave primária for longa, os índices secundários usarão mais espaço, portanto, é vantajoso ter uma chave primária curta.

Portanto, adicionar a PRIMARY KEY a um índice secundário é definitivamente redundante. Sua entrada de índice gostaria (person_id, pet_id, pet_id). Isso também iria inchar desnecessariamente o índice secundário por ter 2 cópias do PRIMARY KEY.

Para o índice com (person_id), se você executasse uma consulta como esta

SELECT * FROM yourtable WHERE person_id = 127 ORDER BY pet_id;

O PRIMARY KEYestaria totalmente envolvido nessa consulta e produzirá os resultados ordenados de PRIMARY KEYqualquer maneira. Do ponto de vista físico, as linhas são ordenadas por ordem de inserção. Se o pet_id for AUTO_INCREMENT, será pedido pelo número automático.

RolandoMySQLDBA
fonte
11
O Afaik InnoDB não "inchará" o índice adicionando a coluna PK pela segunda vez quando ela já estiver presente. Você pode até usá-lo para especificar uma ordem diferente de colunas PK para chave de várias colunas: quando você tiver PK, (owner_id, pet_id)mas poderá criar uma chave (vet_id, pet_id[, owner_id])para utilizar ordem de coluna diferente.
Jkavalik
2

Dica 1:

PRIMARY KEY(x, id),
INDEX(id) -- where `id` is `AUTO_INCREMENT`

é perfeitamente válido. Tem a vantagem de desempenho de ser mais eficiente quando muitas consultas precisam encontrar várias linhas WHERE x = 123. Ou seja, é um pouco mais eficiente que o "óbvio"

PRIMARY KEY(id),
INDEX(x, id)

A única regra sobre AUTO_INCREMENT(para o InnoDB) é que iddeve ser a primeira coluna em algum índice. Observe que essa regra não diz nada sobre PRIMARYou UNIQUEou 'apenas coluna'.

A dica é útil para mesas enormes que geralmente são buscadas xjunto com outras coisas.

Dica 2: suponha que você tenha

SELECT name FROM tbl WHERE person_id = 12 AND pet_id = 34;

Este é um índice de "cobertura":

INDEX(person_id, pet_id, name)

Ou seja, toda a consulta pode ser feita dentro do BTree do índice. O EXPLAIN dirá "Usando o índice".

Rick James
fonte