Qu’est-ce que Tarantool ?
Tarantool se positionne comme une base de données en mémoire rapide. Vous pouvez y mettre n’importe quel type de données. De plus, vous pouvez le répliquer et le fragmenter, c’est-à-dire diviser une énorme quantité de données sur plusieurs serveurs et en combiner les résultats pour créer des clusters tolérants aux pannes de type « maître-maître ».
Secondairement, il s’agit d’un serveur d’applications. Vous pouvez y écrire vos propres applications et traiter vos données, par exemple, supprimer les anciens enregistrements de la base de données en arrière-plan selon certaines conditions. Vous pouvez même écrire un serveur HTTP directement dans Tarantool pour traiter les données : afficher le nombre d’enregistrements de la base de données, écrire de nouvelles données et les réduire (comme dans MapReduce) au maître.
J’ai lu un article sur la façon dont les gens de Mail.ru ont créé une file d’attente de messages qui montre d’excellents résultats avec un débit de plus de 20 000 messages par seconde, le tout en seulement 300 lignes de code. Il y a vraiment beaucoup de place ici pour écrire quelque chose d’énorme, et ce ne sera pas des procédures stockées comme dans PostgreS.
Dans cet article, je vais décrire un serveur similaire mais plus simple.
Installation
J’ai configuré trois machines virtuelles standard pour ce test : un disque dur de 20 Go, Ubuntu 18.04, 2 CPU virtuels et 4 Go de RAM.
Installons Tarantool en exécutant un script bash ou en ajoutant un référentiel et en exécutant `apt-get install Tarantool`. Voici le lien vers le script : (`curl -L
tarantoolctl, l’outil principal pour gérer les instances de Tarantool
/etc/tarantool, le répertoire stockant toute la configuration
/ var / log / tarantool, le répertoire stockant les logs
/var/lib/tarantool, le répertoire utilisé pour stocker les données qui sont ensuite subdivisées en instances.
Il existe également le répertoire d’instance, `instance-available` ou `instance-enable`, contenant le fichier de configuration d’instance à exécuter. Ce fichier est en Lua et décrit sur quels ports l’instance écoute, quelle mémoire est disponible, les paramètres du moteur Vinyl, le code qui se déclenche au démarrage du serveur, ainsi que les paramètres de sharding, files d’attente, suppression des données obsolètes, etc.
Les instances fonctionnent de la même manière que dans PostgreSQL. Supposons que vous souhaitiez lancer plusieurs copies d’une base de données qui écoute différents ports. Cela signifie que vous avez besoin de plusieurs instances de base de données s’exécutant sur un serveur mais fonctionnant sur des ports différents. La configuration des instances peut varier considérablement et elles peuvent implémenter une logique nettement différente.
Gestion des instances
Nous avons la commande `tarantoolctl` disponible pour gérer nos instances Tarantool. Si vous exécutez `tarantoolctl check example`, il vérifiera le fichier de configuration et signalera toute erreur de syntaxe.
Vous pouvez vérifier l’état de l’instance en exécutant `tarantoolctl status example`. De la même manière, vous pouvez effectuer `start`, `stop` et `restart`.
Une fois que l’instance est opérationnelle, il existe deux manières d’y accéder.
1. Console d’administration
Par défaut, Tarantool ouvre un socket où du texte ASCII brut est transmis pour fonctionner avec une instance de Tarantool. La connexion à la console d’administration s’effectue toujours sous l’utilisateur `admin`. Il n’y a pas d’authentification, il est donc fortement recommandé d’utiliser cette méthode avec prudence.
Exécutez `tarantoolctl enter name>` pour se connecter à l’instance spécifiée. Cette commande démarre la console et s’y connecte en tant que « admin ». Ne publiez jamais le port de console sur le réseau. Au lieu de cela, laissez-le comme socket unix afin que seuls ceux qui ont un accès en écriture au socket puissent se connecter à Tarantool. Cette méthode est destinée à effectuer des tâches administratives. Pour le traitement des données, utilisez le deuxième moyen de connexion, le protocole binaire. Le paramètre `listen` dans la configuration (module `box`) permet d’ouvrir un port pour les communications externes et de l’utiliser avec le protocole binaire, qui a une authentification obligatoire. Exécutez `tarantoolctl connect port_number` pour vous connecter. Le protocole binaire vous permet de vous connecter à des serveurs distants, d’utiliser l’authentification et d’accorder des droits d’accès. Puisque Tarantool est à la fois une base de données et un serveur d’applications, il contient différents modules. En ce moment, nous allons examiner le module `box`, qui implémente la gestion des données. Lorsque vous écrivez quelque chose dans la « boîte », Tarantool écrit les données sur le disque, les enregistre en mémoire ou les traite d’une autre manière. Entrons dans le module `box` et appelons la fonction `box.once` qui indiquera à Tarantool d’exécuter notre code lors de l’initialisation du serveur. Tout d’abord, nous allons créer un espace pour stocker nos données : Après cela, nous devons créer un index primaire afin de pouvoir rechercher les données. Si l’index primaire n’est pas spécifié, le premier champ de chaque ligne est utilisé comme index primaire par défaut. Ensuite, nous accordons des autorisations de lecture, d’écriture et d’exécution à l’utilisateur « invité », car nous allons l’utiliser pour nous connecter via le protocole binaire. La portée des autorisations est l’instance entière. Par rapport aux bases de données classiques, tout ici est assez simple. Nous avons un espace, une zone où nos données sont stockées. Chaque enregistrement/ligne de base de données est appelé un tuple, qui est présenté comme MessagePack. C’est un format assez soigné – il est binaire et nécessite moins de stockage : 18 octets au lieu de 27. MessagePack est assez pratique à utiliser. Presque chaque ligne, chaque enregistrement de base de données, peut avoir des colonnes entièrement différentes. Tous les espaces à la fois sont accessibles via le module `box.space`. Pour cibler une instance spécifique et obtenir des informations complètes sur celle-ci, exécutez `box.space.example`. Il y a deux moteurs intégrés à Tarantool : Memory et Vinyl. La mémoire stocke tout dans la RAM, de sorte que les opérations sont simples et rapides. Les données sont transférées sur le disque et le mécanisme de journalisation en écriture anticipée garantit que nous ne perdons rien en cas de panne du serveur. Vinyl stocke les données sur le disque d’une manière plus familière, c’est-à-dire que vous pouvez stocker plus de données que vous n’avez de RAM, et Tarantool les lira à partir du disque. Dans cet exemple, nous allons utiliser Memory. Un index primaire doit être créé pour n’importe quel espace, car rien ne fonctionnera sans lui. Comme dans toute base de données, nous allons faire du premier champ un ID d’enregistrement de base de données. Ici, nous spécifions en quoi consiste notre index. Dans notre cas, l’index ne contient qu’une partie : le premier champ d’un tuple. C’est un entier positif de type non signé. Si je me souviens bien, la documentation indique que le nombre maximum non signé est de 18 quintillions. C’est beaucoup. Ensuite, nous pouvons insérer des données à l’aide de la commande `insert`. Étant donné que le premier champ est utilisé comme clé primaire, il doit être unique. Il n’y a pas de limite au nombre de colonnes, nous pouvons donc insérer autant de données que nous le souhaitons. Les colonnes sont présentées au format MessagePack que j’ai mentionné ci-dessus. Maintenant, nous pouvons sortir les données avec la commande `select`. L’exécution de `box.space.example:select` avec la touche {1} affichera le numéro de ligne 1. Si nous omettons la clé, nous allons voir tous les enregistrements de base de données que nous avons. Ils ont tous un nombre différent de colonnes, mais il n’y a pas de colonnes dans Tarantool, à la place, il y a des numéros de champ. Il peut y avoir littéralement autant de données que vous le souhaitez. Disons, par exemple, que nous devons rechercher les données par le deuxième champ. Nous allons avoir besoin d’un index secondaire pour cela. Cette commande « create_index » créera un index secondaire nommé « secondaire ». Maintenant, nous devons spécifier les paramètres d’index. Le type de l’index sera « TREE » ; les valeurs de ce champ peuvent ne pas être uniques, nous définissons donc également `unique: false`. Ensuite, nous devons décrire les parties de l’index. `fieldno` définit le numéro du champ auquel l’index sera lié, et `type` représente le type de valeurs qu’il contient, `string` dans notre cas. Alors voilà : Maintenant, nous pouvons l’appeler comme ceci: Si nous redémarrons notre instance juste après cela et essayons d’accéder à nouveau aux données, nous n’en verrons aucune—il y aura une base de données vide. Tarantool fait des points de contrôle et enregistre les données sur le disque. Si nous arrêtons son travail avant la sauvegarde à venir, nous perdrons toutes les opérations car nous récupérerons la base de données à partir du dernier point de contrôle, qui était, par exemple, il y a deux heures. Enregistrer les données toutes les secondes ne fonctionnera pas non plus, car le vidage constant de 20 Go de données n’est pas une si bonne idée. Pour traiter de telles tâches, des journaux d’écriture anticipée ont été introduits et mis en œuvre. Pour chaque modification des données, un enregistrement est créé dans un petit fichier journal d’écriture anticipée. Chaque enregistrement avant le point de contrôle est enregistré dans ces journaux. Nous définissons la taille de ces fichiers, par exemple, 64 Mo. Une fois le fichier journal plein, Tarantool commence à écrire dans le suivant. Après le redémarrage d’une instance, Tarantool restaure les données du dernier point de contrôle et applique toutes les transactions suivantes jusqu’au moment où l’instance a été arrêtée. Afin de permettre l’écriture anticipée du journal, vous devez spécifier l’option `wal_mode` dans les paramètres `box.cfg`, c’est-à-dire dans votre fichier de configuration Lua : Avec ce que nous avons écrit maintenant, vous pouvez déjà utiliser Tarantool pour stocker des données, et il fonctionnera très rapidement comme une base de données. Et maintenant, la cerise sur le gâteau : ce que vous pouvez réellement faire avec tout cela. Écrivons une application sur Tarantool.2. Protocole binaire pour la connexion à un port spécifique
Le module Box et l’écriture des données
Écriture de données
local function bootstrap()
local space = box.schema.create_space('example')
space:create_index('primary')
box.schema.user.grant('guest', 'read,write,execute', 'universe')
-- Keep things safe by default
-- box.schema.user.create('example', { password = 'secret' })
-- box.schema.user.grant('example', 'replication')
-- box.schema.user.grant('example', 'read,write,execute', 'space', 'example')
end
unix/:/var/run/tarantool/example.control> box.space.example
---
- engine: memtx
before_replace: 'function: 0x41eb02c8'
on_replace: 'function: 0x41eb0568'
ck_constraint: []
field_count: 0
temporary: false
index:
0: &0
unique: true
parts:
- type: unsigned
is_nullable: false
fieldno: 1
id: 0
space_id: 512
type: TREE
name: primary
primary: *0
is_local: false
enabled: true
name: example
id: 512
...
unix/:/var/run/tarantool/example.control>
Indice
les pièces
unix/:/var/run/tarantool/example.control> box.space.example:insert{1, 'test1', 'test2'}
---
- [1, 'test1', 'test2']
...
unix/:/var/run/tarantool/example.control> box.space.example:insert{2, 'test2', 'test3', 'test4'}
---
- [2, 'test2', 'test3', 'test4']
...
unix/:/var/run/tarantool/example.control> box.space.example:insert{3, 'test3'}
---
- [3, 'test3']
...
unix/:/var/run/tarantool/example.control> box.space.example:insert{4, 'test4'}
---
- [4, 'test4']
...
unix/:/var/run/tarantool/example.control>
Sortie de données
box.space.example:create_index( ‘secondary’, { type = ‘TREE’, unique = false, parts = {{field = 2, type =’string’} }})
unix/:/var/run/tarantool/example.control> box.space.example:create_index('secondary', { type="TREE", unique = false, parts = {{field = 2, type="string"}}})
---
- unique: false
parts:
- type: string
is_nullable: false
fieldno: 2
id: 1
space_id: 512
type: TREE
name: secondary
...
unix/:/var/run/tarantool/example.control>
unix/:/var/run/tarantool/example.control> box.space.example.index.secondary:select('test1')
---
- - [1, 'test1', 'test2']
...
La sauvegarde des données
Traitement de l’information
Rédaction d’une candidature
box.cfg {
listen = '0.0.0.0:3301';
io_collect_interval = nil;
readahead = 16320;
memtx_memory = 128 * 1024 * 1024; -- 128Mb
memtx_min_tuple_size = 16;
memtx_max_tuple_size = 128 * 1024 * 1024; --...