Я уже некоторое время использую UUID в своих системах по разным причинам - от регистрации до отложенной корреляции. Форматы, которые я использовал, изменились, поскольку я стал менее наивным из:
VARCHAR(255)
VARCHAR(36)
CHAR(36)
BINARY(16)
Когда я дошел до последнего BINARY(16)
, я начал сравнивать производительность с базовым целым числом с автоинкрементом. Тест и результаты приведены ниже, но если вы просто хотите , резюме, это указывает на то, что INT AUTOINCREMENT
и BINARY(16) RANDOM
имеют одинаковую производительность данных составляет до 200000 (база данных была предварительно заполненные до испытаний).
Сначала я скептически относился к использованию UUID в качестве первичных ключей, и на самом деле я все еще ими пользуюсь, однако я вижу здесь потенциал для создания гибкой базы данных, которая может использовать оба. В то время как многие люди подчеркивают преимущества обоих, какие недостатки устраняются при использовании обоих типов данных?
PRIMARY INT
UNIQUE BINARY(16)
Вариант использования для этого типа установки будет традиционным первичным ключом для межстоловых отношений с уникальным идентификатором, используемым для межсистемных отношений.
По сути, я пытаюсь обнаружить разницу в эффективности между этими двумя подходами. Помимо используемого четырехкратного дискового пространства, которое может быть незначительным после добавления дополнительных данных, они кажутся мне одинаковыми.
Схема:
-- 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 */;
Вставьте бенчмарк:
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;
}
Выберите ориентир:
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;
}
тесты:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
benchmark_insert($pdo, 1000);
benchmark_select($pdo, 100000);
Полученные результаты:
INSERTION
=============================
INC ID: 3
GUID: 2
SPLIT GUID: 3
SELECTION
=============================
INC ID: 5
GUID: 5
SPLIT GUID: 6
источник
BINARY(16)
Я думаю, что мы оба согласны, что это наиболее эффективный способ хранения UUID, но что касаетсяUNIQUE
индекса, я должен просто использовать обычный индекс? Байты генерируются с использованием криптографически защищенных ГСЧ, поэтому я буду полностью зависеть от случайности и отказаться от проверок?innodb_buffer_pool_size
составляет 70% от доступной оперативной памяти.«Рик Джеймс» сказал в принятом ответе: «Наличие в одной таблице и UNIQUE AUTO_INCREMENT, и UNIQUE UUID - пустая трата времени». Но этот тест (я сделал это на своей машине) показывает разные факты.
Например: с помощью теста (T2) я создаю таблицу с (INT AUTOINCREMENT) PRIMARY и UNIQUE BINARY (16) и другим полем в качестве заголовка, затем я вставляю более 1,6 млн строк с очень хорошей производительностью, но с другим тестированием (T3) Я сделал то же самое, но результат медленный после вставки только 300 000 строк.
Это мой результат тестирования:
Поэтому двоичный (16) UNIQUE с автоматическим приращением int_id лучше двоичного (16) UNIQUE без автоматического приращения int_id.
Я делаю тот же тест снова и записываю больше деталей. это полный код и сравнение результатов между (T2) и (T3), как объяснено выше.
(T2) создать tbl2 (mysql):
(T3) создать tbl3 (mysql):
Это полный код тестирования, он вставляет 600 000 записей в tbl2 или tbl3 (код vb.net):
Результат для (T2):
Результат для (T3):
источник
innodb_buffer_pool_size
? Откуда взялся «размер стола»?COMMIT
, а не до. Это может устранить некоторые другие аномалии.@rec_id
и@src_id
генерируются и применяются к каждой строке. Печать парыINSERT
утверждений может удовлетворить меня.t2
также будет падение скалы. Это может даже идти медленнее, чемt3
; Я не уверен. Ваш эталонный тест находится в «дыре от бублика», гдеt3
он временно медленнее.