database.rst 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. .. _database:
  2. ===================
  3. Using In a Database
  4. ===================
  5. .. tip::
  6. `ramsey/uuid-doctrine`_ allows the use of ramsey/uuid as a `Doctrine field
  7. type`_. If you use Doctrine, it's a great option for working with UUIDs and
  8. databases.
  9. There are several strategies to consider when working with UUIDs in a database.
  10. Among these are whether to store the string representation or bytes and whether
  11. the UUID column should be treated as a primary key. We'll discuss a few of these
  12. approaches here, but the final decision on how to use UUIDs in a database is up
  13. to you since your needs will be different from those of others.
  14. .. note::
  15. All database code examples in this section assume the use of `MariaDB`_ and
  16. `PHP Data Objects (PDO)`_. If using a different database engine or
  17. connection library, your code will differ, but the general concepts should
  18. remain the same.
  19. .. _database.string:
  20. Storing As a String
  21. ###################
  22. Perhaps the easiest way to store a UUID to a database is to create a ``char(36)``
  23. column and store the UUID as a string. When stored as a string, UUIDs require
  24. no special treatment in SQL statements or when displaying them.
  25. The primary drawback is the size. At 36 characters, UUIDs can take up a lot of
  26. space, and when handling a lot of data, this can add up.
  27. .. code-block:: sql
  28. :caption: Create a table with a column for UUIDs
  29. :name: database.uuid-column-example
  30. CREATE TABLE `notes` (
  31. `uuid` char(36) NOT NULL,
  32. `notes` text NOT NULL
  33. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  34. Using this database table, we can store the string UUID using code similar to
  35. this (assume some of the variables in this example have been set beforehand):
  36. .. code-block:: php
  37. :caption: Store a string UUID to the uuid column
  38. :name: database.uuid-column-store-example
  39. use Ramsey\Uuid\Uuid;
  40. $uuid = Uuid::uuid4();
  41. $dbh = new PDO($dsn, $username, $password);
  42. $sth = $dbh->prepare('
  43. INSERT INTO notes (
  44. uuid,
  45. notes
  46. ) VALUES (
  47. :uuid,
  48. :notes
  49. )
  50. ');
  51. $sth->execute([
  52. ':uuid' => $uuid->toString(),
  53. ':notes' => $notes,
  54. ]);
  55. .. _database.bytes:
  56. Storing As Bytes
  57. ################
  58. In :ref:`the previous example <database.uuid-column-store-example>`, we saw how
  59. to store the string representation of a UUID to a ``char(36)`` column. As
  60. discussed, the primary drawback is the size. However, if we store the UUID in
  61. byte form, we only need a ``char(16)`` column, saving over half the space.
  62. The primary drawback with this approach is ease-of-use. Since the UUID bytes are
  63. stored in the database, querying and selecting data becomes more difficult.
  64. .. code-block:: sql
  65. :caption: Create a table with a column for UUID bytes
  66. :name: database.uuid-bytes-example
  67. CREATE TABLE `notes` (
  68. `uuid` char(16) NOT NULL,
  69. `notes` text NOT NULL
  70. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  71. Using this database table, we can store the UUID bytes using code similar to
  72. this (again, assume some of the variables in this example have been set
  73. beforehand):
  74. .. code-block:: php
  75. :caption: Store UUID bytes to the uuid column
  76. :name: database.uuid-bytes-store-example
  77. $sth->execute([
  78. ':uuid' => $uuid->getBytes(),
  79. ':notes' => $notes,
  80. ]);
  81. Now, when we ``SELECT`` the records from the database, we will need to convert
  82. the ``notes.uuid`` column to a ramsey/uuid object, so that we are able to use
  83. it.
  84. .. code-block:: php
  85. :caption: Covert database UUID bytes to UuidInterface instance
  86. :name: database.uuid-bytes-convert-example
  87. use Ramsey\Uuid\Uuid;
  88. $uuid = Uuid::uuid4();
  89. $dbh = new PDO($dsn, $username, $password);
  90. $sth = $dbh->prepare('SELECT uuid, notes FROM notes');
  91. $sth->execute();
  92. foreach ($sth->fetchAll() as $record) {
  93. $uuid = Uuid::fromBytes($record['uuid']);
  94. printf(
  95. "UUID: %s\nNotes: %s\n\n",
  96. $uuid->toString(),
  97. $record['notes']
  98. );
  99. }
  100. We'll also need to query the database using the bytes.
  101. .. code-block:: php
  102. :caption: Look-up the record from the database, using the UUID bytes
  103. :name: database.uuid-bytes-select-example
  104. use Ramsey\Uuid\Uuid;
  105. $uuid = Uuid::fromString('278198d3-fa96-4833-abab-82f9e67f4712');
  106. $dbh = new PDO($dsn, $username, $password);
  107. $sth = $dbh->prepare('
  108. SELECT uuid, notes
  109. FROM notes
  110. WHERE uuid = :uuid
  111. ');
  112. $sth->execute([
  113. ':uuid' => $uuid->getBytes(),
  114. ]);
  115. $record = $sth->fetch();
  116. if ($record) {
  117. $uuid = Uuid::fromBytes($record['uuid']);
  118. printf(
  119. "UUID: %s\nNotes: %s\n\n",
  120. $uuid->toString(),
  121. $record['notes']
  122. );
  123. }
  124. .. _database.pk:
  125. Using As a Primary Key
  126. ######################
  127. In the previous examples, we didn't use the UUID as a primary key, but it's
  128. logical to use the ``notes.uuid`` field as a primary key. There's nothing wrong
  129. with this approach, but there are a couple of points to consider:
  130. * InnoDB stores data in the primary key order
  131. * All the secondary keys also contain the primary key (in InnoDB)
  132. We'll deal with the first point in the section, :ref:`database.order`. For the
  133. second point, if you are using the string version of the UUID (i.e.,
  134. ``char(36)``), then not only will the primary key be large and take up a lot of
  135. space, but every secondary key that uses that primary key will also be much
  136. larger.
  137. For this reason, if you choose to use UUIDs as primary keys, it might be worth
  138. the drawbacks to use UUID bytes (i.e., ``char(16)``) instead of the string
  139. representation (see :ref:`database.bytes`).
  140. .. hint::
  141. If not using InnoDB with MySQL or MariaDB, consult your database engine
  142. documentation to find whether it also has similar properties that will
  143. factor into your use of UUIDs.
  144. .. _database.uk:
  145. Using As a Unique Key
  146. #####################
  147. Instead of :ref:`using UUIDs as a primary key <database.pk>`, you may choose to
  148. use an ``AUTO_INCREMENT`` column with the ``int unsigned`` data type as a
  149. primary key, while using a ``char(36)`` for UUIDs and setting a ``UNIQUE KEY``
  150. on this column. This will aid in lookups while helping keep your secondary keys
  151. small.
  152. .. code-block:: sql
  153. :caption: Use an auto-incrementing column as primary key, with UUID as a unique key
  154. :name: database.id-auto-increment-uuid-unique-key
  155. CREATE TABLE `notes` (
  156. `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  157. `uuid` char(36) NOT NULL,
  158. `notes` text NOT NULL,
  159. PRIMARY KEY (`id`),
  160. UNIQUE KEY `notes_uuid_uk` (`uuid`)
  161. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  162. .. _database.order:
  163. Insertion Order and Sorting
  164. ###########################
  165. UUID versions 1, 2, 3, 4, and 5 are not *monotonically increasing*. If using
  166. these versions as primary keys, the inserts will be random, and the data will be
  167. scattered on disk (for InnoDB). Over time, as the database size grows, lookups
  168. will become slower and slower.
  169. .. tip::
  170. See Percona's "`Storing UUID Values in MySQL`_" post, for more details on
  171. the performance of UUIDs as primary keys.
  172. To minimize these problems, two solutions have been devised:
  173. 1. :ref:`rfc4122.version6` UUIDs
  174. 2. :ref:`rfc4122.version7` UUIDs
  175. .. note::
  176. We previously recommended the use of the :ref:`timestamp-first COMB
  177. <customize.timestamp-first-comb-codec>` or :ref:`ordered-time
  178. <customize.ordered-time-codec>` codecs to solve these problems. However,
  179. UUID versions 6 and 7 were defined to provide these solutions in a
  180. standardized way.
  181. .. _ramsey/uuid-doctrine: https://github.com/ramsey/uuid-doctrine
  182. .. _Doctrine field type: https://www.doctrine-project.org/projects/doctrine-dbal/en/2.10/reference/types.html
  183. .. _MariaDB: https://mariadb.org
  184. .. _PHP Data Objects (PDO): https://www.php.net/pdo
  185. .. _Storing UUID Values in MySQL: https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/