# MariaDB desde cero (I): instalación y primeros pasos

*Primera entrega de la serie **[MariaDB desde cero a pro](/search?tag=mariadb-desde-cero)**. Tiempo de lectura estimado: 9 minutos.*

Arranco la tercera serie sobre bases de datos: cinco posts sobre MariaDB, la implementación libre del linaje MySQL que hoy tiene roadmap propio y sigue siendo una opción muy sensata para muchos proyectos.

Si dudas entre MariaDB y MySQL como tecnologías, el post [MySQL y MariaDB, MariaDB y MySQL](/post/mysql-y-mariadb-mariadb-y-mysql) es un buen punto de partida. Aquí doy por supuesto que ya te has decidido por MariaDB o que estás en un entorno donde lo tienes delante.

Esta serie va en paralelo a las de [PostgreSQL](/search?tag=postgresql-desde-cero) y [ClickHouse](/search?tag=clickhouse-desde-cero). Cuando una idea aparece también en las otras, la enlazo.

## Por qué MariaDB hoy

MariaDB nació como *fork* de MySQL cuando Oracle compró Sun en 2009. Durante años fue un *drop-in replacement*, pero desde MySQL 8 y MariaDB 10.5 se han ido separando lo suficiente como para que conviene saber en cuál estás.

Razones por las que MariaDB sigue siendo una buena elección:

- **GPL pura** y desarrollo 100% abierto (MariaDB Foundation).
- Varios **storage engines** integrados: InnoDB, Aria, MyRocks, ColumnStore, Spider.
- **Compatibilidad Oracle parcial**: modo PL/SQL y sintaxis de secuencias desde 10.3.
- Funciones modernas de SQL: CTEs, window functions, análisis estadístico.
- **Galera Cluster** integrado para HA síncrona multi-master.
- `mariabackup`, compatible con snapshots en caliente.

Para un sistema transaccional de tamaño mediano, MariaDB es una elección sin drama.

## Instalación

### Debian/Ubuntu

El repositorio oficial con paquetes actualizados:

```bash
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash

sudo apt update
sudo apt install -y mariadb-server mariadb-client
sudo systemctl enable --now mariadb
sudo mariadb-secure-installation
```

`mariadb-secure-installation` hace lo típico: establece contraseña de root, elimina usuarios anónimos, desactiva login remoto de root, etc. Úsalo.

### Docker

```bash
docker run -d \
  --name mdb \
  -e MARIADB_ROOT_PASSWORD=secret \
  -p 3306:3306 \
  -v mdbdata:/var/lib/mysql \
  mariadb:11.4
```

Puerto 3306, el clásico. El directorio de datos mantiene el nombre histórico `/var/lib/mysql/` por compatibilidad.

## El cliente `mariadb`

Antiguamente `mysql`, hoy `mariadb` (el binario `mysql` sigue siendo un alias en la mayoría de paquetes). Acepta prácticamente las mismas opciones y meta-comandos.

```bash
mariadb -u root -p
```

Comandos útiles dentro del cliente:

```
\h             -- ayuda
SHOW DATABASES;
USE nombre;
SHOW TABLES;
SHOW CREATE TABLE tabla\G
DESCRIBE tabla;
SHOW PROCESSLIST;
STATUS;
\e             -- abre $EDITOR
\q             -- salir
```

`\G` en lugar de `;` muestra la salida en formato vertical, muy útil para filas anchas. Equivale al `\x` de psql.

Mi `~/.my.cnf` básico (modo cliente):

```ini
[client]
user=javier
password=...
host=localhost

[mariadb]
prompt="\\u@\\h [\\d]> "
auto-rehash
```

`prompt` muestra usuario, host y base de datos activa. `auto-rehash` habilita autocompletado de tablas y columnas.

## Autenticación por socket y usuarios

En Debian/Ubuntu modernos, el usuario `root` de MariaDB se autentica por **Unix socket** por defecto: si eres root del sistema, entras sin contraseña con `sudo mariadb`. Es seguro y cómodo.

Para crear usuarios:

```sql
-- Usuario local con contraseña
CREATE USER 'app'@'localhost' IDENTIFIED BY 'xxx';

-- Usuario accesible desde una red interna
CREATE USER 'app'@'10.0.%.%' IDENTIFIED BY 'xxx';

-- Permisos completos sobre una BD
GRANT ALL PRIVILEGES ON blog.* TO 'app'@'localhost';

-- Permisos granulares, como debería ser en producción
GRANT SELECT, INSERT, UPDATE, DELETE ON blog.* TO 'app'@'localhost';

FLUSH PRIVILEGES;
```

Un detalle clave que confunde al que viene de PostgreSQL: en MariaDB, el usuario es **`'usuario'@'host'`**. El mismo nombre desde distintos hosts puede tener distintos permisos. Es peculiar pero muy útil.

Para roles (similar a PostgreSQL):

```sql
CREATE ROLE app_read;
GRANT SELECT ON blog.* TO app_read;
GRANT app_read TO 'reporter'@'localhost';
SET DEFAULT ROLE app_read FOR 'reporter'@'localhost';
```

## Tu primera base de datos

