• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

Simplifying MySQL Sharding with ProxySQL: A Step-by-Step Guide

Vivek Thakkar

Cloud & DevOps

Introduction:

ProxySQL is a powerful SQL-aware proxy designed to sit between database servers and client applications, optimizing database traffic with features like load balancing, query routing, and failover. This article focuses on simplifying the setup of ProxySQL, especially for users implementing data-based sharding in a MySQL database.

What is Sharding?

Sharding involves partitioning a database into smaller, more manageable pieces called shards based on certain criteria, such as data attributes. ProxySQL supports data-based sharding, allowing users to distribute data across different shards based on specific conditions.

Understanding the Need for ProxySQL:

ProxySQL is an intermediary layer that enhances database management, monitoring, and optimization. With features like data-based sharding, ProxySQL is an ideal solution for scenarios where databases need to be distributed based on specific data attributes, such as geographic regions.

Installation & Setup:

There are two ways to install the proxy, either by installing it using packages or running  ProxySQL in docker. ProxySQL can be installed using two methods: via packages or running it in a Docker container. For this guide, we will focus on the Docker installation.

1. Install ProxySQL and MySQL Docker Images:

To start, pull the necessary Docker images for ProxySQL and MySQL using the following commands:

docker pull mysql:latest
docker pull proxysql/proxysql
view raw .sh hosted with ❤ by GitHub

2. Create Docker Network:

Create a Docker network for communication between MySQL containers:

docker network create multi-tenant-network
view raw .sh hosted with ❤ by GitHub

Note: ProxySQL setup will need connections to multiple SQL servers. So, we will set up multiple SQL servers on our docker inside a Docker network.

Containers within the same Docker network can communicate with each other using their container names or IP addresses.

You can check the list of all the Docker networks currently present by running the following command:

docker network ls
view raw .sh hosted with ❤ by GitHub

3. Set Up MySQL Containers:

Now, create three MySQL containers within the network:

Note: We can create any number of MySQL containers.

docker run -d --name mysql_host_1 --network=multi-tenant-network -p 3307:3306 -e MYSQL_ROOT_PASSWORD=pass123 mysql:latest
docker run -d --name mysql_host_2 --network=multi-tenant-network -p 3308:3306 -e MYSQL_ROOT_PASSWORD=pass123 mysql:latest
docker run -d --name mysql_host_3 --network=multi-tenant-network -p 3309:3306 -e MYSQL_ROOT_PASSWORD=pass123 mysql:latest
view raw .sh hosted with ❤ by GitHub

Note: Adjust port numbers as necessary. 

The default MySQL protocol port is 3306, but since we cannot access all three of our MySQL containers on the same port, we have set their ports to 3307, 3308, and 3309. Although internally, all MySQL containers will connect using port 3306.

--network=multi-tenant-network. This specifies that the container should be created under the specified network.

We have also specified the root password of the MySQL container to log into it, where the username is “root” and the password is “pass123” for all three of them.

After running the above three commands, three MySQL containers will start running inside the network. You can connect to these three hosts using host = localhost or 127.0.0.1 and port = 3307 / 3308 / 3309.

To ping the port, use the following command:

for macOS:

nc -zv 127.0.0.1 3307
view raw .sh hosted with ❤ by GitHub

for Windows: 

ping 127.0.0.1 3307
view raw .sh hosted with ❤ by GitHub

for Linux: 

telnet 127.0.0.1 3307
view raw .sh hosted with ❤ by GitHub

Reference Image

4. Create Users in MySQL Containers:

Create “user_shard” and “monitor” users in each MySQL container.

The “user_shard” user will be used by the proxy to make queries to the DB.

The “monitor” user will be used by the proxy to monitor the DB.

Note: To access the MySQL container mysql_host_1, use the command:

docker exec -it mysql_host_1 mysql -uroot -ppass123
view raw .sh hosted with ❤ by GitHub

Use the following commands inside the MySQL container to create the user:

CREATE USER 'user_shard'@'%' IDENTIFIED BY 'pass123';
GRANT ALL PRIVILEGES ON *.* TO 'user_shard'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
CREATE USER monitor@'%' IDENTIFIED BY 'pass123';
GRANT ALL PRIVILEGES ON *.* TO monitor@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
view raw .sql hosted with ❤ by GitHub

Repeat the above steps for mysql_host_2 & mysql_host_3.

