How to Partition MySQL Tables

Tags: guide, schema, mysql, database
Published: 2021-10-06 16:14:37

## Horizontal and Vertical Partitioning
​There are two types of database partitions—vertical and horizontal.
​
### Vertical Partitioning 
​Vertical partitioning relies upon initially creating tables with fewer columns and then using additional tables to store leftover columns. Since rows are split according to their columns, vertical partitioning is also known as row splitting (opposite horizontal partitioning). As of right now, MySQL does **not** support vertical partitioning in its database.
​
### Horizontal Partitioning 
​The partitioning logic divides the rows into multiple tables. The number of columns remains constant throughout partitions, while the number of rows can vary. My SQL currently supports horizontal partitioning. In this post, we’ll look at three different kinds of horizontal partitions in MySQL.
​
## Range Partitioning
​When implementing range partitioning, if a column value falls within the specified range for a particular partition, the row is then added to that partition.
​
### Implementing Range Partitioning
1. Create a sample table:
    ```
    CREATE TABLE arctype.range_crypto(
    timestamp INT,
    open DOUBLE,
    close INT,
    high DOUBLE,
    low DOUBLE,
    volume DOUBLE,
    );
    ```
<br/>2. *(Optional)* Populate the table with sample data. In this case, [download this dataset](https://gist.github.com/Bamimore-Tomi/54b34a36a2a4f4e895cdd1081f4f4798#file-forex-csv). Then, import the CSV data into a table.
​
<br/>3. Create the range partitions using the `ALTER TABLE` expression:
​
    
    ALTER TABLE arctype.range_crypto
    PARTITION BY RANGE (close) (
      partition p0 VALUES LESS THAN (10000),
      partition p1 VALUES LESS THAN (20000),
      partition p2 VALUES LESS THAN (30000),
      partition p3 VALUES LESS THAN (40000),
      partition p4 VALUES LESS THAN (50000),
      partition p5 VALUES LESS THAN (60000),
      partition p6 VALUES LESS THAN  MAXVALUE
    )
     

<br/>4. You can now query data from any of the partitions you have created:
​
    
    SELECT * 
    FROM arctype.range_crypto PARTITION (p3) 
    WHERE close BETWEEN 35000 and 38000;
    
    
<br/>Conversely, if you run this query:
​
    
    SELECT * 
    FROM arctype.range_crypto PARTITION (p0) 
    WHERE close BETWEEN 35000 and 38000;
    
     

then the result will come up empty. Specifying the partition shows MySQL where to look, making your queries run faster (if you have millions of rows).

Another variation of the range partitioning is the `RANGE COLUMNS`. It lets you specify more than one partitioning column. Let us create range partitions that hold a range of close prices over some time.

   
    ALTER TABLE arctype.range_crypto 
    PARTITION BY RANGE COLUMNS (timestamp, close) (
        PARTITION from_2018_10k VALUES LESS THAN (1533127192, 10000),
        PARTITION from_2019_20k VALUES LESS THAN (1564663192, 20000),
        PARTITION from_2020_30k VALUES LESS THAN (1596285592,30000),
        PARTITION from_2021_40k VALUES LESS THAN (1627821592,40000 ),
        PARTITION from_latest_highest VALUES LESS THAN (MAXVALUE, MAXVALUE)
      );    
​
If you want to query all the rows with timestamps between 2018 and 2019 where prices closed between 13500 and 11600, you can run a query like this:

    
```
SELECT * 
FROM arctype.range_crypto PARTITION(from_2019_20k) 
WHERE close BETWEEN 11600 AND 13500
```
   
​
## List Partitioning
​In list partitioning, rows are grouped on the premise that their value in the column used for partitioning is similar to a value in the list (set of discrete values) defined. To put things in perspective, when you create the partitioning list(s), MySQL checks, “does a column in this row have a similar value with values in this list ?”. If the value is the same, MySQL adds that row to the partition for that value in your list. 
​
### Implementing List Partitioning
1. Create the sample table using:  
    ```
    CREATE TABLE arctype.football(
    home_team TEXT,
    away_team TEXT,
    home_goals INT,
    away_goals INT, 
    result TEXT,
    season TEXT
    );
    ```
<br />2. Optional — Populate the table with sample data. In this case, [download this dataset](https://gist.github.com/Bamimore-Tomi/54b34a36a2a4f4e895cdd1081f4f4798#file-football-csv).
​<br />3. You can now create the list partition using the `ALTER TABLE` expression.
    
    
    ALTER TABLE arctype.football
    PARTITION BY LIST (home_goals) (
    PARTITION odd VALUES IN (1,3,5,7,9),
    PARTITION even VALUES IN (0,2,4,6,8)
    );
    

We can now write queries using the partitions we just created:

```
 SELECT * FROM arctype.football PARTITION(odd) WHERE (home_goals=3);
```

##Hash Partitioning
When defining the previous partitions, you had to state which column value, range, or column values fell into a particular partition. With hash partitioning, you must specify the number of partitions you want for the column (or expression). MySQL uses `MOD(expression, number of partitions)` to determine which partition any row falls in. To understand how this works, look at this example;

![](https://lh4.googleusercontent.com/Y9XUmAdJgHEM0iB7Pje14usWvnF53SGCsRxALJ9-ksRO0l-3jX6R4-5xm9T6l6lOIug-aQXqaW6JtVN4Apb_tLXHmeYFn4JVqoiLO84ScHI4NQ0qzzZ8l2H0ZeRQpNpCwzDcRJvE=s0)

For the first row, the partition it belongs to based on the `home_goals` column is determined using `MOD(1,2)=1`. The partition for the third row will be selected using `MOD(2,2)=0`. If you have an imaginary row with `9 home_goals, MOD(9,2)=1`.

```
ALTER TABLE arctype.football
PARTITION BY HASH (home_goals)
PARTITIONS 2;
```

##Conclusion
In general, partitions speed up your searches. While this is correct, the effect of partitions is not readily apparent in smaller tables. So, if your queries are executing slowly and your database table does not include millions of rows, you should consider other optimization approaches before splitting your tables.

##Further Reading
1. https://en.wikipedia.org/wiki/Partition_(database)
2. https://dev.mysql.com/doc/refman/8.0/en/partitioning.html
3. https://www.w3resource.com/mysql/mysql-partition.php