```sql
CREATE DATABASE blog
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

USE blog;

CREATE TABLE authors (
  id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name       VARCHAR(200) NOT NULL,
  email      VARCHAR(320) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY ux_authors_email (email)
) ENGINE=InnoDB;

CREATE TABLE posts (
  id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  author_id    BIGINT UNSIGNED NOT NULL,
  title        VARCHAR(300) NOT NULL,
  slug         VARCHAR(300) NOT NULL,
  body         MEDIUMTEXT NOT NULL,
  published_at DATETIME NULL,
  created_at   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY ux_posts_slug (slug),
  KEY ix_posts_author_published (author_id, published_at),
  CONSTRAINT fk_posts_author FOREIGN KEY (author_id) REFERENCES authors(id)
) ENGINE=InnoDB;
```

Piezas que ya introducen el sabor de MariaDB:

- **`utf8mb4`**: el charset que quieres. `utf8` en MySQL/MariaDB es una versión limitada (3 bytes) por razones históricas. Con `utf8mb4` soportas emoji y todo Unicode.
- **`ENGINE=InnoDB`**: el storage engine transaccional. Lo desgranamos en la [entrega II](/post/mariadb-desde-cero-ii-storage-engines-tipos-y-restricciones).
- **`UNSIGNED`**: enteros sin signo. Ahorras la mitad del rango negativo cuando no lo necesitas.
- **`AUTO_INCREMENT`**: la secuencia clásica. Desde 10.3 también están las `SEQUENCE` al estilo Oracle/PostgreSQL.
- **`ON UPDATE CURRENT_TIMESTAMP`**: magia de MariaDB que mantiene `updated_at` solo.

## Primeros INSERT y SELECT

```sql
INSERT INTO authors (name, email)
VALUES ('Javier', 'javier@example.com');

SELECT LAST_INSERT_ID();
-- Devuelve el id generado

INSERT INTO posts (author_id, title, slug, body, published_at)
VALUES
  (1, 'Hola mundo', 'hola-mundo', 'Primer post', NOW()),
  (1, 'En borrador', 'borrador', 'Aún no publicado', NULL);

SELECT id, title, published_at IS NOT NULL AS publicado
FROM posts
ORDER BY created_at DESC;
```

A diferencia de PostgreSQL, MariaDB no tiene `RETURNING` universal (aunque sí soporta un subset limitado: `INSERT ... RETURNING` desde 10.5). El patrón habitual para INSERT es `LAST_INSERT_ID()`.

## Transacciones

MariaDB con InnoDB soporta transacciones ACID:

```sql
START TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

COMMIT;
-- o ROLLBACK;
```

Savepoints:

```sql
START TRANSACTION;
INSERT INTO ... ;
SAVEPOINT sp1;
UPDATE ... ;   -- podría fallar
ROLLBACK TO sp1;
COMMIT;
```

Nivel de aislamiento por defecto: `REPEATABLE READ` (distinto a PostgreSQL, que va con `READ COMMITTED`). Para una explicación larga de las diferencias, merece un post entero.

## Configuración: los ficheros que importan

En Debian/Ubuntu, la configuración está dispersa en varios ficheros que se unen por el método include:

- `/etc/mysql/mariadb.cnf` — fichero raíz.
- `/etc/mysql/conf.d/` — configuración compartida con clientes.
- `/etc/mysql/mariadb.conf.d/50-server.cnf` — la config del servidor.

Los ajustes más habituales van en `50-server.cnf` o en tus propios `.cnf` en `mariadb.conf.d/`.

Parámetros para mirar cualquier valor:

```sql
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW STATUS LIKE 'Threads_connected';
```

Y para ver estado global:

```sql
SHOW GLOBAL STATUS;
```

## Log slow query

Lo primero que activo en un servidor que dudo:

```sql
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;          -- segundos
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
```

Y en producción, persiste en el `.cnf`:

```ini
[mariadb]
slow_query_log = 1
long_query_time = 1
slow_query_log_file = /var/log/mysql/slow.log
log_queries_not_using_indexes = 1
```

Con `mariadb-dumpslow` (antes `mysqldumpslow`) agregas el fichero para ver las consultas lentas agrupadas. Es la primera parada cuando algo va mal. Volveremos a ello en la [entrega IV](/post/mariadb-desde-cero-iv-indices-explain-y-tuning).

## Character set y collation

Por omisión, usa `utf8mb4`. En versiones modernas de MariaDB ya es el default del servidor, pero conviene verificar:

```sql
SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
```

Si heredas un sistema viejo con `latin1` o `utf8` (3 bytes), considéralo deuda técnica y planea una migración. Es un dolor de cabeza recurrente.

## Por dónde seguir

- **[II: storage engines, tipos y restricciones](/post/mariadb-desde-cero-ii-storage-engines-tipos-y-restricciones)** — InnoDB, Aria, MyRocks, tipos numéricos, JSON, foreign keys.
- **[III: consultas, CTEs y window functions](/post/mariadb-desde-cero-iii-consultas-ctes-y-window-functions)** — el SQL moderno que MariaDB soporta desde 10.2.
- **[IV: índices, EXPLAIN y tuning](/post/mariadb-desde-cero-iv-indices-explain-y-tuning)** — rendimiento en serio.
- **[V: replicación, Galera y producción](/post/mariadb-desde-cero-v-replicacion-galera-y-produccion)** — replicación async, cluster síncrono y operación.

Como lectura complementaria: [MySQL y MariaDB, MariaDB y MySQL](/post/mysql-y-mariadb-mariadb-y-mysql) para tener clara la comparativa con MySQL.