If, at any point, you need to drop the user, you can use the following command:

DROP USER monitor@’%’;
view raw .sql hosted with ❤ by GitHub

5. Prepare ProxySQL Configuration:

To prepare the configuration, we will need the IP addresses of the MySQL containers. To find those, we can use the following command:

docker inspect mysql_host_1;
docker inspect mysql_host_2;
docker inspect mysql_host_3;
view raw .sh hosted with ❤ by GitHub

By running these commands, you will get all the details of the MySQL Docker container under a field named “IPAddress” inside your network. That is the IP address of that particular MySQL container.

Example:
mysql_host_1: 172.19.0.2

mysql_host_2: 172.19.0.3

mysql_host_3: 172.19.0.4

Reference image for IP address of mysql_host_1: 172.19.0.2

Now, create a ProxySQL configuration file named proxysql.cnf. Include details such as IP addresses of MySQL containers, administrative credentials, and MySQL users.

Below is the content that needs to be added to the proxysql.cnf file:

datadir="/var/lib/proxysql"
admin_variables=
{
admin_credentials="admin:admin;radmin:radmin"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
hash_passwords=false
}
mysql_variables=
{
threads=4
max_connections=2048
default_query_delay=0
default_query_timeout=36000000
have_compress=true
poll_timeout=2000
interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
default_schema="information_schema"
stacksize=1048576
server_version="5.1.30"
connect_timeout_server=10000
monitor_history=60000
monitor_connect_interval=200000
monitor_ping_interval=200000
ping_interval_server_msec=10000
ping_timeout_server=200
commands_stats=true
sessions_sort=true
monitor_username="monitor"
monitor_password="pass123"
}
mysql_servers =
(
{ address="172.19.0.2" , port=3306 , hostgroup=10, max_connections=100 },
{ address="172.19.0.3" , port=3306 , hostgroup=20, max_connections=100 },
{ address="172.19.0.4" , port=3306 , hostgroup=30, max_connections=100 }
)
mysql_users =
(
{ username = "user_shard" , password = "pass123" , default_hostgroup = 10 , active = 1 },
{ username = "user_shard" , password = "pass123" , default_hostgroup = 20 , active = 1 },
{ username = "user_shard" , password = "pass123" , default_hostgroup = 30 , active = 1 }
)
view raw .sh hosted with ❤ by GitHub

Most of the settings are default; we won’t go into much detail for each setting. 

admin_variables: These variables are used for ProxySQL's administrative interface. It allows you to connect to ProxySQL and perform administrative tasks such as configuring runtime settings, managing servers, and monitoring performance.

mysql_variables, monitor_username, and monitor_password are used to specify the username that ProxySQL will use when connecting to MySQL servers for monitoring purposes. This monitoring user is used to execute queries and gather statistics about the health and performance of the MySQL servers. This is the user we created during step 4.

mysql_servers will contain all the MySQL servers we want to be connected with ProxySQL. Each entry will have the IP address of the MySQL container, port, host group, and max_connections. Mysql_users will have all the users we created during step 4.

7. Run ProxySQL Container:

Inside the same directory where the proxysql.cnf file is located, run the following command to start ProxySQL:

docker run -d --rm -p 6032:6032 -p 6033:6033 -p 6080:6080 --name=proxysql --network=multi-tenant-network -v $PWD/proxysql.cnf:/etc/proxysql.cnf proxysql/proxysql
view raw .sh hosted with ❤ by GitHub

Here, port 6032 is used for ProxySQL's administrative interface. It allows you to connect to ProxySQL and perform administrative tasks such as configuring runtime settings, managing servers, and monitoring performance.

Port 6033 is the default port for ProxySQL's MySQL protocol interface. It is used for handling MySQL client connections. Our application will use it to access the ProxySQL db and make SQL queries.

The above command will make ProxySQL run on our Docker with the configuration provided in the proxysql.cnf file.

Inside ProxySQL Container:

8. Access ProxySQL Admin Console:

Now, to access the ProxySQL Docker container, use the following command:

docker exec -it proxysql bash
view raw .sh hosted with ❤ by GitHub

Now, once you’re inside the ProxySQL Docker container, you can access the ProxySQL admin console using the command:

mysql -u admin -padmin -h 127.0.0.1 -P 6032
view raw .sh hosted with ❤ by GitHub

You can run the following queries to get insights into your ProxySQL server:

i) To get the list of all the connected MySQL servers:

