Retour aux articles

Cet article a été publié à l'origine sur Laravel Cameroun

Lire l'article original

Laravel

IDS Différence entre Integer vs UUID vs ULID

IDS Différence entre Integer vs UUID vs ULID

Les identifiants peuvent être définis de différentes manières. La méthode traditionnelle consiste à utiliser un identifiant sous la forme d'un nombre entier (integer) qui s'incrémente automatiquement. Lorsque l'on travaille avec des bases de données, il est courant de penser que l'identifiant généré par SQL est suffisant. Cependant, les applications modernes sont beaucoup plus complexes, et il y a beaucoup de pièces différentes auxquelles il faut penser, et les ID entiers ne suffisent pas.

En réalité, de nombreux développeurs ne tiennent pas compte des identifiants uniques lors de la conception d'une base de données. Vous pouvez prendre de meilleures décisions dans votre travail quotidien si vous savez ce que sont les identificateurs uniques et comment les équilibrer dans votre code et votre stockage de données.

Dans cet article, nous allons voir pourquoi les identifiants entiers ne peuvent pas répondre aux besoins des applications modernes et comment les identifiants uniques tels que UUID et ULID peuvent mieux répondre aux exigences des applications modernes, et nous allons voir comment les mettre en place sur Laravel

Integer IDs (par défaut)

Comme indiqué précédemment, la méthode la plus courante utilisée par les applications logicielles pour représenter l'identifiant d'un bit de données spécifique a été l'incrémentation de nombres entiers. Il y a de nombreuses bonnes raisons à cela. Les ID entiers sont faciles à stocker et à trier, faciles à comprendre, ils peuvent être générés automatiquement, et bien d'autres choses encore.

Néanmoins, l'utilisation de nombres entiers incrémentés en tant qu'identifiants présente de nombreux inconvénients, en particulier lorsque l'on travaille avec des applications développées selon l'approche des microservices. Un problème important est que vous ne pouvez pas générer ces identifiants en une seule fois. Il existe quelques techniques que vous pouvez utiliser, comme sauter des valeurs ou varier les valeurs de départ, mais elles sont loin d'être efficaces. Séparer la génération d'identifiants de votre application peut entraîner un point de défaillance unique, ce qui affectera les performances de votre application puisque des allers-retours sont nécessaires entre le système de génération d'identifiants et l'application requérante.

Les identifiants incrémentés sont relativement faciles à deviner et sont vulnérables à la force brute ou à d'autres types d'attaques susceptibles d'entraîner la fuite de données commerciales. Les hackeurs cibleront une application dont les identifiants sont faciles à deviner dans les URL et autres emplacements, car ils sont beaucoup plus faciles à exploiter. Les identifiants peuvent contenir plus d'informations que vous ne pouvez l'imaginer, par exemple le nombre de commandes passées entre la période A et la période B ou la taille d'un ensemble de données spécifique. Il s'agit d'informations précieuses que vous ne voulez pas voir tomber entre de mauvaises mains, car elles pourraient entraîner d'énormes problèmes pour votre entreprise.

Exemple de code

Sur Laravel lors de la création d'une migration, c'est le type d'identifiant qui est choisi par défaut. En effectuant juste la commande php artisan make:migration articles --create=articles vous aurez une classe qui ressemble à ceci

declare(strict_types=1);
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class () extends Migration
{
public function up(): void
{
Schema::create('articles', static function (Blueprint $table): void {
$table->id();
 
//
 
$table->timestamps();
});
}
 
public function down(): void
{
Schema::dropIfExists('articles');
}
};

Et si vous regardant la définition de $table->id() vous allez voir qu'il s'agit en fait d'un entier autoincrément

public function id($column = 'id')
{
return $this->bigIncrements($column);
}

UUID (Universally Unique IDentifier)

L'utilisation d'une valeur aléatoire issue d'une large plage numérique au lieu d'une petite séquence numérique est une solution réaliste qui permet de résoudre la plupart des problèmes. L'identifiant universellement unique, ou UUID comme on l'appelle communément, est un exemple de mise en pratique de cette idée.

Un UUID est un type d'identifiant qui peut être considéré comme unique dans la plupart des cas pratiques et qui est utile dans tous les cas où la génération d'un identifiant unique distribué est nécessaire. Les chances que deux UUID correctement générés soient identiques sont pratiquement nulles, même s'ils sont générés dans des environnements différents.

