Sunteți pe pagina 1din 10

FRAGMENTACION EN MySQL

Desde la versin 5.1 existe la posibilidad de particionar nuestras tablas de forma horizontal (en
lneas), algo que nos puede ayudar en casos puntuales a mejorar el rendimiento de nuestra
base de datos. Resumiendo, este sistema nos permite dividir lgicamente una tabla muy
grande en otras ms pequeas, dentro de un rango de valores que nosotros indiquemos, de
forma que la consulta de datos sea ms rpida. Su uso es muy sencillo pero... cuando
debemos utilizarlo?

Cuando la tabla sea tan grande que los ndices no entren en RAM.

Cuando tengamos una tabla realmente grande (no hablo de megas).

Cuando almacenamos datos histricos.

Cuando los datos no paran de crecer y crecer...

Hay que tener en cuenta que este particionado es totalmente transparente para el usuario (y
por lgica tambin para nuestra aplicacin) por lo que en el caso de decidirnos por esta
solucin el cambio ser poco dramtico. Solamente tendremos que tener en cuenta estos
detalles:

La columna que utilicemos para definir el rango de las particiones debe ser un INT, no
se acepta cualquier otro valor.

Si tenemos una clave nica o una primary key, esta debe usarse para particionar.

Como mximo se permiten 1024 particiones.

No se permiten claves externas.

Si tenemos una tabla con millones de registros y hacemos una select, MySQL se deber
recorrer toda la tabla (en caso de no usar ndices, si estos ocupan ms que la RAM) o se tendr
que recorrer todos los ndices. Esto, contra ms grande es la tabla, mas ineficiente es:

Gracias al particionado es posible hacer una bsqueda en una fraccin mucho ms pequea
de nuestra tabla. Si la dividimos de forma que cada particin incluya 100 filas (particionando
por ID), MySQL sabe que el dato se tiene que encontrar en la segunda particin, por lo
que se evita tener que buscar en los restantes X millones de registros.

La mejora es clara, pero no siempre particionar nos va a dar mayor rendimiento. Si la tabla NO
es lo suficientemente grande incluso podemos degradar el rendimiento.

Tipos de fragmentacin
FRAGMENTACION VERTICAL
Alumno (cdigo, Nombre, apellido, grado, edad, asignaturas)
Esta relacin pude ser fragmentada verticalmente de la siguiente forma:
alumno1= cdigo, Nombre, apellido, edad ALUMNO

alumno2= cdigo, grado, asignatura ALUMNO


La operacin de reconstruccin es:
ALUMNO= alumno1 join alumno2
DATOS 1
cdigo

nombre

apellido

grado

edad

Asignatura

12345

Laura Camila

casas

Matemticas

124568

Jos Antonio

Rodrguez

espaol

ALUMNO1
CODIGO

NOMBRE

APELLIDO

EDAD

12345

Laura Camila

Casas

124568

Jos Antonio

rodrguez

ALUMNO2

Cdigo

Grado

Asignatura

12345

Matemticas

124568

Espaol

FRAGMENTACION HORIZONTAL
Alumno (cdigo, Nombre, apellido, grado, edad, asignaturas)

Cdigo nombre

Apellido

grado

edad

Asignatura

12345

Casas

Matemticas

124568 Jos Antonio

Rodrguez

espaol

876334 Oscar Ernesto

Majares

Matemticas

Laura Camila

Cdigo nombre

Apellido

grado edad Asignatura

12345

Casas

Matemticas

876334 Oscar Ernesto

Majares

Matemticas

Cdigo nombre

Apellido

grado edad Asignatura

124568 Jos Antonio

Rodrguez

Laura Camila

espaol

FRAGMENTACION MIXTA
Alumno (cdigo, Nombre, apellido, grado, edad, asignaturas)

Cdigo

nombre

Apellido

grado edad

12345

Laura Camila

Casas

124568

Jos Antonio

Rodrguez

876334

Oscar Ernesto

Majares

Cdigo

nombre

Apellido

grado Asignatura

12345

Laura Camila

Casas

Matemticas

124568

Jos Antonio

Rodrguez

espaol

876334

Oscar Ernesto

Majares

Cdigo

nombre

Apellido

grado edad

Asignatura

12345

Laura Camila

Casas

Matemticas

Matemticas

124568

Jos Antonio

Rodrguez

876334

Oscar Ernesto

Majares

3
2

espaol

Matemticas

Fragmentacin en MySQL
Al utilizar comandos de fragmentado a nivel de tablas, mysqld utiliza una clave de particin y un
algoritmo de particionado para determinar la divisin de los datos entre los fragmentos. Los
algoritmos de fragmentacin que tenemos son:
RANGE: Si la clave de fragmentacin est dentro de un rango de valores.
LIST: El fragmento es seleccionado de acuerdo a una lista de valores enteros.
HASH: El fragmento se elige de acuerdo a una funcin de hash.
KEY: Un algoritmo interno es utilizado por mysqld para elegir como sern distribuidos los datos
entre los fragmentos..