SELECT * FROM mysql_servers;
view raw .sql hosted with ❤ by GitHub

ii) Verify the status of the MySQL backends in the monitor database tables in ProxySQL admin using the following command:

SHOW TABLES FROM monitor;
view raw .sql hosted with ❤ by GitHub

If this returns an empty set, it means that the monitor username and password are not set correctly. You can do so by using the below commands:

UPDATE global_variables SET variable_value=’monitor’ WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value=’pass123’ WHERE variable_name='mysql-monitor_password';
LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
view raw .sql hosted with ❤ by GitHub

And then restart the proxy Docker container:

iii) Check the status of DBs connected to ProxySQL using the following command:

SELECT * FROM monitor.mysql_server_connect_log ORDER BY time_start_us DESC;
view raw .sql hosted with ❤ by GitHub

iv) To get a list of all the ProxySQL global variables, use the following command:

SELECT * FROM global_variables; 
view raw .sql hosted with ❤ by GitHub

v) To get all the queries made on ProxySQL, use the following command:

Select * from stats_mysql_query_digest;
view raw .sql hosted with ❤ by GitHub

Note: Whenever we change any row, use the below commands to load them:

Change in variables:

LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
Change in mysql_servers:
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
Change in mysql_query_rules:
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
view raw .sql hosted with ❤ by GitHub

And then restart the proxy docker container.

IMPORTANT:

To connect to ProxySQL’s admin console, first get into the Docker container using the following command:

docker exec -it proxysql bash
view raw .sh hosted with ❤ by GitHub

Then, to access the ProxySQL admin console, use the following command:

mysql -u admin -padmin -h 127.0.0.1 -P6032
view raw .sh hosted with ❤ by GitHub

To access the ProxySQL MySQL console, we can directly access it using the following command without going inside the Docker ProxySQL container:

mysql -u user_shard -ppass123 -h 127.0.0.1 -P6033
view raw .sh hosted with ❤ by GitHub

To make queries to the database, we make use of ProxySQL’s 6033 port, where MySQL is being accessed.

9. Define Query Rules:

We can add custom query rules inside the mysql_query_rules table to redirect queries to specific databases based on defined patterns. Load the rules to runtime and save to disk.

12. Sharding Example:

Now, let's illustrate how to leverage ProxySQL's data-based sharding capabilities through a practical example. We'll create three MySQL containers, each containing data from different continents in the “world” database, specifically within the “countries” table.

Step 1: Create 3 MySQL containers named mysql_host_1, mysql_host_2 & mysql_host_3.

Inside all containers, create a database named “world” with a table named “countries”.

i) Inside mysql_host_1: Insert countries using the following query:

INSERT INTO `countries` VALUES (1,'India','Asia'),(2,'Japan','Asia'),(3,'China','Asia'),(4,'USA','North America'),(5,'Cuba','North America'),(6,'Honduras','North America');
view raw .sql hosted with ❤ by GitHub

ii) Inside mysql_host_2: Insert countries using the following query:

INSERT INTO `countries` VALUES (1,'Kenya','Africa'),(2,'Ghana','Africa'),(3,'Morocco','Africa'),(4, "Brazil", "South America"), (5, "Chile", "South America"), (6, "Morocco", "South America");
view raw .sql hosted with ❤ by GitHub

iii) Inside mysql_host_3: Insert countries using the following query:

CODE: INSERT INTO `countries` VALUES (1, "Italy", "Europe"), (2, "Germany", "Europe"), (3, "France", "Europe");

Now, we have distinct data sets for Asia & North America in mysql_host_1, Africa & South America in mysql_host_2, and Europe in mysql_host_3..js

Now, we have distinct data sets for Asia & North America in mysql_host_1, Africa & South America in mysql_host_2, and Europe in mysql_host_3.

Step 2: Define Query Rules for Sharding

Let's create custom query rules to redirect queries based on the continent specified in the SQL statement.

For example, if the query contains the continent “Asia,” we want it to be directed to mysql_host_1.

-- Query Rule for Asia and North America 

INSERT INTO mysql_query_rules (rule_id, active, username, match_pattern, destination_hostgroup, apply) VALUES (10, 1, 'user_shard', "\s*continent\s*=\s*.*?(Asia|North America).*?\s*", 10, 0);
view raw .sql hosted with ❤ by GitHub

-- Query Rule for Africa and South America

