Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxime-Cllt committed Sep 20, 2024
0 parents commit ae37be1
Show file tree
Hide file tree
Showing 14 changed files with 802 additions and 0 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Go CI

# Le workflow s'exécute sur chaque push ou pull request vers les branches principales
on:
workflow_dispatch:
inputs:
gosqlcleaner:
description: 'Version du build'
required: true

jobs:
build:
# Utilisation de la machine virtuelle ubuntu-latest
runs-on: ubuntu-latest

steps:
# Vérifier le code depuis GitHub
- name: Checkout code
uses: actions/checkout@v3

# Installer Go
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.23.1' # Spécifiez ici la version de Go que vous utilisez

# Installer les dépendances Go (go.mod et go.sum doivent être présents)
- name: Install dependencies
run: go mod download

- name: Build
run: go build -v .

- name: Creating release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.inputs.gosqlcleaner }}
name: ${{ github.event.inputs.gosqlcleaner }}
draft: false
prerelease: false
files: |
package/linux/*
package/windows/*
token: ${{ secrets.GITHUB_TOKEN }}

5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/docker-compose.yaml
config.json
go.sum
.postgres_data
.idea
102 changes: 102 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<div align=center>
<img src="/assets/dbcleaner.png" width="100px" height="100px" alt="DBCleaner" align="center" />
<h1>DBCleaner</h1>
</div>

## Description

DBCleaner is a program made to be run on the backend of a server or an application to clean the database. It will reduce
the storage of the database and optimise all tables except system tables. It is a simple and efficient way to keep your
database clean and optimised without having to do it manually. It is a great way to keep your database running in the
best
conditions possible. Using Go language, it is compatible with all platforms and can be run on any server or application.

## Features

<ul>
<li>Reduce storage of the database</li>
<li>Optimise all tables except system tables</li>
<li>Simple and efficient way to keep your database clean</li>
<li>Compatible with all platforms</li>
<li>Maintain your database in the best conditions possible</li>
<li>Don't require any dump or backup</li>
<li>Easily run on any server or application</li>
<li>Easy to use</li>
</ul>

## Supported Databases

- [x] MySQL
- [x] MariaDB
- [x] PostgreSQL

## Platforms & Requirements

<div align="center">
<img src="https://img.shields.io/badge/OS-MacOS-informational?style=flat&logo=apple&logoColor=white&color=53a863" alt="MacOS" />
<img src="https://img.shields.io/badge/OS-Linux-informational?style=flat&logo=linux&logoColor=white&color=53a863" alt="Linux" />
<img src="https://img.shields.io/badge/OS-Windows-informational?style=flat&logo=windows&logoColor=white&color=53a863" alt="Windows" />
</div>

<div align="center">
<img src="https://img.shields.io/badge/Golang-1.16-informational?style=flat&logo=go&logoColor=white&color=53a863" alt="Golang" />
</div>

## Installation

To run the program :

1. Clone the repository:

```bash
git clone https://github.com/Maxime-Cllt/GoSqlCleaner.git
```

2. Import the libraries:

```bash
go mod tidy
```

3. Compile the program:

```bash
go build -o GoSqlCleaner
```

4. Run the program with the following your database information:

You need to create a file named `config.json` in the same directory as the program with the following content:

```json
{
"driver": "mysql",
"host": "localhost",
"port": "3306",
"username": "root",
"password": "password",
"database": "testdb"
}
```

Then run the program with the following command:

### MacOS & Linux

```bash
./GoSqlCleaner
```

### Windows

```bash
GoSqlCleaner.exe
```

## Notes

- Time complexity: O(n) where n is the number of tables in the database
- Don't clean triggers, stored procedures, functions, and views
- May not reduce much storage but don't cost much time to run and can be run frequently
- Require some privileges to connect to the database and to perform the cleaning

Binary file added assets/dbcleaner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions cleaner/cleaner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package cleaner

type Cleaner interface {
Clean() bool
}
193 changes: 193 additions & 0 deletions cleaner/mariadb/mariadb_cleaner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package mariadb

import (
"GoSqlCleaner/database"
"GoSqlCleaner/util"
"database/sql"
"fmt"
"log"
)

type MariaDbCleaner struct {
database.Database
}

func (c *MariaDbCleaner) Clean() bool {

// Connexion à la base de données MySQL
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/", c.Username, c.Password, c.Host, c.Port)

db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal("Erreur lors de la connexion à la base de données:", err)
}
defer db.Close()

err = db.Ping()
if err != nil {
log.Fatal("Impossible de se connecter à la base de données:", err)
}

totalSize := getTotalSizeSql()
startSize := util.GetDbSize(db, totalSize)

println("Start size of the database:", util.Green, startSize, " bytes", util.Reset)

// Set global variables OFF
println("Setting global variables OFF...")
setGlobalVariablesOFF(db)

// Rebuild index
println("Rebuilding index...")

// Repair tables
println("Repairing tables...")
repairTables(db)

// Clean all tables
println("Cleaning all tables...")
cleanAllTables(db)

// Clear logs
println("Clearing logs...")
clearLogs(db)

// Set global variables ON
println("Setting global variables ON...")
setGlobalVariablesON(db)

endSize := util.GetDbSize(db, totalSize)
println("End size of the database:", util.Green, endSize, " bytes", util.Reset)
println("Optimization of ", util.Green, startSize-endSize, util.Reset, " bytes")

return true
}

func rebuildIndex(db *sql.DB) {
// Rebuild index
rows, err := db.Query("SELECT CONCAT('ALTER TABLE `', TABLE_SCHEMA, '`.`', TABLE_NAME, '` ENGINE=InnoDB') AS stmt FROM information_schema.TABLES WHERE ENGINE = 'InnoDB' AND TABLE_SCHEMA NOT IN ('sys', 'performance_schema', 'information_schema', 'mysql')")
if err != nil {
log.Fatal("Erreur lors de la récupération des tables:", err)
}
defer rows.Close()

// iterate over the result
var stmt string
for rows.Next() {
err := rows.Scan(&stmt)
if err != nil {
log.Fatal("Erreur lors de la lecture de la ligne:", err)
}
_, err = db.Exec(stmt)
if err != nil {
log.Fatal("Erreur lors de l'exécution de la requête:", err)
}
}
}

func repairTables(db *sql.DB) {
// repair tables
rows, err := db.Query("SELECT CONCAT('REPAIR TABLE ',TABLE_SCHEMA, TABLE_NAME, ' EXTENDED') FROM information_schema.TABLES WHERE ENGINE IN ('MyISAM', 'ARCHIVE', 'CSV') AND TABLE_SCHEMA NOT IN ('sys', 'performance_schema', 'information_schema', 'mysql');")
if err != nil {
log.Fatal("Erreur lors de la récupération des tables:", err)
}
defer rows.Close()

var stmt string
for rows.Next() {
err := rows.Scan(&stmt)
if err != nil {
log.Fatal("Erreur lors de la lecture de la ligne:", err)
}
_, err = db.Exec(stmt)
if err != nil {
log.Fatal("Erreur lors de l'exécution de la requête:", err)
}
}
}

func cleanAllTables(db *sql.DB) {
// clean all tables
rows, err := db.Query("SELECT CONCAT('`',TABLE_SCHEMA,'`.`', TABLE_NAME, '`') AS stmt FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')")
if err != nil {
log.Fatal("Erreur lors de la récupération des tables:", err)
}
defer rows.Close()

var stmt string
for rows.Next() {
err := rows.Scan(&stmt)
if err != nil {
log.Fatal("Erreur lors de la lecture de la ligne:", err)
}
_, err = db.Exec("ANALYZE TABLE " + stmt)
if err != nil {
log.Fatal("Erreur lors de l'exécution de la requête:", err)
}

_, err = db.Exec("OPTIMIZE TABLE " + stmt)
if err != nil {
log.Fatal("Erreur lors de l'exécution de la requête:", err)
}
}
}

func getTotalSizeSql() string {
return "SELECT SUM(data_length + index_length) AS 'size' FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')"
}

func clearLogs(db *sql.DB) {
list := []string{
"FLUSH LOGS",
"PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 30 DAY)",
"FLUSH PRIVILEGES",
"FLUSH TABLES",
"FLUSH TABLES WITH READ LOCK",
"UNLOCK TABLES",
"FLUSH STATUS",
"RESET MASTER",
"RESET SLAVE",
}

for _, cmd := range list {
_, err := db.Exec(cmd)
if err != nil {
log.Fatal("Erreur lors de l'exécution de la requête:", err)
}
}
}

func setGlobalVariablesOFF(db *sql.DB) {
globalVariables := []string{
"SET GLOBAL general_log = 'OFF'", // Disable general log to avoid performance issues during cleaning
"SET GLOBAL slow_query_log = 'OFF'", // Disable slow query log to avoid performance issues during cleaning
"SET GLOBAL log_output = 'TABLE'", // Set log output to table to avoid performance issues during cleaning
"SET GLOBAL log_queries_not_using_indexes = 'ON'", // Enable logging of queries not using indexes
"SET GLOBAL log_slow_admin_statements = 'ON'", // Enable logging of slow admin statements
"SET GLOBAL log_slow_slave_statements = 'ON'", // Enable logging of slow slave statements
}

for _, cmd := range globalVariables {
_, err := db.Exec(cmd)
if err != nil {
log.Fatal("Erreur lors de l'exécution de la requête:", err)
}
}
}

func setGlobalVariablesON(db *sql.DB) {
globalVariables := []string{
"SET GLOBAL general_log = 'ON'", // Enable general log to monitor database activities
"SET GLOBAL slow_query_log = 'ON'", // Enable slow query log to monitor slow queries
"SET GLOBAL log_output = 'FILE'", // Set log output to file to monitor database activities
"SET GLOBAL log_queries_not_using_indexes = 'OFF'", // Disable logging of queries not using indexes
"SET GLOBAL log_slow_admin_statements = 'OFF'", // Disable logging of slow admin statements
"SET GLOBAL log_slow_slave_statements = 'OFF'", // Disable logging of slow slave statements
}
for _, cmd := range globalVariables {
_, err := db.Exec(cmd)
if err != nil {
log.Fatal("Erreur lors de l'exécution de la requête:", err)
}
}
}
Loading

0 comments on commit ae37be1

Please sign in to comment.