Ich verwende UUIDs in meinen Systemen seit einiger Zeit aus verschiedenen Gründen, von der Protokollierung bis zur verzögerten Korrelation. Die Formate, die ich verwendet habe, haben sich geändert, als ich weniger naiv wurde von:
VARCHAR(255)
VARCHAR(36)
CHAR(36)
BINARY(16)
Als ich die letzte erreichte BINARY(16)
, begann ich, die Leistung mit der grundlegenden Auto-Inkrement-Ganzzahl zu vergleichen. Der Test und die Ergebnisse sind unten gezeigt, aber wenn Sie nur die Zusammenfassung wollen, bedeutet dies , dass INT AUTOINCREMENT
und BINARY(16) RANDOM
auf Daten identische Leistung haben reicht bis 200.000 bis (die Datenbank wurde Tests vorausgefüllt vor).
Ich war anfangs skeptisch gegenüber der Verwendung von UUIDs als Primärschlüssel und bin es auch heute noch. Ich sehe hier jedoch das Potenzial, eine flexible Datenbank zu erstellen, die beide verwenden kann. Während viele Menschen über die Vorteile von beidem streiten, welche Nachteile werden durch die Verwendung beider Datentypen ausgeglichen?
PRIMARY INT
UNIQUE BINARY(16)
Der Anwendungsfall für diese Art der Einrichtung wäre der traditionelle Primärschlüssel für Beziehungen zwischen Tabellen, wobei eine eindeutige Kennung für Beziehungen zwischen Systemen verwendet wird.
Was ich im Wesentlichen zu entdecken versuche, ist der Unterschied in der Effizienz zwischen den beiden Ansätzen. Abgesehen von dem vierfachen Speicherplatz, der nach dem Hinzufügen zusätzlicher Daten weitgehend vernachlässigbar sein kann, scheinen sie mir gleich zu sein.
Schema:
-- phpMyAdmin SQL Dump
-- version 4.0.10deb1
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Sep 22, 2015 at 10:54 AM
-- Server version: 5.5.44-0ubuntu0.14.04.1
-- PHP Version: 5.5.29-1+deb.sury.org~trusty+3
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- Database: `test`
--
-- --------------------------------------------------------
--
-- Table structure for table `with_2id`
--
CREATE TABLE `with_2id` (
`guidl` bigint(20) NOT NULL,
`guidr` bigint(20) NOT NULL,
`data` varchar(255) NOT NULL,
PRIMARY KEY (`guidl`,`guidr`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `with_guid`
--
CREATE TABLE `with_guid` (
`guid` binary(16) NOT NULL,
`data` varchar(255) NOT NULL,
PRIMARY KEY (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `with_id`
--
CREATE TABLE `with_id` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`data` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=197687 ;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Benchmark einfügen:
function benchmark_insert(PDO $pdo, $runs)
{
$data = 'Sample Data';
$insert1 = $pdo->prepare("INSERT INTO with_id (data) VALUES (:data)");
$insert1->bindParam(':data', $data);
$insert2 = $pdo->prepare("INSERT INTO with_guid (guid, data) VALUES (:guid, :data)");
$insert2->bindParam(':guid', $guid);
$insert2->bindParam(':data', $data);
$insert3 = $pdo->prepare("INSERT INTO with_2id (guidl, guidr, data) VALUES (:guidl, :guidr, :data)");
$insert3->bindParam(':guidl', $guidl);
$insert3->bindParam(':guidr', $guidr);
$insert3->bindParam(':data', $data);
$benchmark = array();
$time = time();
for ($i = 0; $i < $runs; $i++) {
$insert1->execute();
}
$benchmark[1] = 'INC ID: ' . (time() - $time);
$time = time();
for ($i = 0; $i < $runs; $i++) {
$guid = openssl_random_pseudo_bytes(16);
$insert2->execute();
}
$benchmark[2] = 'GUID: ' . (time() - $time);
$time = time();
for ($i = 0; $i < $runs; $i++) {
$guid = openssl_random_pseudo_bytes(16);
$guidl = unpack('q', substr($guid, 0, 8))[1];
$guidr = unpack('q', substr($guid, 8, 8))[1];
$insert3->execute();
}
$benchmark[3] = 'SPLIT GUID: ' . (time() - $time);
echo 'INSERTION' . PHP_EOL;
echo '=============================' . PHP_EOL;
echo $benchmark[1] . PHP_EOL;
echo $benchmark[2] . PHP_EOL;
echo $benchmark[3] . PHP_EOL . PHP_EOL;
}
Benchmark auswählen:
function benchmark_select(PDO $pdo, $runs) {
$select1 = $pdo->prepare("SELECT * FROM with_id WHERE id = :id");
$select1->bindParam(':id', $id);
$select2 = $pdo->prepare("SELECT * FROM with_guid WHERE guid = :guid");
$select2->bindParam(':guid', $guid);
$select3 = $pdo->prepare("SELECT * FROM with_2id WHERE guidl = :guidl AND guidr = :guidr");
$select3->bindParam(':guidl', $guidl);
$select3->bindParam(':guidr', $guidr);
$keys = array();
for ($i = 0; $i < $runs; $i++) {
$kguid = openssl_random_pseudo_bytes(16);
$kguidl = unpack('q', substr($kguid, 0, 8))[1];
$kguidr = unpack('q', substr($kguid, 8, 8))[1];
$kid = mt_rand(0, $runs);
$keys[] = array(
'guid' => $kguid,
'guidl' => $kguidl,
'guidr' => $kguidr,
'id' => $kid
);
}
$benchmark = array();
$time = time();
foreach ($keys as $key) {
$id = $key['id'];
$select1->execute();
$row = $select1->fetch(PDO::FETCH_ASSOC);
}
$benchmark[1] = 'INC ID: ' . (time() - $time);
$time = time();
foreach ($keys as $key) {
$guid = $key['guid'];
$select2->execute();
$row = $select2->fetch(PDO::FETCH_ASSOC);
}
$benchmark[2] = 'GUID: ' . (time() - $time);
$time = time();
foreach ($keys as $key) {
$guidl = $key['guidl'];
$guidr = $key['guidr'];
$select3->execute();
$row = $select3->fetch(PDO::FETCH_ASSOC);
}
$benchmark[3] = 'SPLIT GUID: ' . (time() - $time);
echo 'SELECTION' . PHP_EOL;
echo '=============================' . PHP_EOL;
echo $benchmark[1] . PHP_EOL;
echo $benchmark[2] . PHP_EOL;
echo $benchmark[3] . PHP_EOL . PHP_EOL;
}
Tests:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
benchmark_insert($pdo, 1000);
benchmark_select($pdo, 100000);
Ergebnisse:
INSERTION
=============================
INC ID: 3
GUID: 2
SPLIT GUID: 3
SELECTION
=============================
INC ID: 5
GUID: 5
SPLIT GUID: 6
quelle
BINARY(16)
Ich denke, wir sind uns beide einig, dass dies die effizienteste Methode zum Speichern einer UUID ist. SollteUNIQUE
ich jedoch in Bezug auf den Index einfach einen regulären Index verwenden? Die Bytes werden mit kryptografisch sicheren RNGs generiert. Soll ich mich also vollständig auf die Zufälligkeit verlassen und auf die Überprüfungen verzichten?innodb_buffer_pool_size
ist 70% des verfügbaren RAM.'Rick James' sagte in akzeptierter Antwort: "Es ist eine Verschwendung, sowohl eine EINZIGARTIGE AUTO_INCREMENT als auch eine EINZIGARTIGE UUID in derselben Tabelle zu haben." Aber dieser Test (ich habe ihn auf meiner Maschine durchgeführt) zeigt verschiedene Fakten.
Zum Beispiel: Mit dem Test (T2) erstelle ich eine Tabelle mit (INT AUTOINCREMENT) PRIMARY und UNIQUE BINARY (16) und einem anderen Feld als Titel, dann füge ich mehr als 1,6 Millionen Zeilen mit sehr guter Leistung ein, aber mit einem anderen Test (T3). Ich habe das gleiche getan, aber das Ergebnis ist langsam, nachdem nur 300.000 Zeilen eingefügt wurden.
Dies ist mein Testergebnis:
Binär (16) UNIQUE mit automatischem Inkrement int_id ist also besser als binäres (16) UNIQUE ohne automatisches Inkrement int_id.
Ich mache den gleichen Test noch einmal und nehme weitere Details auf. Dies ist ein vollständiger Code- und Ergebnisvergleich zwischen (T2) und (T3), wie oben erläutert.
(T2) erstelle tbl2 (mysql):
(T3) erstelle tbl3 (mysql):
Dies ist ein vollständiger Testcode, der 600.000 Datensätze in tbl2 oder tbl3 einfügt (vb.net-Code):
Das Ergebnis für (T2):
Das Ergebnis für (T3):
quelle
innodb_buffer_pool_size
? Woher kommt "Tischgröße"?COMMIT
, nicht vor. Dies kann einige andere Anomalien beseitigen.@rec_id
und@src_id
generiert und auf jede Zeile angewendet werden. Das Drucken einigerINSERT
Aussagen könnte mich befriedigen.t2
auch eine Klippe. Es kann sogar langsamer gehen alst3
; Ich bin mir nicht sicher. Ihr Benchmark befindet sich in einem "Donut-Loch", in demt3
es vorübergehend langsamer ist.