INSERT INTO mysql_query_rules (rule_id, active, username, match_pattern, destination_hostgroup, apply) VALUES (20, 1, 'user_shard', "\s*continent\s*=\s*.*?(Africa|South America).*?\s*", 20, 0);
view raw .sql hosted with ❤ by GitHub

-- Query Rule for Europe 

INSERT INTO mysql_query_rules (rule_id, active, username, match_pattern, destination_hostgroup, apply) VALUES (30, 1, 'user_shard', "\s*continent\s*=\s*.*?(Europe).*?\s*", 30, 0);
view raw .sql hosted with ❤ by GitHub

Step 3: Apply and Save Query Rules

After adding the query rules, ensure they take effect by running the following commands:

LOAD MYSQL QUERY RULES TO RUNTIME; 
SAVE MYSQL QUERY RULES TO DISK;
view raw .sql hosted with ❤ by GitHub

Now, access the MySQL server using the ProxySQL port and execute queries:

mysql -u user_shard -ppass123 -h 127.0.0.1 -P 6033
view raw .sh hosted with ❤ by GitHub

use world;
view raw .sql hosted with ❤ by GitHub

-- Example Queries:

Select * from countries where id = 1 and continent = "Asia";
view raw .sql hosted with ❤ by GitHub

-- This will return id=1, name=India, continent=Asia

Select * from countries where id = 1 and continent = "Africa";
view raw .sql hosted with ❤ by GitHub

-- This will return id=1, name=Kenya, continent=Africa.

Select * from countries where id = 1 and continent = "Africa";
view raw .sql hosted with ❤ by GitHub

Based on the defined query rules, the queries will be redirected to the specified MySQL host groups. If no rules match, the default host group that’s specified in mysql_users inside proxysql.cnf will be used.

Conclusion:

ProxySQL simplifies access to distributed data through effective sharding strategies. Its flexible query rules, combined with regex patterns and host group definitions, offer significant flexibility with relative simplicity.

By following this step-by-step guide, users can quickly set up ProxySQL and leverage its capabilities to optimize database performance and achieve efficient data distribution.

References:

Download and Install ProxySQL - ProxySQL

How to configure ProxySQL for the first time - ProxySQL

Admin Variables - ProxySQL

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

Simplifying MySQL Sharding with ProxySQL: A Step-by-Step Guide

Introduction:

ProxySQL is a powerful SQL-aware proxy designed to sit between database servers and client applications, optimizing database traffic with features like load balancing, query routing, and failover. This article focuses on simplifying the setup of ProxySQL, especially for users implementing data-based sharding in a MySQL database.

What is Sharding?

Sharding involves partitioning a database into smaller, more manageable pieces called shards based on certain criteria, such as data attributes. ProxySQL supports data-based sharding, allowing users to distribute data across different shards based on specific conditions.

Understanding the Need for ProxySQL:

ProxySQL is an intermediary layer that enhances database management, monitoring, and optimization. With features like data-based sharding, ProxySQL is an ideal solution for scenarios where databases need to be distributed based on specific data attributes, such as geographic regions.

Installation & Setup:

There are two ways to install the proxy, either by installing it using packages or running  ProxySQL in docker. ProxySQL can be installed using two methods: via packages or running it in a Docker container. For this guide, we will focus on the Docker installation.

1. Install ProxySQL and MySQL Docker Images:

To start, pull the necessary Docker images for ProxySQL and MySQL using the following commands:

docker pull mysql:latest
docker pull proxysql/proxysql
view raw .sh hosted with ❤ by GitHub

2. Create Docker Network:

Create a Docker network for communication between MySQL containers:

docker network create multi-tenant-network
view raw .sh hosted with ❤ by GitHub

Note: ProxySQL setup will need connections to multiple SQL servers. So, we will set up multiple SQL servers on our docker inside a Docker network.

Containers within the same Docker network can communicate with each other using their container names or IP addresses.

You can check the list of all the Docker networks currently present by running the following command:

docker network ls
view raw .sh hosted with ❤ by GitHub

3. Set Up MySQL Containers:

Now, create three MySQL containers within the network:

Note: We can create any number of MySQL containers.