Fragmentado RANGE (Por rango de valores)


Suponiendo que tenemos la siguiente tabla definida:
CREATE TABLE empleados (
Id_empleado INT NOT NULL,
nombre VARCHAR(30) NOT NULL,
apellidos VARCHAR(30) NOT NULL,
fecha_inicio DATE NOT NULL DEFAULT 2000-01-01,
fecha_termino DATE DEFAULT NULL,
salario DECIMAL (8,2) NOT NULL,
codigo_de_trabajo INT NOT NULL,
id_almacenamiento INT NOT NULL
);
Suponiendo que queremos almacenar los datos de 300 empleados en tres tablas de a 100
cada una, procedemos con la siguiente instruccin:
ALTER TABLE empleados
PARTITION BY RANGE (id_almacenamiento) (
PARTITION p0 VALUES LESS THAN (101),
PARTITION p1 VALUES LESS THAN (201),
PARTITION p2 VALUES LESS THAN (301),
);

Esta instruccin utiliza el atributo id_almacenamiento como clave de fragmentado (partition


key), y las particiones son p0, p1, p2, , en este caso las tres particiones corresponden a los
registros en el rango desde 0 hasta 300, en el caso de que quisiramos insertar un registro con
un valor mayor a 300, nos generara un error como el siguiente:
ERROR 1526 (HY000): Table has no partition for value 301.
Si desde la definicin de la tabla queremos especificar un esquema de fragmentado, usaramos
la siguiente instruccin:
CREATE TABLE empleados (
Id_empleado INT NOT NULL,
nombre VARCHAR(30) NOT NULL,
apellidos VARCHAR(30) NOT NULL,
fecha_inicio DATE NOT NULL DEFAULT 2000-01-01,
fecha_termino DATE DEFAULT NULL,
salario DECIMAL (8,2) NOT NULL,
codigo_de_trabajo INT NOT NULL,
id_almacenamiento INT NOT NULL
)
PARTITION BY RANGE (id_almacenamiento) (
PARTITION p0 VALUES LESS THAN (101),
PARTITION p1 VALUES LESS THAN (201),
PARTITION p2 VALUES LESS THAN (301),
PARTITION pfinal VALUES LESS THAN MAXVALUE);
Fragmentado LIST (Por lista definida)
Suponiendo que ahora tenemos la misma tabla, pero lo que queremos es distribuir a los
empleados de acuerdo a una lista de valores definida, los valores se distribuirn de acuerdo
a los valores definidos en cada lista de cada particin.
CREATE TABLE empleados (
Id_empleado INT NOT NULL,
nombre VARCHAR(30) NOT NULL,
apellidos VARCHAR(30) NOT NULL,
fecha_inicio DATE NOT NULL DEFAULT 2000-01-01,
fecha_termino DATE DEFAULT NULL,
salario DECIMAL (8,2) NOT NULL,
codigo_de_trabajo INT NOT NULL,
id_almacenamiento INT NOT NULL
)
PARTITION BY LIST (id_almacenamiento) (
PARTITION Sureste VALUES IN (1,2,3,4,5,6,7,8,9,10,15,17,18,20,21,24),
PARTITION AtlanticoMedio VALUES IN
(11,12,13,14,16,19,22,23,25,26,27,28),
PARTITION Noreste VALUES IN (29,30,33,38,40,41,50,56,64,65,75),
PARTITION EsteMedio VALUES IN (32,34,35,42,43,49,51,61,62,63,71),
PARTITION Noroeste VALUES IN (46,53,58,67,68,69,72,74),
PARTITION Canada VALUES IN (31,47,52,59,73),
PARTITION Inlaterra VALUES IN (39,55)

);
Fragmentado HASH (Por dispersin)
El uso de HASH nos permite dividir los datos de forma equitativa entre todas las particiones,
cosa que con otros tipos de particiones podra no pasar. De esta forma, si estamos trabajando
una tabla enorme y la queremos dividir en 10 particiones, estas tendrn un nmero de valores
muy similar.

CREATE TABLE empleados (


Id_empleado INT NOT NULL,
nombre VARCHAR(30) NOT NULL,
apellidos VARCHAR(30) NOT NULL,
fecha_inicio DATE NOT NULL DEFAULT 2000-01-01,
fecha_termino DATE DEFAULT NULL,
salario DECIMAL (8,2) NOT NULL,
codigo_de_trabajo INT NOT NULL,
id_almacenamiento INT NOT NULL
)
PARTITION BY HASH (id_almacenamiento)
PARTITIONS 16;

