JSON_STORAGE_SIZE
La función JSON_STORAGE_SIZE devuelve el número de bytes que utiliza la representación binaria de un documento JSON en el almacenamiento de MySQL. Cuando trabajas con columnas JSON, es fundamental saber cuánto espacio consumen tus datos para dimensionar correctamente las tablas, identificar documentos sobredimensionados y planificar la capacidad de tu base de datos.
Sintaxis
JSON_STORAGE_SIZE(documento_json)La función recibe una expresión JSON y devuelve un entero que representa el tamaño en bytes de la representación binaria interna del documento. Este tamaño corresponde al espacio que el documento ocupa en disco, no al tamaño del texto JSON que ves al consultarlo.
Es importante entender que MySQL no almacena JSON como texto plano. Internamente lo convierte a un formato binario optimizado para acceso aleatorio. Este formato suele ser ligeramente mayor que el texto JSON equivalente, pero permite extraer valores individuales sin parsear todo el documento.
Comportamiento básico
Veamos cuánto ocupa un objeto JSON sencillo:
SELECT JSON_STORAGE_SIZE('{"nombre": "iPhone 15 Pro", "precio": 1299.99}') AS bytes;| bytes |
|---|
| 52 |
El texto JSON tiene 48 caracteres, pero la representación binaria ocupa 52 bytes. La diferencia se debe a los metadatos que MySQL almacena junto con el contenido (tipo de cada valor, offsets para acceso directo, etc.).
Con documentos más grandes, la proporción de overhead se reduce:
SELECT
JSON_STORAGE_SIZE('{"a": 1}') AS pequenio,
JSON_STORAGE_SIZE('{"nombre": "MacBook Air M3", "precio": 1399.00, "stock": 25, "activo": true}') AS mediano,
JSON_STORAGE_SIZE('{
"producto": "iPhone 15 Pro",
"especificaciones": {
"procesador": "A17 Pro",
"ram": "8GB",
"almacenamiento": "256GB",
"pantalla": 6.1
},
"colores": ["Titanio Natural", "Titanio Azul", "Titanio Blanco", "Titanio Negro"]
}') AS grande;| pequenio | mediano | grande |
|---|---|---|
| 19 | 84 | 253 |
El overhead relativo del formato binario disminuye a medida que el documento crece. Para documentos muy pequeños el binario puede ser más grande que el texto, pero para documentos medianos y grandes la diferencia se diluye.
Caso práctico: monitorizar el tamaño de columnas JSON
En una tabla de productos con atributos JSON variables, es útil saber qué productos tienen los documentos más grandes:
CREATE TABLE productos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(200),
precio DECIMAL(10,2),
atributos JSON
);
INSERT INTO productos (nombre, precio, atributos) VALUES
('iPhone 15 Pro', 1299.99, '{"color": "Titanio", "almacenamiento": "256GB", "chip": "A17 Pro", "pantalla": {"tamanio": 6.1, "tipo": "OLED"}, "camaras": [48, 12, 12]}'),
('Camiseta básica', 24.99, '{"talla": "M", "color": "Azul"}'),
('MacBook Air M3', 1399.00, '{"procesador": "M3", "ram": "16GB", "ssd": "512GB", "pantalla": 15.3, "bateria": "18h", "peso": 1.51, "puertos": ["MagSafe", "USB-C", "USB-C", "Audio"]}'),
('Cable USB-C', 19.99, '{"longitud": "1m"}');SELECT
nombre,
JSON_STORAGE_SIZE(atributos) AS bytes,
ROUND(JSON_STORAGE_SIZE(atributos) / 1024, 2) AS kb
FROM productos
ORDER BY bytes DESC;| nombre | bytes | kb |
|---|---|---|
| MacBook Air M3 | 178 | 0.17 |
| iPhone 15 Pro | 158 | 0.15 |
| Camiseta básica | 37 | 0.04 |
| Cable USB-C | 22 | 0.02 |
Los productos electrónicos con más especificaciones consumen significativamente más espacio que los productos simples. Esta información te ayuda a estimar el crecimiento de la tabla.
Caso práctico: identificar documentos sobredimensionados
En una tabla con miles de registros, puedes identificar los documentos JSON que superan un umbral:
SELECT
id,
nombre,
JSON_STORAGE_SIZE(atributos) AS bytes
FROM productos
WHERE JSON_STORAGE_SIZE(atributos) > 100
ORDER BY JSON_STORAGE_SIZE(atributos) DESC;| id | nombre | bytes |
|---|---|---|
| 3 | MacBook Air M3 | 178 |
| 1 | iPhone 15 Pro | 158 |
También puedes obtener estadísticas generales de la columna:
SELECT
COUNT(*) AS total_productos,
MIN(JSON_STORAGE_SIZE(atributos)) AS min_bytes,
MAX(JSON_STORAGE_SIZE(atributos)) AS max_bytes,
ROUND(AVG(JSON_STORAGE_SIZE(atributos)), 0) AS promedio_bytes,
SUM(JSON_STORAGE_SIZE(atributos)) AS total_bytes,
ROUND(SUM(JSON_STORAGE_SIZE(atributos)) / 1024 / 1024, 2) AS total_mb
FROM productos;| total_productos | min_bytes | max_bytes | promedio_bytes | total_bytes | total_mb |
|---|---|---|---|---|---|
| 4 | 22 | 178 | 99 | 395 | 0.00 |
Con cuatro productos el tamaño total es insignificante, pero imagina una tabla con 500.000 productos donde el promedio sea 500 bytes: estarías usando aproximadamente 238 MB solo para la columna JSON. Este tipo de análisis es fundamental para la planificación de capacidad.
Caso práctico: planificación de capacidad
Supongamos que estás diseñando una tabla para almacenar respuestas de API y necesitas estimar el espacio que consumirá:
CREATE TABLE log_api (
id INT AUTO_INCREMENT PRIMARY KEY,
endpoint VARCHAR(200),
respuesta JSON,
creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO log_api (endpoint, respuesta) VALUES
('/api/pedidos/1001', '{"pedido_id": 1001, "estado": "enviado", "items": [{"producto": "iPhone 15 Pro", "cantidad": 1, "precio": 1299.99}, {"producto": "Funda silicona", "cantidad": 2, "precio": 49.99}], "direccion": {"calle": "Gran Vía 28", "ciudad": "Madrid", "cp": "28013"}}'),
('/api/pedidos/1002', '{"pedido_id": 1002, "estado": "pendiente", "items": [{"producto": "MacBook Air M3", "cantidad": 1, "precio": 1399.00}], "direccion": {"calle": "Diagonal 456", "ciudad": "Barcelona", "cp": "08006"}}');SELECT
endpoint,
JSON_STORAGE_SIZE(respuesta) AS bytes_almacenados,
LENGTH(respuesta) AS bytes_texto
FROM log_api;| endpoint | bytes_almacenados | bytes_texto |
|---|---|---|
| /api/pedidos/1001 | 320 | 299 |
| /api/pedidos/1002 | 218 | 201 |
La comparación entre JSON_STORAGE_SIZE (formato binario) y LENGTH (texto) muestra el overhead del formato binario, que suele ser entre un 5% y un 15% mayor. Si esperas recibir 10.000 peticiones al día con un tamaño promedio de 300 bytes, puedes estimar que la columna JSON crecerá aproximadamente 3 MB por día, o unos 90 MB por mes.
Diferencia entre literal y columna almacenada
Un detalle importante: JSON_STORAGE_SIZE puede devolver valores diferentes según si el argumento es un literal JSON o un valor almacenado en una columna. Cuando pasas un literal, la función calcula el tamaño que tendría si se almacenara. Cuando pasas una columna, devuelve el tamaño real almacenado en disco.
La diferencia puede aparecer después de actualizaciones parciales. Cuando MySQL realiza una actualización parcial de un documento JSON (modificar un valor sin reescribir todo el documento), puede dejar espacio libre dentro del documento. JSON_STORAGE_SIZE siempre devuelve el tamaño total incluyendo ese espacio libre. Para conocer solo el espacio libre, existe la función JSON_STORAGE_FREE que veremos a continuación.
Manejo de NULL
Si el argumento es NULL, la función devuelve NULL:
SELECT JSON_STORAGE_SIZE(NULL) AS resultado;| resultado |
|---|
| NULL |
Para valores escalares, la función también es válida:
SELECT
JSON_STORAGE_SIZE('"cadena"') AS cadena,
JSON_STORAGE_SIZE('42') AS entero,
JSON_STORAGE_SIZE('true') AS booleano,
JSON_STORAGE_SIZE('null') AS nulo_json;| cadena | entero | booleano | nulo_json |
|---|---|---|---|
| 8 | 2 | 2 | 2 |
Los escalares ocupan muy poco espacio. Los booleanos y null ocupan solo 2 bytes. Un entero pequeño también 2 bytes. Las cadenas ocupan su longitud más algunos bytes de overhead.
Combinación con otras funciones
Puedes combinar JSON_STORAGE_SIZE con funciones de agregación para análisis más profundos:
SELECT
CASE
WHEN JSON_STORAGE_SIZE(atributos) < 50 THEN 'Pequeño (menos de 50 bytes)'
WHEN JSON_STORAGE_SIZE(atributos) < 200 THEN 'Mediano (50-200 bytes)'
ELSE 'Grande (>200 bytes)'
END AS categoria_tamanio,
COUNT(*) AS cantidad
FROM productos
GROUP BY categoria_tamanio;| categoria_tamanio | cantidad |
|---|---|
| Pequeño (menos de 50 bytes) | 2 |
| Mediano (50-200 bytes) | 2 |
Este tipo de consultas te permite entender la distribución de tamaños en tu tabla y tomar decisiones informadas sobre particionamiento, archivado o compresión.
También puedes usar JSON_STORAGE_SIZE junto con JSON_KEYS para correlacionar el número de claves con el tamaño:
SELECT
nombre,
JSON_LENGTH(JSON_KEYS(atributos)) AS num_claves,
JSON_DEPTH(atributos) AS profundidad,
JSON_STORAGE_SIZE(atributos) AS bytes
FROM productos
ORDER BY bytes DESC;| nombre | num_claves | profundidad | bytes |
|---|---|---|---|
| MacBook Air M3 | 7 | 2 | 178 |
| iPhone 15 Pro | 5 | 3 | 158 |
| Camiseta básica | 2 | 1 | 37 |
| Cable USB-C | 1 | 1 | 22 |
Esta vista combinada te muestra que los documentos más grandes no siempre son los que tienen más claves: el iPhone tiene menos claves que el MacBook pero mayor profundidad por sus objetos y arrays anidados.
En el siguiente artículo veremos JSON_STORAGE_FREE para conocer el espacio libre tras actualizaciones parciales.
Escrito por Eduardo Lázaro