docker run -d --name mysql_host_1 --network=multi-tenant-network -p 3307:3306 -e MYSQL_ROOT_PASSWORD=pass123 mysql:latest
docker run -d --name mysql_host_2 --network=multi-tenant-network -p 3308:3306 -e MYSQL_ROOT_PASSWORD=pass123 mysql:latest
docker run -d --name mysql_host_3 --network=multi-tenant-network -p 3309:3306 -e MYSQL_ROOT_PASSWORD=pass123 mysql:latest
view raw .sh hosted with ❤ by GitHub

Note: Adjust port numbers as necessary. 

The default MySQL protocol port is 3306, but since we cannot access all three of our MySQL containers on the same port, we have set their ports to 3307, 3308, and 3309. Although internally, all MySQL containers will connect using port 3306.

--network=multi-tenant-network. This specifies that the container should be created under the specified network.

We have also specified the root password of the MySQL container to log into it, where the username is “root” and the password is “pass123” for all three of them.

After running the above three commands, three MySQL containers will start running inside the network. You can connect to these three hosts using host = localhost or 127.0.0.1 and port = 3307 / 3308 / 3309.

To ping the port, use the following command:

for macOS:

nc -zv 127.0.0.1 3307
view raw .sh hosted with ❤ by GitHub

for Windows: 

ping 127.0.0.1 3307
view raw .sh hosted with ❤ by GitHub

for Linux: 

telnet 127.0.0.1 3307
view raw .sh hosted with ❤ by GitHub

Reference Image

4. Create Users in MySQL Containers:

Create “user_shard” and “monitor” users in each MySQL container.

The “user_shard” user will be used by the proxy to make queries to the DB.

The “monitor” user will be used by the proxy to monitor the DB.

Note: To access the MySQL container mysql_host_1, use the command:

docker exec -it mysql_host_1 mysql -uroot -ppass123
view raw .sh hosted with ❤ by GitHub

Use the following commands inside the MySQL container to create the user:

CREATE USER 'user_shard'@'%' IDENTIFIED BY 'pass123';
GRANT ALL PRIVILEGES ON *.* TO 'user_shard'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
CREATE USER monitor@'%' IDENTIFIED BY 'pass123';
GRANT ALL PRIVILEGES ON *.* TO monitor@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
view raw .sql hosted with ❤ by GitHub

Repeat the above steps for mysql_host_2 & mysql_host_3.

If, at any point, you need to drop the user, you can use the following command:

DROP USER monitor@’%’;
view raw .sql hosted with ❤ by GitHub

5. Prepare ProxySQL Configuration:

To prepare the configuration, we will need the IP addresses of the MySQL containers. To find those, we can use the following command:

docker inspect mysql_host_1;
docker inspect mysql_host_2;
docker inspect mysql_host_3;
view raw .sh hosted with ❤ by GitHub

By running these commands, you will get all the details of the MySQL Docker container under a field named “IPAddress” inside your network. That is the IP address of that particular MySQL container.

Example:
mysql_host_1: 172.19.0.2

mysql_host_2: 172.19.0.3

mysql_host_3: 172.19.0.4

Reference image for IP address of mysql_host_1: 172.19.0.2

Now, create a ProxySQL configuration file named proxysql.cnf. Include details such as IP addresses of MySQL containers, administrative credentials, and MySQL users.

Below is the content that needs to be added to the proxysql.cnf file:

datadir="/var/lib/proxysql"
admin_variables=
{
admin_credentials="admin:admin;radmin:radmin"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
hash_passwords=false
}
mysql_variables=
{
threads=4
max_connections=2048
default_query_delay=0
default_query_timeout=36000000
have_compress=true
poll_timeout=2000
interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
default_schema="information_schema"
stacksize=1048576
server_version="5.1.30"
connect_timeout_server=10000
monitor_history=60000
monitor_connect_interval=200000
monitor_ping_interval=200000
ping_interval_server_msec=10000
ping_timeout_server=200
commands_stats=true
sessions_sort=true
monitor_username="monitor"
monitor_password="pass123"
}
mysql_servers =
(
{ address="172.19.0.2" , port=3306 , hostgroup=10, max_connections=100 },
{ address="172.19.0.3" , port=3306 , hostgroup=20, max_connections=100 },
{ address="172.19.0.4" , port=3306 , hostgroup=30, max_connections=100 }
)
mysql_users =
(
{ username = "user_shard" , password = "pass123" , default_hostgroup = 10 , active = 1 },
{ username = "user_shard" , password = "pass123" , default_hostgroup = 20 , active = 1 },
{ username = "user_shard" , password = "pass123" , default_hostgroup = 30 , active = 1 }
)
view raw .sh hosted with ❤ by GitHub

