Der Autor wählte die Kooperation Open Sourcing Mental Illness, um eine Spende im Rahmen des Programms Write for DOnations zu erhalten.
Eine MySQL-Transaktion ist eine Gruppe von logisch zusammenhängenden SQL-Befehlen, die in der Datenbank als eine einzige Einheit ausgeführt werden. Transaktionen werden verwendet, um die ACID (Atomicity, Consistency, Isolation und Durability)-Konformität in einer Anwendung durchzusetzen. Dabei handelt es sich um eine Reihe von Standards, die die Zuverlässigkeit der Verarbeitungsvorgänge in einer Datenbank regeln.
Atomarität (Atomicity) gewährleistet den Erfolg zusammengehöriger Transaktionen oder einen vollständigen Ausfall, wenn ein Fehler auftritt. Konsistenz (Consistency) garantiert die Gültigkeit der an die Datenbank übermittelten Daten gemäß der definierten Geschäftslogik. Isolierung (Isolation) ist die korrekte Ausführung von gleichzeitigen Transaktionen, wobei sichergestellt wird, dass die Auswirkungen verschiedener Clients, die sich mit einer Datenbank verbinden, sich nicht gegenseitig beeinflussen. Dauerhaftigkeit (Durability) stellt sicher, dass logisch zusammenhängende Transaktionen dauerhaft in der Datenbank verbleiben.
Über eine Transaktion ausgegebene SQL-Anweisungen sollten entweder erfolgreich sein oder ganz fehlschlagen. Wenn eine der Abfragen fehlschlägt, macht MySQL die Änderungen rückgängig und sie werden niemals in die Datenbank übertragen.
Ein gutes Beispiel zum Verständnis der Funktionsweise von MySQL-Transaktionen ist eine E-Commerce-Website. Wenn ein Kunde eine Bestellung aufgibt, fügt die Anwendung je nach Geschäftslogik Datensätze in mehrere Tabellen ein, wie beispielsweise: orders
und orders_products
. Mehrtabellen-Datensätze, die sich auf eine einzelne Bestellung beziehen, müssen als eine einzige logische Einheit atomar an die Datenbank gesendet werden.
Ein weiterer Anwendungsfall ist in einer Bankanwendung. Wenn ein Kunde Geld überweist, werden einige Transaktionen an die Datenbank gesendet. Das Konto des Senders wird belastet und dem Konto der Partei des Empfängers gutgeschrieben. Die beiden Transaktionen müssen gleichzeitig bestätigt werden. Wenn eine von ihnen fehlschlägt, kehrt die Datenbank in ihren ursprünglichen Zustand zurück, und es sollten keine Änderungen auf der Festplatte gespeichert werden.
In diesem Tutorial werden Sie die PDO-PHP-Erweiterung verwenden, die eine Schnittstelle für die Arbeit mit Datenbanken in PHP bietet, um MySQL-Transaktionen auf einem Ubuntu 18.04-Server durchzuführen.
Bevor Sie beginnen, benötigen Sie Folgendes:
Als Erstes erstellen Sie eine Beispieldatenbank und fügen einige Tabellen hinzu, bevor Sie mit MySQL-Transaktionen zu arbeiten beginnen. Melden Sie sich zunächst als root bei Ihrem MySQL-Server an:
- sudo mysql -u root -p
Wenn Sie dazu aufgefordert werden, geben Sie Ihr MySQL-Root-Passwort ein und drücken Sie ENTER
, um fortzufahren. Erstellen Sie dann eine Datenbank. Für die Zwecke dieses Tutorials werden wir die Datenbank sample_store
nennen:
- CREATE DATABASE sample_store;
Sie sehen die folgende Ausgabe:
OutputQuery OK, 1 row affected (0.00 sec)
Erstellen Sie einen Benutzer namens sample_user
für Ihre Datenbank. Denken Sie daran, PASSWORD
durch einen starken Wert zu ersetzen:
- CREATE USER 'sample_user'@'localhost' IDENTIFIED BY 'PASSWORD';
Erteilen Sie Ihrem Benutzer uneingeschränkte Berechtigungen für die Datenbank sample_store
:
- GRANT ALL PRIVILEGES ON sample_store.* TO 'sample_user'@'localhost';
Laden Sie abschließend die MySQL-Berechtigungen neu:
- FLUSH PRIVILEGES;
Sobald Sie Ihren Benutzer angelegt haben, sehen Sie die folgende Ausgabe:
OutputQuery OK, 0 rows affected (0.01 sec)
. . .
Mit der vorhandenen Datenbank und dem Benutzer können Sie jetzt mehrere Tabellen erstellen, um zu demonstrieren, wie MySQL-Transaktionen funktionieren.
Melden Sie sich vom MySQL-Server ab:
- QUIT;
Sobald das System Sie abmeldet, sehen Sie die folgende Ausgabe:
OutputBye.
Melden Sie sich dann mit den Anmeldeinformationen des Benutzers sample_user
an, den Sie gerade angelegt haben:
- sudo mysql -u sample_user -p
Geben Sie das Passwort für den sample_user
ein und drücken Sie ENTER
, um fortzufahren.
Wechseln Sie zu sample_store
, um sie zur aktuell ausgewählten Datenbank zu machen:
- USE sample_store;
Sobald die Datenbank ausgewählt ist, sehen Sie die folgende Ausgabe:
OutputDatabase Changed.
Erstellen Sie als Nächstes eine Tabelle products
:
- CREATE TABLE products (product_id BIGINT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(50), price DOUBLE) ENGINE = InnoDB;
Mit diesem Befehl wird eine Tabelle products
mit einem Feld namens product_id
erstellt. Verwenden Sie einen Datentyp BIGINT
, der einen großen Wert von bis zu 2^63-1 aufnehmen kann. Sie verwenden dasselbe Feld als PRIMARY KEY
zur eindeutigen Identifizierung von Produkten. Das Schlüsselwort AUTO_INCREMENT
weist MySQL an, den nächsten numerischen Wert zu erzeugen, wenn neue Produkte eingefügt werden.
Das Feld product_name
ist vom Typ VARCHAR
und kann bis zu 50
Buchstaben oder Zahlen enthalten. Für den Produktpreis (price
) verwenden Sie einen Datentyp DOUBLE
, um Fließkommaformate in Preisen mit Dezimalzahlen zu berücksichtigen.
Zuletzt verwenden Sie die InnoDB
als ENGINE
, weil sie im Gegensatz zu anderen Speicher-Engines wie MyISAM
MySQL-Transaktionen komfortabel unterstützt.
Sobald Sie Ihre Tabelle products
erstellt haben, erhalten Sie die folgende Ausgabe:
OutputQuery OK, 0 rows affected (0.02 sec)
Fügen Sie als Nächstes einige Artikel zur Tabelle products
hinzu, indem Sie die folgenden Befehle ausführen:
- INSERT INTO products(product_name, price) VALUES ('WINTER COAT','25.50');
- INSERT INTO products(product_name, price) VALUES ('EMBROIDERED SHIRT','13.90');
- INSERT INTO products(product_name, price) VALUES ('FASHION SHOES','45.30');
- INSERT INTO products(product_name, price) VALUES ('PROXIMA TROUSER','39.95');
Nach jeder Operation INSERT
sehen Sie eine Ausgabe ähnlich der folgenden:
OutputQuery OK, 1 row affected (0.02 sec)
. . .
Überprüfen Sie dann, ob die Daten der Tabelle products hinzugefügt wurden:
- SELECT * FROM products;
Sie sehen eine Liste mit den vier von Ihnen eingefügten Produkten:
Output+------------+-------------------+-------+
| product_id | product_name | price |
+------------+-------------------+-------+
| 1 | WINTER COAT | 25.5 |
| 2 | EMBROIDERED SHIRT | 13.9 |
| 3 | FASHION SHOES | 45.3 |
| 4 | PROXIMA TROUSER | 39.95 |
+------------+-------------------+-------+
4 rows in set (0.01 sec)
Als Nächstes erstellen Sie eine Tabelle customers
, die grundlegende Informationen über Kunden enthält:
- CREATE TABLE customers (customer_id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_name VARCHAR(50) ) ENGINE = InnoDB;
Wie in der Tabelle products
verwenden Sie für die customer_id
den Datentyp BIGINT
, wodurch sichergestellt wird, dass die Tabelle viele Kunden mit bis zu 2^63-1 Datensätzen unterstützt. Das Schlüsselwort AUTO_INCREMENT
erhöht den Wert der Spalten, sobald Sie einen neuen Kunden einfügen.
Da die Spalte customer_name
alphanumerische Werte akzeptiert, verwenden Sie den Datentyp VARCHAR
mit einer Beschränkung von 50
Zeichen. Auch hier verwenden Sie die InnoDB
Storage ENGINE
zur Unterstützung von Transaktionen.
Nachdem Sie den vorherigen Befehl zum Anlegen der Tabelle customers
ausgeführt haben, sehen Sie die folgende Ausgabe:
OutputQuery OK, 0 rows affected (0.02 sec)
Sie fügen der Tabelle drei Musterkunden hinzu. Führen Sie die folgenden Befehle aus:
- INSERT INTO customers(customer_name) VALUES ('JOHN DOE');
- INSERT INTO customers(customer_name) VALUES ('ROE MARY');
- INSERT INTO customers(customer_name) VALUES ('DOE JANE');
Sobald die Kunden hinzugefügt wurden, sehen Sie eine Ausgabe ähnlich der folgenden:
OutputQuery OK, 1 row affected (0.02 sec)
. . .
Überprüfen Sie dann die Daten in der Tabelle customers
:
- SELECT * FROM customers;
Sie sehen eine Liste mit den drei Kunden:
Output+-------------+---------------+
| customer_id | customer_name |
+-------------+---------------+
| 1 | JOHN DOE |
| 2 | ROE MARY |
| 3 | DOE JANE |
+-------------+---------------+
3 rows in set (0.00 sec)
Anschließend erstellen Sie eine Tabelle orders
, um die von verschiedenen Kunden aufgegebenen Bestellungen aufzuzeichnen. Führen Sie den folgenden Befehl aus, um die Tabelle orders
zu erstellen:
- CREATE TABLE orders (order_id BIGINT AUTO_INCREMENT PRIMARY KEY, order_date DATETIME, customer_id BIGINT, order_total DOUBLE) ENGINE = InnoDB;
Verwenden Sie die Spalte order_id
als PRIMARY KEY
. Der Datentyp BIGINT
ermöglicht Ihnen die Aufnahme von bis zu 2^63-1 Bestellungen und wird nach jeder Bestellungseinfügung automatisch erhöht. Das Feld order_date
enthält das tatsächliche Datum und die tatsächliche Uhrzeit der Bestellung, daher verwenden Sie den Datentyp DATETIME
. Die customer_id
bezieht sich auf die zuvor von Ihnen erstellte Tabelle customers
.
Sie sehen die folgende Ausgabe:
OutputQuery OK, 0 rows affected (0.02 sec)
Da die Bestellung eines einzelnen Kunden mehrere Artikel enthalten kann, müssen Sie eine Tabelle orders_products
erstellen, um diese Informationen zu speichern.
Führen Sie den folgenden Befehl aus, um die Tabelle orders_products
zu erstellen:
- CREATE TABLE orders_products (ref_id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT, product_id BIGINT, price DOUBLE, quantity BIGINT) ENGINE = InnoDB;
Verwenden Sie die ref_id
als PRIMARY KEY
, der nach jeder Einfügung eines Datensatzes automatisch erhöht wird. Die order_id
und product_id
beziehen sich auf die Tabelle orders
und products
. Die Spalte price
ist vom Datentyp DOUBLE
, um gleitende Werte aufzunehmen.
Die Speicher-Engine InnoDB
muss mit den zuvor erstellten Tabellen übereinstimmen, da sich die Bestellung eines einzelnen Kunden unter Verwendung von Transaktionen gleichzeitig auf mehrere Tabellen auswirkt.
Ihre Ausgabe bestätigt die Erstellung der Tabelle:
OutputQuery OK, 0 rows affected (0.02 sec)
Sie werden vorerst keine Daten zu den Tabellen orders
und orders_products
hinzufügen. Dies geschieht jedoch später mit einem PHP-Skript, das MySQL-Transaktionen implementiert.
Melden Sie sich vom MySQL-Server ab:
- QUIT;
Ihr Datenbankschema ist nun vollständig und Sie haben es mit einigen Datensätzen gefüllt. Nun werden Sie eine PHP-Klasse für die Handhabung von Datenbankverbindungen und MySQL-Transaktionen erstellen.
In diesem Schritt erstellen Sie eine PHP-Klasse, die PDO (PHP Data Objects) zur Abwicklung von MySQL-Transaktionen verwendet. Die Klasse wird eine Verbindung zu Ihrer MySQL-Datenbank herstellen und Daten atomar in die Datenbank einfügen.
Speichern Sie die Klassendatei im Stammverzeichnis Ihres Apache-Webservers. Erstellen Sie dazu mit Ihrem Texteditor eine Datei DBTransaction.php
:
- sudo nano /var/www/html/DBTransaction.php
Fügen Sie dann den folgenden Code in die Datei ein: Ersetzen Sie PASSWORD
mit dem in Schritt 1 erstellten Wert:
<?php
class DBTransaction
{
protected $pdo;
public $last_insert_id;
public function __construct()
{
define('DB_NAME', 'sample_store');
define('DB_USER', 'sample_user');
define('DB_PASSWORD', 'PASSWORD');
define('DB_HOST', 'localhost');
$this->pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASSWORD);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
Zu Beginn der Klasse DBTransaction
verwendet das PDO die Konstanten (DB_HOST
, DB_NAME
, DB_USER
und DB_PASSWORD
) zur Initialisierung und Verbindung mit der Datenbank, die Sie in Schritt 1 erstellt haben.
Anmerkung: Da wir hier MySQL-Transaktionen in kleinem Maßstab demonstrieren, haben wir die Datenbankvariable in der Klasse DBTransaction
deklariert. In einem großen Produktionsprojekt würden Sie normalerweise eine separate Konfigurationsdatei erstellen und die Datenbankkonstanten aus dieser Datei mit Hilfe einer PHP-Anweisung require_once
laden.
Als Nächstes legen Sie zwei Attribute für die Klasse PDO fest:
ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION
: Dieses Attribut weist PDO an, eine Ausnahme auszulösen, wenn ein Fehler auftritt. Solche Fehler können zum Debugging protokolliert werden.ATTR_EMULATE_PREPARES, false
: Diese Option deaktiviert die Emulation vorbereiteter Anweisungen und erlaubt der MySQL-Datenbank-Engine, die Anweisungen selbst vorzubereiten.Fügen Sie nun den folgenden Code in Ihre Datei ein, um die Methoden für Ihre Klasse zu erstellen:
. . .
public function startTransaction()
{
$this->pdo->beginTransaction();
}
public function insertTransaction($sql, $data)
{
$stmt = $this->pdo->prepare($sql);
$stmt->execute($data);
$this->last_insert_id = $this->pdo->lastInsertId();
}
public function submitTransaction()
{
try {
$this->pdo->commit();
} catch(PDOException $e) {
$this->pdo->rollBack();
return false;
}
return true;
}
}
Speichern und schließen Sie die Datei, indem Sie STRG
+ X
, Y
, dann ENTER
drücken.
Um mit MySQL-Transaktionen zu arbeiten, erstellen Sie in der Klasse DBTransaction
drei Hauptmethoden: startTransaction
, insertTransaction
und submitTransaction
.
startTransaction
: Diese Methode weist PDO an, eine Transaktion zu starten, und schaltet Auto-Commit aus, bis ein Commit-Befehl ausgegeben wird.
insertTransaction
: Diese Methode benötigt zwei Argumente. Die Variable $sql
enthält die auszuführende SQL-Anweisung, während die Variable $data
ein Array mit Daten ist, die an die SQL-Anweisung gebunden werden sollen, da Sie vorbereitete Anweisungen verwenden. Die Daten werden als Array an die Methode insertTransaction
übergeben.
submitTransaction
: Diese Methode bindet die Änderungen dauerhaft an die Datenbank, indem Sie einen Befehl commit()
ausgibt. Wenn jedoch ein Fehler auftritt und die Transaktionen ein Problem haben, ruft die Methode die Methode rollBack()
auf, um die Datenbank in ihren ursprünglichen Zustand zurückzuversetzen, falls eine PDO-Ausnahme ausgelöst wird.
Ihre Klasse DBTransaction
initialisiert eine Transaktion, bereitet die verschiedenen auszuführenden SQL-Befehle vor, und bindet schließlich die Änderungen atomar in die Datenbank ein, wenn es keine Probleme gibt. Andernfalls wird die Transaktion rückgängig gemacht. Darüber hinaus ermöglicht Ihnen die Klasse, den Datensatz order_id
abzurufen, den Sie gerade durch Zugriff auf die öffentliche Eigenschaft last_insert_id
erstellt haben.
Die Klasse DBTransaction
ist nun bereit, von jedem PHP-Code, den Sie als Nächstes erstellen, aufgerufen und verwendet zu werden.
Sie erstellen ein PHP-Skript, das die Klasse DBTransaction
implementiert und eine Gruppe von SQL-Befehlen in die MySQL-Datenbank sendet. Sie imitieren den Workflow einer Kundenbestellung in einem Online-Einkaufswagen.
Diese SQL-Abfragen wirken sich auf die Tabellen orders
und orders_products
aus. Ihre Klasse DBTransaction
sollte nur Änderungen in der Datenbank zulassen, wenn alle Abfragen fehlerfrei ausgeführt werden. Andernfalls erhalten Sie einen Fehler zurück, und alle Änderungsversuche werden rückgängig gemacht.
Sie erstellen eine einzelne Bestellung für den mit customer_id 1
identifizierten Kunden JOHN DOE
. Die Bestellung des Kunden enthält drei verschiedene Artikel mit unterschiedlichen Mengen aus der Tabelle products
. Ihr PHP-Skript nimmt die Bestelldaten des Kunden und übergibt sie an die Klasse DBTransaction
.
Erstellen Sie die Datei orders.php
:
- sudo nano /var/www/html/orders.php
Fügen Sie dann den folgenden Code in die Datei ein:
<?php
require("DBTransaction.php");
$db_host = "database_host";
$db_name = "database_name";
$db_user = "database_user";
$db_password = "PASSWORD";
$customer_id = 2;
$products[] = [
'product_id' => 1,
'price' => 25.50,
'quantity' => 1
];
$products[] = [
'product_id' => 2,
'price' => 13.90,
'quantity' => 3
];
$products[] = [
'product_id' => 3,
'price' => 45.30,
'quantity' => 2
];
$transaction = new DBTransaction($db_host, $db_user, $db_password, $db_name);
Sie haben ein PHP-Skript erstellt, das eine Instanz der Klasse DBTransaction
initialisiert, die Sie in Schritt 2 erstellt haben.
In diesem Skript fügen Sie die Datei DBTransaction.php
ein und initialisieren die Klasse DBTransaction
. Als Nächstes bereiten Sie ein mehrdimensionales Array aller Produkte vor, die der Kunde im Geschäft bestellt. Sie rufen auch die Methode startTransaction()
auf, um eine Transaktion zu starten.
Anschließend fügen Sie den folgenden Code hinzu, um Ihr Skript orders.php
zu vervollständigen:
. . .
$order_query = "insert into orders (order_id, customer_id, order_date, order_total) values(:order_id, :customer_id, :order_date, :order_total)";
$product_query = "insert into orders_products (order_id, product_id, price, quantity) values(:order_id, :product_id, :price, :quantity)";
$transaction->insertQuery($order_query, [
'customer_id' => $customer_id,
'order_date' => "2020-01-11",
'order_total' => 157.8
]);
$order_id = $transaction->last_insert_id;
foreach ($products as $product) {
$transaction->insertQuery($product_query, [
'order_id' => $order_id,
'product_id' => $product['product_id'],
'price' => $product['price'],
'quantity' => $product['quantity']
]);
}
$result = $transaction->submit();
if ($result) {
echo "Records successfully submitted";
} else {
echo "There was an error.";
}
Speichern und schließen Sie die Datei, indem Sie STRG
+ X
, Y
, dann ENTER
drücken.
Bereiten Sie den Befehl vor, der über die Methode insertTransaction
in die Tabelle orders eingefügt werden soll. Danach rufen Sie den Wert der öffentlichen Eigenschaft last_insert_id
aus der Klasse DBTransaction
ab und verwenden ihn als $order_id
.
Sobald Sie über eine $order_id
verfügen, verwenden Sie die eindeutige ID, um die von dem Kunden bestellten Artikel in die Tabelle orders_products
einzufügen.
Anschließend rufen Sie die Methode submitTransaction
auf, um die gesamten Bestelldetails des Kunden in die Datenbank zu übertragen, wenn es keine Probleme gibt. Andernfalls macht die Methode submitTransaction
die versuchten Änderungen rückgängig.
Jetzt führen Sie das Skript orders.php
in Ihrem Browser aus. Führen Sie das Folgende aus und ersetzen Sie your-server-IP
mit der öffentlichen IP-Adresse Ihres Servers:
http://your-server-IP/orders.php
Sie erhalten eine Bestätigung, dass die Datensätze erfolgreich übermittelt wurden.
Ihr PHP-Skript funktioniert wie erwartet und die Bestellung zusammen mit den zugehörigen Bestellprodukten wurde atomar in die Datenbank übertragen.
Sie haben die Datei orders.php
in einem Browserfenster ausgeführt. Das Skript hat die Klasse DBTransaction
aufgerufen, die ihrerseits die Details der orders
in die Datenbank übermittelte. Im nächsten Schritt überprüfen Sie, ob die Datensätze in den Bezugstabellen der Datenbank gespeichert wurden.
In diesem Schritt überprüfen Sie, ob die vom Browserfenster aus initiierte Transaktion für die Bestellung des Kunden wie erwartet in die Datenbanktabellen verbucht wurde.
Dazu melden Sie sich erneut bei Ihrer MySQL-Datenbank an:
- sudo mysql -u sample_user -p
Geben Sie das Passwort für den sample_user
ein und drücken Sie ENTER
, um fortzufahren.
Wechseln Sie zur Datenbank sample_store
:
- USE sample_store;
Stellen Sie sicher, dass die Datenbank geändert wird, bevor Sie fortfahren, indem Sie die folgende Ausgabe bestätigen:
OutputDatabase Changed.
Geben Sie dann den folgenden Befehl ein, um Datensätze aus der Tabelle orders
abzurufen:
- SELECT * FROM orders;
Dadurch wird die folgende Ausgabe angezeigt, die die Bestellung des Kunden beschreibt:
Output+----------+---------------------+-------------+-------------+
| order_id | order_date | customer_id | order_total |
+----------+---------------------+-------------+-------------+
| 1 | 2020-01-11 00:00:00 | 2 | 157.8 |
+----------+---------------------+-------------+-------------+
1 row in set (0.00 sec)
Rufen Sie dann die Datensätze aus der Tabelle orders_products
ab:
- SELECT * FROM orders_products;
Sie sehen eine Ausgabe ähnlich der folgenden mit einer Liste von Produkten aus der Bestellung des Kunden:
Output+--------+----------+------------+-------+----------+
| ref_id | order_id | product_id | price | quantity |
+--------+----------+------------+-------+----------+
| 1 | 1 | 1 | 25.5 | 1 |
| 2 | 1 | 2 | 13.9 | 3 |
| 3 | 1 | 3 | 45.3 | 2 |
+--------+----------+------------+-------+----------+
3 rows in set (0.00 sec)
Die Ausgabe bestätigt, dass die Transaktion in der Datenbank gespeichert wurde und Ihre Helferklasse DBTransaction
wie erwartet funktioniert.
In diesem Leitfaden haben Sie die PHP PDO verwendet, um mit MySQL-Transaktionen zu arbeiten. Obwohl dies kein erschöpfender Artikel über die Gestaltung einer E-Commerce-Software ist, hat er ein Beispiel für die Verwendung von MySQL-Transaktionen in Ihren Anwendungen bereitgestellt.
Weitere Informationen über das Modell MySQL ACID finden Sie in dem Leitfaden InnoDB und das ACID-Modell auf der offiziellen MySQL-Website. Besuchen Sie unsere MySQL-Inhaltsseite für weitere verwandte Tutorials, Artikel und Fragen und Antworten.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!