The goal of this project is to use ProxySQL to load balance requests from a Spring Boot application to MySQL Replication Master-Slave Cluster.
On ivangfr.github.io, I have compiled my Proof-of-Concepts (PoCs) and articles. You can easily search for the technology you are interested in by using the filter. Who knows, perhaps I have already implemented a PoC or written an article about what you are looking for.
- 
MySQLis the most popular Open Source SQL database management system, supported byOracle. In this project, we set a MySQL Replication Master-Slave Cluster that contains threeMySQLinstances: one master and two slaves. In the replication process, data is automatically copied from the master to the slaves.
- 
ProxySQLis an open-source, high-performanceMySQLproxy server. It sits between application and database servers, accepting incoming traffic fromMySQLclients and forwarding it to backendMySQLservers. In this project, we set twohostgroups:writer=10andreader=20. Those hostgroups say to which database servers write or read requests should go. TheMySQLmaster belongs to thewriterhostgroup. On the other hand, the slaves belong to thereaderhostgroup.
- 
Spring BootWeb Java application that exposes a REST API for managing customers. Instead of connecting directly toMySQL, as usual, the application will be connected toProxySQL.customer-apihas the following endpoints:GET /api/customers GET /api/customers/{id} POST /api/customers {"firstName":"...", "lastName":"..."} PUT /api/customers/{id} {"firstName":"...", "lastName":"..."} DELETE /api/customers/{id}
- 
Open a terminal and, inside the springboot-proxysql-mysqlroot folder, run the following script:./init-environment.sh 
- 
Wait until the environment is up and running 
- 
In a terminal, make sure you are inside the springboot-proxysql-mysqlroot folder;
- 
To check the replication status run: ./check-replication-status.sh You should see something like: mysql-master ------------ File Position Binlog_Do_DB Binlog_Ignore_DB Executed_Gtid_Set mysql-bin-1.000003 1397 62a2f52f-b16b-11ed-91fc-0242c0a85002:1-14 mysql-slave-1 ------------- *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: mysql-master Master_User: replication Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin-1.000003 Read_Master_Log_Pos: 1397 Relay_Log_File: fa249eba35d6-relay-bin.000003 Relay_Log_Pos: 1614 Relay_Master_Log_File: mysql-bin-1.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes ... mysql-slave-2 ------------- *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: mysql-master Master_User: replication Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin-1.000003 Read_Master_Log_Pos: 1397 Relay_Log_File: cbfd1f4bb01a-relay-bin.000003 Relay_Log_Pos: 1614 Relay_Master_Log_File: mysql-bin-1.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes ...
- 
In a terminal and inside the springboot-proxysql-mysqlroot folder, run the script below to connect toProxySQLcommand line terminal:./proxysql-admin.sh 
- 
In ProxySQL Admin>terminal run the following command to see theMySQLservers:SELECT * FROM mysql_servers; 
- 
The following select shows the global variables: SELECT * FROM global_variables; 
- 
In order to exit ProxySQLcommand line terminal, typeexit.
- 
In a terminal and navigate to the springboot-proxysql-mysqlroot folder;
- 
Run the following Maven command to start the application: ./mvnw clean spring-boot:run --projects customer-api 
- 
Open three terminals: one for mysql-master, one formysql-slave-1and another formysql-slave-2;
- 
In mysql-masterterminal, connect toMySQL Monitorby running:docker exec -it -e MYSQL_PWD=secret mysql-master mysql -uroot --database customerdb
- 
Do the same for mysql-slave-1...docker exec -it -e MYSQL_PWD=secret mysql-slave-1 mysql -uroot --database customerdb
- 
... and mysql-slave-2docker exec -it -e MYSQL_PWD=secret mysql-slave-2 mysql -uroot --database customerdb
- 
Inside each MySQL Monitor'sterminal, run the following commands to enableMySQLlogs:SET GLOBAL general_log = 'ON'; SET global log_output = 'table'; 
- 
Open a new terminal. In it, we will just run curlcommands;
- 
In the curlterminal, let's create a customer:curl -i -X POST http://localhost:8080/api/customers \ -H 'Content-Type: application/json' \ -d '{"firstName": "Ivan", "lastName": "Franchin"}' 
- 
Go to mysql-masterterminal and run the followingSELECTcommand:SELECT event_time, command_type, SUBSTRING(argument,1,250) argument FROM mysql.general_log WHERE command_type = 'Query' AND (argument LIKE 'insert into customers %' OR argument LIKE 'select c1_0.id%' OR argument LIKE 'update customers %' OR argument LIKE 'delete from customers %'); It should return: +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | event_time | command_type | argument | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | 2023-02-20 22:13:15.400178 | Query | insert into customers (created_at, first_name, last_name, updated_at) values ('2023-02-20 22:13:15', 'Ivan', 'Franchin', '2023-02-20 22:13:15') | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+Note: If you run the same SELECTin the slave's terminal, you will see that just themysql-masterprocessed theinsertcommand. By the way, all inserts, updates, and deletes are executed onmysql-master.
- 
Now, let's call to the GETendpoint to retrievecustomer 1. For it, go tocurlterminal and run:curl -i http://localhost:8080/api/customers/1 
- 
If you run, in one of the slave's terminal, the SELECTcommand below:SELECT event_time, command_type, SUBSTRING(argument,1,250) argument FROM mysql.general_log WHERE command_type = 'Query' AND (argument LIKE 'insert into customers %' OR argument LIKE 'select c1_0.id%' OR argument LIKE 'update customers %' OR argument LIKE 'delete from customers %'); It should return: +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------+ | event_time | command_type | argument | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------+ | 2023-02-20 22:14:06.582449 | Query | select c1_0.id,c1_0.created_at,c1_0.first_name,c1_0.last_name,c1_0.updated_at from customers c1_0 where c1_0.id=1 | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------+Note: Just one slave should process it. 
- 
Next, let's UPDATEthecustomer 1. For it, go to thecurlterminal and run:curl -i -X PUT http://localhost:8080/api/customers/1 \ -H 'Content-Type: application/json' \ -d '{"firstName": "Ivan2", "lastName": "Franchin2"}' 
- 
Running the following SELECTinside themysql-masterterminal:SELECT event_time, command_type, SUBSTRING(argument,1,250) argument FROM mysql.general_log WHERE command_type = 'Query' AND (argument LIKE 'insert into customers %' OR argument LIKE 'select c1_0.id%' OR argument LIKE 'update customers %' OR argument LIKE 'delete from customers %'); It should return: +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | event_time | command_type | argument | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | 2023-02-20 22:13:15.400178 | Query | insert into customers (created_at, first_name, last_name, updated_at) values ('2023-02-20 22:13:15', 'Ivan', 'Franchin', '2023-02-20 22:13:15') | | 2023-02-20 22:14:33.019875 | Query | update customers set created_at='2023-02-20 22:13:15', first_name='Ivan2', last_name='Franchin2', updated_at='2023-02-20 22:14:33' where id=1 | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+Note: During an update, Hibernate/JPA does a select before performing the record update. So, you should see another select on one of the slaves. 
- 
Finally, let's DELETEthecustomer 1. For it, go to thecurlterminal and run:curl -i -X DELETE http://localhost:8080/api/customers/1 
- 
Running the following SELECTinside themysql-masterterminal:SELECT event_time, command_type, SUBSTRING(argument,1,250) argument FROM mysql.general_log WHERE command_type = 'Query' AND (argument LIKE 'insert into customers %' OR argument LIKE 'select c1_0.id%' OR argument LIKE 'update customers %' OR argument LIKE 'delete from customers %'); It should return: +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | event_time | command_type | argument | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | 2023-02-20 22:13:15.400178 | Query | insert into customers (created_at, first_name, last_name, updated_at) values ('2023-02-20 22:13:15', 'Ivan', 'Franchin', '2023-02-20 22:13:15') | | 2023-02-20 22:14:33.019875 | Query | update customers set created_at='2023-02-20 22:13:15', first_name='Ivan2', last_name='Franchin2', updated_at='2023-02-20 22:14:33' where id=1 | | 2023-02-20 22:14:52.358207 | Query | delete from customers where id=1 | +----------------------------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------+Note: As with an update, during a deletion, Hibernate/JPA performs a select before deleting the record. So, you should see another select in one of the slaves. 
- To stop the customer-apiapplication, go to the terminal where it's running and pressCtrl+C;
- In order to get out of the MySQL Monitorstypeexit;
- To stop and remove MySQLs andProxySQLcontainers, network and volumes, make sure you are inside thespringboot-proxysql-mysqlroot folder and run the following script:./shutdown-environment.sh 