Fragmentado KEY (Fragmentado por clave)


Con Key NO es necesario indicar la columna que deseamos para particionar, en ese caso har
uso de la clave primaria. KEY es muy parecida a HASH, solo que en lugar de indicarle
nosotros el HASH mediante una expresin, lo har el propio MySQL usando MD5.
CREATE TABLE empleados (
Id_empleado INT NOT NULL PRIMARY KEY,
nombre VARCHAR(30) NOT NULL,
apellidos VARCHAR(30) NOT NULL,
fecha_inicio DATE NOT NULL DEFAULT 2000-01-01,
fecha_termino DATE DEFAULT NULL,
salario DECIMAL (8,2) NOT NULL,
codigo_de_trabajo INT NOT NULL,
id_almacenamiento INT NOT NULL
)
PARTITION BY HASH (Id_empleado)
PARTITIONS 16;

Ejercicio con la BD employees


1. Particiona la Tabla employees mediante RANGO por el campo store_id en 5 partes
(parte1 >6, parte 2<11, parte3 <16, parte4<21, parte 5 resto)

2. Igual particiona la misma tabla mediante LISTAS segn estos criterios:


a. PARTITION pNorth VALUES IN (3,5,6,9,17),
b. PARTITION pEast VALUES IN (1,2,10,11,19,20),
c. PARTITION pWest VALUES IN (4,12,13,14,1),
d. PARTITION pCentral VALUES IN (7,8,15,16)
3. Igual particiona la misma tabla mediante el mtodo HASHES por el campo hired en 4
partes.
4. Igual pero con el mtodo KEY en 2 partes
Dicho esto, comenzamos a particionar nuestra BBDD. La tabla en cuestin ser, como ya
indique, la de salarios.

select count(*) from salaries;


+----------+
| count(*) |
+----------+
|

2844047 |

+----------+
1 row in set (1.66 sec)

Casi 3 millones de registros, no est nada mal.


Vamos a hacer una bsqueda para saber cuntos corresponden al ao 2000.

select count(*) from salaries where from_date between


'2000-01-01' and '2000-12-31';
+----------+
| count(*) |
+----------+
|

255785 |

+----------+
1 row in set (2.39 sec)
Fijaros en el tiempo que ha tardado MySQL en realizar la consulta, 2.39 segundos. Bastante
tiempo, si en lugar de una sola consulta fuesen 100 concurrentes estaramos ante un verdadero
problema de rendimiento.

explain select count(*) from salaries where from_date


between '2000-01-01' and '2000-12-31'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: salaries
type: index
possible_keys: NULL
key: emp_no
key_len: 4
ref: NULL
rows: 2844513
Extra: Using where; Using index
1 row in set (0.00 sec)
La querie se tiene que recorrer los 3 millones de registros usando un ndice (lo cual no est
mal) y an as el rendimiento es pobre. Entonces, pidamos ayuda a nuestras amigas las
particiones. Vamos a particionar por ao .

ALTER TABLE salaries


PARTITION by range (year(from_date))
(
partition p001 VALUES LESS THAN (1986)
, partition p002 VALUES LESS THAN (1987)
, partition p003 VALUES LESS THAN (198
, partition p004 VALUES LESS THAN (1989)
, partition p005 VALUES LESS THAN (1990)
, partition p006 VALUES LESS THAN (1991)
, partition p007 VALUES LESS THAN (1992)
, partition p008 VALUES LESS THAN (1993)
, partition p009 VALUES LESS THAN (1994)
, partition p010 VALUES LESS THAN (1995)
, partition p011 VALUES LESS THAN (1996)
, partition p012 VALUES LESS THAN (1997)
, partition p013 VALUES LESS THAN (199
, partition p014 VALUES LESS THAN (1999)
, partition p015 VALUES LESS THAN (2000)
, partition p016 VALUES LESS THAN (2001)
, partition p017 VALUES LESS THAN (2002)
, partition p018 VALUES LESS THAN (2003)
);

Si ahora hacemos la misma select de antes:

select count(*) from salaries where


from_date between '2000-01-01' and '2000-12-31';
+----------+
| count(*) |
+----------+
|

255785 |

+----------+
1 row in set (0.22 sec)
La bsqueda de los salarios del ao 2000. Antes 2,39 segundos, ahora 0,22. La mejora es
impresionante.
Un explain partitions nos indica que particin ha usado para la select:

explain partitions select count(*) from salaries


where from_date between '2000-01-01' and '2000-12-31'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: salaries
partitions: p016
type: index
possible_keys: NULL
key: emp_no
key_len: 4
ref: NULL
rows: 2830488
Extra: Using where; Using index
1 row in set (0.00 sec)