Les UUID sont des identifiants uniques générés et représentés par des formats normalisés. La spécification RFC 4122 définit les UUID valides. Il décrit les algorithmes qui peuvent générer des UUIDS uniques sans autorité d'émission centralisée. Cinq algorithmes sont inclus dans le RFC, chacun utilisant un mécanisme différent pour générer une valeur. Ces versions sont les suivantes

  • Versions 1 et 2 il s'agit d'UUID temporels générés en combinant des valeurs de date et d'heure, une valeur aléatoire et une partie de l'adresse MAC de l'appareil. Il est pratiquement impossible d'avoir des UUID identiques lorsque les UUID sont générés de cette manière. Les UUID des versions 1 et 2 peuvent être utilisés pour déterminer, par exemple, quel nœud de la base de données a généré l'ID. Cela peut être avantageux, en particulier dans les systèmes distribués.
  • Versions 3 et 5 ces UUID sont générés par hachage d'un identifiant d'espace de noms et d'un nom. Les données relatives à l'espace de noms sont essentiellement un UUID, et les données relatives au nom peuvent être une chaîne aléatoire. Une fois que ces deux valeurs sont hachées ensemble, elles produisent une chaîne alphanumérique de 36 caractères utilisée comme UUID final. Les versions 3 et 5 de l'UUID différent principalement par le fait qu'elles utilisent des algorithmes de hachage différents. La version 3 utilise l'algorithme MD5 tandis que la version 5 utilise SHA-1.
  • Version 4. ces UUID sont des chaînes aléatoires de 36 caractères. Hormis le "4" qui indique la version de l'UUID, tous les autres caractères sont des caractères aléatoires de a à z et de 0 à 9. La génération est entièrement aléatoire, ce qui rend la possibilité d'avoir les mêmes identifiants presque inexistante. En outre, ils ne contiennent pas d'informations d'identification telles que la date et l'heure, l'adresse MAC ou le nom, ce qui peut être une bonne ou une mauvaise chose en fonction du cas d'utilisation.

Avantages

  • Les valeurs UUID sont uniques dans les bases de données, les tables et les serveurs. Cela vous permet de fusionner des lignes provenant de plusieurs bases de données ou de répartir des bases de données sur plusieurs serveurs.
  • Les valeurs UUID ne donnent pas d'informations sur vos données. Elles peuvent donc être utilisées en toute sécurité dans une URL.
  • Les valeurs UUID sont créées n'importe où, ce qui évite les allers-retours avec la base de données et simplifie la logique de l'application.

Inconvénients

  • Les valeurs UUID ont des besoins de stockage plus importants que les entiers.
  • Il est plus difficile de les déboguer.
  • En raison de leur taille et de leur manque d'ordre, les valeurs UUID peuvent entraîner des problèmes de performance.

Applications des UUID

Les UUID conviennent à un large éventail de cas d'utilisation. Voici quelques applications typiques des UUIDs.

Applications web

Les UUID peuvent être générés à partir de l'interface d'une application web sans communiquer avec le serveur ou une base de données. Ils constituent une bonne option pour l'étiquetage d'objets de données tels que les identifiants d'utilisateurs.

Outils tiers

Les outils tiers qui s'intègrent à une application peuvent avoir besoin de générer des identifiants uniques pour diverses raisons. Par exemple, une équipe de marketing qui souhaite mieux connaître ses utilisateurs peut générer un UUID pour enregistrer le comportement d'un utilisateur particulier au cours d'une session.

Systèmes de base de données

Comme nous l'avons déjà mentionné, les UUID sont utiles dans les systèmes de bases de données distribuées. Vous pouvez diviser ou fusionner de grandes tables et les stocker sur plusieurs serveurs. Chaque section d'une même table peut se voir attribuer le même UUID, ce qui permet d'identifier les données comme un ensemble unique lors des opérations de lecture/écriture.

Exemple de code

Après avoir créer votre migration comme dans l'exemple précédent et que vous souhaitez passer sur l'utilisation des UUID vous pouvez modifier votre migration en changeant l'id en uuid

Schema::create('articles', static function (Blueprint $table): void {
- $table->id();
+ $table->uuid('id')->primary();
 
//
 
$table->timestamps();
});

Une fois ceci fait, vous devez aussi préciser à votre Model (Article dans notre exemple) que vous allez utiliser les UUID. Pour cela il vous suffit d'ajouter le trait HasUuids

declare(strict_types=1);
 
namespace App\Models;
 
+use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
final class Article extends Model
{
use HasFactory;
+ use HasUuids;
 
protected $fillable = [];
 
protected $casts = [];
}

Et si vous avez une clé étrangère dans votre migration (par exemple un article qui appartient à un utilisateur). Vous devez préciser aussi le type de cette clé étrangère pour lui dire que votre clé est en fait un uuid

$table->foreignUuid('user_id')
->constrained('users')
->cascadeOnDelete();

ULID (Universally Unique Lexicographically Sortable Identifier)

Bien qu'il s'agisse de l'un des identificateurs universels les plus couramment utilisés dans le domaine du développement logiciel, l'UUID n'est pas une solution universelle. De nombreuses autres implémentations d'identificateurs uniques ont été développées pour résoudre certains des problèmes rencontrés par les UUID. L'implémentation la plus populaire est l'ULID.

