| Entrée | Sortie | Alias |
|---|---|---|
| ✔ | ✔ |
Description
Native est disponible ici, et la spécification complémentaire du protocole Native — le protocole TCP sous-jacent qui le transporte — est disponible ici.
Les deux spécifications ont été générées par des LLM à partir du code source de ClickHouse. Le code reste la principale source de référence : en cas de désaccord entre la spécification et le code, c’est le code qui fait foi.
Native est le format le plus efficace de ClickHouse, car il est véritablement « colonnaire »
dans le sens où il ne convertit pas les colonnes en lignes.
Dans ce format, les données sont écrites et lues par blocs, au format binaire.
Pour chaque bloc, le nombre de lignes, le nombre de colonnes, les noms et types des colonnes, ainsi que les parties des colonnes dans le bloc sont enregistrés les uns à la suite des autres.
C’est le format utilisé dans l’interface native pour les échanges entre serveurs, par le client en ligne de commande et par les clients C++.
Format filaire des types de données
Lors de l’utilisation du protocole binaire TCP natif (ou lorsque le endpoint HTTP reçoit
?client_protocol_version=<n>),
une structure BlockInfo est écrite avant le nombre de colonnes et de lignes. Les exemples de cette section utilisent
l’interface HTTP standard sans version de protocole, ce qui omet BlockInfo.Structure d’un bloc
number et str, et comporte trois lignes :
Blocs multiples
Types de données simples
RowBinary/RowBinaryWithNamesAndTypes.
La liste complète des types correspondant à cette description est la suivante :
- (U)Int8, (U)Int16, (U)Int32, (U)Int64, (U)Int128, (U)Int256
- Float32, Float64
- Bool
- String
- FixedString(N)
- Date
- Date32
- DateTime
- DateTime64
- IPv4
- IPv6
- UUID
Types de données complexes
RowBinary et de RowBinaryWithNamesAndTypes.
- Nullable
- LowCardinality
- Array
- Map
- Variant
- Dynamic
- JSON
Nullable
Native, une colonne Nullable est précédée d’un nombre d’octets égal au nombre de lignes du bloc, avant les données proprement dites. Chacun de ces octets indique si la valeur est NULL ou non. Par exemple, avec cette requête, chaque nombre impair sera NULL à la place :
Nullable(String). L’indicateur de valeur nulle provient toujours de l’octet de masque de nullable —
une valeur de masque de 0x01 signifie que la ligne est NULL, quel que soit le contenu de la chaîne. Pour les lignes NULL,
la chaîne sous-jacente est stockée sous la forme d’une chaîne vide (longueur LEB128 0). Notez qu’une chaîne vide non NULL
a également une longueur LEB128 de 0 ; seul l’octet de masque permet donc de distinguer les deux cas. Par exemple, la requête suivante :
LowCardinality
LowCardinality est transparent, le format Native utilise un encodage colonnaire basé sur un dictionnaire. Une colonne est encodée sous la forme d’un préfixe de version, puis d’un dictionnaire de valeurs uniques et d’un tableau d’index entiers pointant vers ce dictionnaire.
Une colonne peut être définie comme
LowCardinality(Nullable(T)), mais il n’est pas possible de la définir comme Nullable(LowCardinality(T)) — cela renverra toujours une erreur du serveur.UInt64(LE) de valeur 1, écrit une fois par colonne. Ensuite, pour chaque bloc, les éléments suivants sont écrits :
UInt64(LE)— champ de bitsIndexesSerializationType. Les bits 0–7 encodent la largeur de l’index (0 = UInt8, 1 = UInt16, 2 = UInt32, 3 = UInt64). Le bit 8 (NeedGlobalDictionaryBit) n’est jamais défini dans le format Native (le serveur lève une exception s’il le rencontre). Le bit 9 indique la présence de clés de dictionnaire supplémentaires. Le bit 10 indique que le dictionnaire doit être réinitialisé.UInt64(LE)— nombre de clés du dictionnaire, suivi des clés sérialisées en bloc à l’aide de l’encodage du type interne.UInt64(LE)— nombre de lignes, suivi des valeurs d’index sérialisées en bloc à l’aide de la largeur UInt appropriée.
String, 0 pour les types numériques). Pour LowCardinality(Nullable(T)), l’index 0 représente NULL, et les clés sont sérialisées sans le wrapper Nullable.
Par exemple, LowCardinality(String) avec 5 lignes ['foo', 'bar', 'baz', 'foo', 'bar'] :
LowCardinality(Nullable(String)), l’indice 0 est NULL :
Array
- N offsets
UInt64cumulatifs (little-endian, 8 octets chacun). La ligneicontientoffset[i] - offset[i-1]éléments, avecoffset[-1]implicitement égal à 0. - Tous les éléments imbriqués de toutes les lignes, sérialisés en bloc de façon contiguë.
Array(UInt32) avec 3 lignes [[0, 10], [1, 11], [2, 12]] :
Array(String) avec 4 lignes [[], ['0'], ['0','1'], ['0','1','2']] :
Map
Map(K, V) est encodé sous la forme Array(Tuple(K, V)) — les offsets du tableau, suivis de toutes les clés, puis de toutes les valeurs. Cela diffère de RowBinary, où les clés et les valeurs sont entrelacées pour chaque entrée.
Par exemple, Map(String, UInt64) avec 3 lignes [{'a':0,'b':10}, {'a':1,'b':11}, {'a':2,'b':12}] :
Variant
Variant est encodée comme suit :
- préfixe du mode des discriminants
UInt64(LE)(0= BASIC,1= COMPACT). La sortie du format Native utilise généralement BASIC (0) ; le mode COMPACT peut apparaître lors de la lecture de données stockées avecuse_compact_variant_discriminators_serializationactivé. - N discriminants
UInt8, un par ligne. - Les données de chaque variant type dans une colonne en bloc distincte ne contenant que les lignes correspondantes, dans l’ordre des discriminants.
Variant(String, UInt32) avec 5 lignes [0::UInt32, 'hello', NULL, 3::UInt32, 'hello'] (triés : String = 0, UInt32 = 1) :
Dynamic
Dynamic en un préfixe de structure suivi d’une colonne Variant.
Le préfixe de structure contient une version de sérialisation UInt64(LE), puis le nombre de types dynamiques (sous forme de VarUInt), puis les noms des types sous forme de chaînes. Dans la version V1, le nombre de types est écrit deux fois pour assurer la compatibilité. Les données qui suivent forment une colonne Variant dont la liste de types correspond aux types dynamiques, plus un type interne SharedVariant, triés par ordre alphabétique.
Par exemple, Dynamic avec 5 lignes [0::UInt32, 'hello', NULL, 3::UInt32, 'hello'] :
JSON
JSON dans une structure en colonnes. L’encodage est complexe et dépend de la version : il se compose d’un préfixe de structure avec la version de sérialisation, des noms de chemins dynamiques et l’organisation des données partagées, suivis de chemins typés (chacun sous la forme d’une colonne en bloc), de chemins dynamiques (chacun sous la forme d’une colonne Dynamic) et de données partagées pour les chemins en dépassement.
Pour une interopérabilité plus simple, envisagez d’utiliser le paramètre output_format_native_write_json_as_string=1, qui sérialise les colonnes JSON sous forme de simples chaînes de texte JSON (une String par ligne).