Most of the settings are default; we won’t go into much detail for each setting. 

admin_variables: These variables are used for ProxySQL's administrative interface. It allows you to connect to ProxySQL and perform administrative tasks such as configuring runtime settings, managing servers, and monitoring performance.

mysql_variables, monitor_username, and monitor_password are used to specify the username that ProxySQL will use when connecting to MySQL servers for monitoring purposes. This monitoring user is used to execute queries and gather statistics about the health and performance of the MySQL servers. This is the user we created during step 4.

mysql_servers will contain all the MySQL servers we want to be connected with ProxySQL. Each entry will have the IP address of the MySQL container, port, host group, and max_connections. Mysql_users will have all the users we created during step 4.

7. Run ProxySQL Container:

Inside the same directory where the proxysql.cnf file is located, run the following command to start ProxySQL:

docker run -d --rm -p 6032:6032 -p 6033:6033 -p 6080:6080 --name=proxysql --network=multi-tenant-network -v $PWD/proxysql.cnf:/etc/proxysql.cnf proxysql/proxysql
view raw .sh hosted with ❤ by GitHub

Here, port 6032 is used for ProxySQL's administrative interface. It allows you to connect to ProxySQL and perform administrative tasks such as configuring runtime settings, managing servers, and monitoring performance.

Port 6033 is the default port for ProxySQL's MySQL protocol interface. It is used for handling MySQL client connections. Our application will use it to access the ProxySQL db and make SQL queries.

The above command will make ProxySQL run on our Docker with the configuration provided in the proxysql.cnf file.

Inside ProxySQL Container:

8. Access ProxySQL Admin Console:

Now, to access the ProxySQL Docker container, use the following command:

docker exec -it proxysql bash
view raw .sh hosted with ❤ by GitHub

Now, once you’re inside the ProxySQL Docker container, you can access the ProxySQL admin console using the command:

mysql -u admin -padmin -h 127.0.0.1 -P 6032
view raw .sh hosted with ❤ by GitHub

You can run the following queries to get insights into your ProxySQL server:

i) To get the list of all the connected MySQL servers:

SELECT * FROM mysql_servers;
view raw .sql hosted with ❤ by GitHub

ii) Verify the status of the MySQL backends in the monitor database tables in ProxySQL admin using the following command:

SHOW TABLES FROM monitor;
view raw .sql hosted with ❤ by GitHub

If this returns an empty set, it means that the monitor username and password are not set correctly. You can do so by using the below commands:

UPDATE global_variables SET variable_value=’monitor’ WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value=’pass123’ WHERE variable_name='mysql-monitor_password';
LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
view raw .sql hosted with ❤ by GitHub

And then restart the proxy Docker container:

iii) Check the status of DBs connected to ProxySQL using the following command:

SELECT * FROM monitor.mysql_server_connect_log ORDER BY time_start_us DESC;
view raw .sql hosted with ❤ by GitHub

iv) To get a list of all the ProxySQL global variables, use the following command:

SELECT * FROM global_variables; 
view raw .sql hosted with ❤ by GitHub

v) To get all the queries made on ProxySQL, use the following command:

Select * from stats_mysql_query_digest;
view raw .sql hosted with ❤ by GitHub

Note: Whenever we change any row, use the below commands to load them:

Change in variables:

LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
Change in mysql_servers:
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
Change in mysql_query_rules:
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
view raw .sql hosted with ❤ by GitHub

And then restart the proxy docker container.

IMPORTANT:

To connect to ProxySQL’s admin console, first get into the Docker container using the following command:

docker exec -it proxysql bash
view raw .sh hosted with ❤ by GitHub

Then, to access the ProxySQL admin console, use the following command:

mysql -u admin -padmin -h 127.0.0.1 -P6032
view raw .sh hosted with ❤ by GitHub

To access the ProxySQL MySQL console, we can directly access it using the following command without going inside the Docker ProxySQL container:

mysql -u user_shard -ppass123 -h 127.0.0.1 -P6033
view raw .sh hosted with ❤ by GitHub

To make queries to the database, we make use of ProxySQL’s 6033 port, where MySQL is being accessed.

9. Define Query Rules:

We can add custom query rules inside the mysql_query_rules table to redirect queries to specific databases based on defined patterns. Load the rules to runtime and save to disk.

