Indici: numero intero vs prestazioni della stringa se il numero di nodi è lo stesso


26

Sto sviluppando un'applicazione in Ruby on Rails con il database PostgreSQL (9.4). Nel mio caso d'uso, le colonne nelle tabelle verranno cercate molto frequentemente, poiché l'intero punto dell'applicazione è alla ricerca di attributi molto specifici su un modello.

Attualmente sto decidendo se utilizzare un integertipo o semplicemente utilizzare un tipo di stringa tipico (ad esempio character varying(255), che è l'impostazione predefinita in Rails ) per le colonne, poiché non sono sicuro di quale sarà la differenza di prestazioni nell'indice.

Queste colonne sono enumerazioni . Hanno una dimensione fissa per la quantità di possibili valori che possono avere. La maggior parte delle lunghezze di enum non supera 5, il che significa che l'indice sarebbe più o meno fisso per tutta la durata dell'applicazione ; pertanto, gli indici di numero intero e stringa sarebbero identici nel numero di nodi.

Tuttavia, la stringa che dovrebbe essere indicizzata potrebbe essere lunga circa 20 caratteri, che in memoria è all'incirca 5 volte quella dell'intero (se un numero intero è 4 byte e le stringhe sono ASCII pure a 1 byte per carattere, quindi vale). Non so come i motori di database eseguano l'indicizzazione delle ricerche, ma se deve "scansionare" la stringa fino a quando non corrisponde esattamente , allora in sostanza significa che la ricerca della stringa sarebbe 5 volte più lenta di una ricerca intera; la "scansione" fino a quando la corrispondenza per la ricerca di numeri interi sarebbe di 4 byte anziché di 20. Questo è quello che sto immaginando:

Il valore di ricerca è (intero) 4:

scansione ............................ TROVATO | ottenere record ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

Il valore di ricerca è (stringa) "some_val" (8 byte):

scansione ................................................. .................................... TROVATO | ottenere record ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

Spero che abbia senso. Fondamentalmente, poiché l'intero occupa meno spazio, può essere "abbinato" più velocemente della sua controparte stringa. Forse questa è una supposizione completamente sbagliata, ma non sono un esperto, quindi è per questo che te lo chiedo ragazzi! Suppongo che questa risposta che ho appena trovato sembra supportare la mia ipotesi, ma voglio essere sicuro.

Il numero di possibili valori nella colonna non cambierebbe usando nessuno dei due, quindi l'indice stesso non cambierebbe (a meno che non aggiungessi un nuovo valore all'enum). In questo caso, ci sarebbe una differenza di prestazioni nell'uso di integero varchar(255), o ha un tipo intero ha più senso?


Il motivo per cui sto chiedendo è che il enumtipo di Rails associa numeri interi a chiavi di stringa, ma non sono pensati per essere colonne rivolte all'utente. In sostanza, non è possibile verificare che il valore enum sia valido, poiché un valore non valido ne causerà uno ArgumentErrorprima che sia possibile eseguire qualsiasi convalida. L'uso di un stringtipo consentirebbe le convalide, ma se c'è un costo prestazionale preferirei semplicemente risolvere il problema della convalida.

Risposte:


32

Risposta breve: integerè più veloce di varcharo textsotto ogni aspetto. Non importa molto per piccoli tavoli e / o tasti di scelta rapida. La differenza aumenta con la lunghezza delle chiavi e il numero di righe.

stringa ... lunga 20 caratteri, che in memoria è all'incirca 5 volte quella dell'intero (se un intero è di 4 byte e le stringhe sono ASCII pure a 1 byte per carattere, quindi vale)

Per essere precisi, i tipi di caratteri ( texto varchar) occupano esattamente 21 byte per 20 caratteri ASCII sul disco e 23 byte nella RAM. Valutazione dettagliata:

Anche importante: le COLLATIONregole possono rendere l'ordinamento dei dati dei personaggi più costoso, a differenza dei tipi di dati numerici:

La dimensione dell'indice è probabilmente responsabile della maggior parte delle differenze di performance nella maggior parte dei casi. Considera l' overhead per tupla indice (sostanzialmente uguale a una tabella): 4 byte per il puntatore elemento e 24 byte per l'intestazione tupla. Quindi la tupla indice per integerammonterebbe a 36 byte (inclusi 4 byte di riempimento di allineamento ) e per varchar(20)con 20 caratteri ASCII sarebbe 52 byte (incluso anche il riempimento). Dettagli:

Tutta la teoria a parte: è meglio testare solo:

Postgres 9.5 ha introdotto un'ottimizzazione per l'ordinamento di lunghe stringhe di dati carattere (parola chiave "chiavi abbreviate" ). Ma un bug in alcune funzioni della libreria C su Linux ha costretto il progetto a disabilitare la funzione per le regole di confronto non C in Postgres 9.5.2. Dettagli nelle note di rilascio.

Tuttavia, se utilizzi effettivamente i enumtipi Postgres , la maggior parte di queste considerazioni sono irrilevanti, poiché comunque sono implementate con integervalori internamente. Il manuale:

Un enumvalore occupa quattro byte sul disco.

A parte: varchar(255)usato per dare un senso alle prime versioni di SQL Server, che potevano utilizzare internamente un tipo di dati più efficiente fino al limite di 255 caratteri. Ma la strana limitazione della lunghezza di 255 caratteri non ha alcun impatto speciale sulle prestazioni in Postgres.


1
Non esiste un'ottimizzazione nascosta in SQL Server per varchar(255)vs. es varchar(260). Potrebbe essersi verificato qualcosa del genere con SQL Server 6.x ma questo non è vero da molto tempo.
a_horse_with_no_name

@a_horse_with_no_name: grazie, ho chiarito di conseguenza.
Erwin Brandstetter,

Scusami per aver impiegato così tanto tempo ad accettarlo, sono stato lento nello sviluppo di quel progetto;)
Chris Cirefice,

Questa risposta è ancora valida per Postgres 10, per favore?
Matty,

1
@Matty: ancora valido. E non vedo ancora nulla cambiare per pag. 11.
Erwin Brandstetter,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.