L'ULID est une chaîne de 26 caractères (128 bits) composée de dix caractères d'horodatage qui offrent une précision de l'ordre de la milliseconde et de seize caractères générés de manière aléatoire. Ces deux parties sont des chaînes codées en base32 avec 48 bits et 80 bits, respectivement. Cette structure garantit l'unicité de la chaîne tout en permettant de la trier.

Avantages

  • Le principal avantage est que les identifiants peuvent être triés. Vous pouvez les trier par ordre de création grâce à l'horodatage encodé dans l'ULID lors de sa création.
  • Comme les UUID, les ULID sont uniques. La possibilité d'avoir le même identifiant est techniquement nulle.
  • Les ULID n'utilisent pas de caractères spéciaux, ils peuvent donc être utilisés dans des URL ou même en HTML. En outre, ils sont plus courts que les UUID, puisqu'ils comprennent 26 caractères, contre 36 pour les UUID.
  • L'ULID prend en charge près de 50 langages de programmation, tels que Java, Python et JavaScript. Il prend également en charge représentations binaires pour plusieurs langages, dont Python et JavaScript.

Inconvénients

  • Les ULID comportent des horodatages intégrés à l'ID. Bien que cette fonctionnalité soit utile, il existe des cas d'utilisation où les horodatages révèleraient des informations sensibles et devraient donc être évités.
  • Si vous avez besoin d'une précision inférieure à la milliseconde, les capacités de tri fournies par l'ULID ne vous conviendront peut-être pas.
  • Il n'existe pas de normes pour définir les ULID et, contrairement aux UUID, il n'y a pas de RFC. Cela peut s'avérer problématique si vous devez travailler avec des fournisseurs. Toutefois, un ULID peut être défini comme un UUID si nécessaire.
  • Comme indiqué précédemment, les ULID prennent en charge de nombreux langages de programmation. Toutefois, contrairement aux UUID, il n'existe pas de moyen direct de créer des ULID sans installer un langage de programmation et une bibliothèque pour les générer à partir du système d'exploitation.

Applications des ULID

Les ULID conviennent également à de nombreux cas d'utilisation, en particulier lorsqu'un tri est nécessaire. Voici quelques applications courantes des ULID.

  • Les ULID sont efficaces pour le partitionnement des données et l'indexation des bases de données NoSQL. Vous pouvez facilement diviser ou partitionner les bases de données, ce qui permet un tri pratique.
  • Les ULID sont une bonne option lorsque vous travaillez avec des bases de données relationnelles, par exemple, dans les cas où vous souhaitez que les ID auto-incrémentés restent privés tout en continuant à trier et à accéder aux éléments d'une autre colonne.
  • Sur les systèmes distribués, si vous souhaitez suivre l'ordre d'une liste d'événements en fonction de leur ID.

Exemple de code

En reprenant l'exemple de code sur les UUIDs, vous allez plutôt remplacer id par ulid

Schema::create('articles', static function (Blueprint $table): void {
- $table->id();
+ $table->ulid('id')->primary();
 
//
 
$table->timestamps();
});

Et au niveau de votre Model, vous allez utiliser plutôt le trait HasUlids et pour les clés étrangères aussi vous allez vous servir encore de notre exemple précédent mais cette fois en remplaçant $table->foreignUuid('user_id') par $table->foreignUlid('user_id')

Bonus 🚀

Par défaut quand vous créez une nouvelle migration sur Laravel votre table vient avec l'Id comme primary key mais si vous souhaitez modifier ca et que vous travaillez avec les uuids ou avec les ulids, vous pouvez éxécutter la commande

php artisan stub:publish

Cette commande va avoir pour effet de créer un dossier stubs au sein de votre projet avec tous les fichiers stubs (exemple) de fichier qui sont utilisés à chaque que vous faites un php artisan make:{command}. Et vous pouvez modifier chaque stub pour qu'à la création de votre Model, migration les modifications s'applique directement.

Exemple du stub pour la création d'une migration

declare(strict_types=1);
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class () extends Migration
{
public function up(): void
{
Schema::create('{{ table }}', static function (Blueprint $table): void {
$table->ulid('id')->primary();
 
//
 
$table->timestamps();
});
}
 
public function down(): void
{
Schema::dropIfExists('{{ table }}');
}
};

Conclusion

Les UUID et ULID sont des identifiants uniques que vous pouvez utiliser en toute sécurité pour la génération décentralisée d'identités sans vous soucier des collisions. Contrairement aux clés entières traditionnelles, les UUID garantissent l'unicité quel que soit le magasin de données, l'appareil ou l'environnement. Malgré leur utilisation répandue dans le développement de logiciels, les UUID ne sont pas parfaits. L'ULID est un excellent choix à envisager si vous souhaitez générer une valeur aléatoire qui puisse être triée, une caractéristique dont les UUID sont dépourvus. En outre, les ULID sont également très utiles pour un certain nombre de cas d'utilisation et sont considérés comme les successeurs des UUID.