12. Sharding Example:

Now, let's illustrate how to leverage ProxySQL's data-based sharding capabilities through a practical example. We'll create three MySQL containers, each containing data from different continents in the “world” database, specifically within the “countries” table.

Step 1: Create 3 MySQL containers named mysql_host_1, mysql_host_2 & mysql_host_3.

Inside all containers, create a database named “world” with a table named “countries”.

i) Inside mysql_host_1: Insert countries using the following query:

INSERT INTO `countries` VALUES (1,'India','Asia'),(2,'Japan','Asia'),(3,'China','Asia'),(4,'USA','North America'),(5,'Cuba','North America'),(6,'Honduras','North America');
view raw .sql hosted with ❤ by GitHub

ii) Inside mysql_host_2: Insert countries using the following query:

INSERT INTO `countries` VALUES (1,'Kenya','Africa'),(2,'Ghana','Africa'),(3,'Morocco','Africa'),(4, "Brazil", "South America"), (5, "Chile", "South America"), (6, "Morocco", "South America");
view raw .sql hosted with ❤ by GitHub

iii) Inside mysql_host_3: Insert countries using the following query:

CODE: INSERT INTO `countries` VALUES (1, "Italy", "Europe"), (2, "Germany", "Europe"), (3, "France", "Europe");

Now, we have distinct data sets for Asia & North America in mysql_host_1, Africa & South America in mysql_host_2, and Europe in mysql_host_3..js

Now, we have distinct data sets for Asia & North America in mysql_host_1, Africa & South America in mysql_host_2, and Europe in mysql_host_3.

Step 2: Define Query Rules for Sharding

Let's create custom query rules to redirect queries based on the continent specified in the SQL statement.

For example, if the query contains the continent “Asia,” we want it to be directed to mysql_host_1.

-- Query Rule for Asia and North America 

INSERT INTO mysql_query_rules (rule_id, active, username, match_pattern, destination_hostgroup, apply) VALUES (10, 1, 'user_shard', "\s*continent\s*=\s*.*?(Asia|North America).*?\s*", 10, 0);
view raw .sql hosted with ❤ by GitHub

-- Query Rule for Africa and South America

INSERT INTO mysql_query_rules (rule_id, active, username, match_pattern, destination_hostgroup, apply) VALUES (20, 1, 'user_shard', "\s*continent\s*=\s*.*?(Africa|South America).*?\s*", 20, 0);
view raw .sql hosted with ❤ by GitHub

-- Query Rule for Europe 

INSERT INTO mysql_query_rules (rule_id, active, username, match_pattern, destination_hostgroup, apply) VALUES (30, 1, 'user_shard', "\s*continent\s*=\s*.*?(Europe).*?\s*", 30, 0);
view raw .sql hosted with ❤ by GitHub

Step 3: Apply and Save Query Rules

After adding the query rules, ensure they take effect by running the following commands:

LOAD MYSQL QUERY RULES TO RUNTIME; 
SAVE MYSQL QUERY RULES TO DISK;
view raw .sql hosted with ❤ by GitHub

Now, access the MySQL server using the ProxySQL port and execute queries:

mysql -u user_shard -ppass123 -h 127.0.0.1 -P 6033
view raw .sh hosted with ❤ by GitHub

use world;
view raw .sql hosted with ❤ by GitHub

-- Example Queries:

Select * from countries where id = 1 and continent = "Asia";
view raw .sql hosted with ❤ by GitHub

-- This will return id=1, name=India, continent=Asia

Select * from countries where id = 1 and continent = "Africa";
view raw .sql hosted with ❤ by GitHub

-- This will return id=1, name=Kenya, continent=Africa.

Select * from countries where id = 1 and continent = "Africa";
view raw .sql hosted with ❤ by GitHub

Based on the defined query rules, the queries will be redirected to the specified MySQL host groups. If no rules match, the default host group that’s specified in mysql_users inside proxysql.cnf will be used.

Conclusion:

ProxySQL simplifies access to distributed data through effective sharding strategies. Its flexible query rules, combined with regex patterns and host group definitions, offer significant flexibility with relative simplicity.

By following this step-by-step guide, users can quickly set up ProxySQL and leverage its capabilities to optimize database performance and achieve efficient data distribution.

References:

Download and Install ProxySQL - ProxySQL

How to configure ProxySQL for the first time - ProxySQL

Admin Variables - ProxySQL

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings