set up

secure installation:

sudo /usr/bin/mysql_secure_installation

fondamentale considerare le opzioni spiegate nel manuale

enable query cache:

/etc/mysql/my.cnf

query_cache_size = 20M
query_cache_type=1
query_cache_limit=1M

default Memory size in Kb. needs

/etc/init.d/mysql restart

test and maintanance

SHOW VARIABLES LIKE '%query_cache%';
SHOW VARIABLES LIKE 'query_cache_size';
SHOW STATUS LIKE '%qcache%';
 
# provare valori senza configurare/riavviare
SET GLOBAL query_cache_size = "30M";
 
FLUSH QUERY CACHE;
 
SHOW processlist;
SHOW full processlist;

installare mytop per profilare:

apt-get --yes install mytop

permessi utente

GRANT ALL PRIVILEGES ON $db TO $user@localhost;
GRANT ALL ON $db.* TO '$user'@'localhost';
# es. dare permesso di accedere dall'esterno a root
GRANT ALL ON mydb.* TO root@'%' IDENTIFIED BY 'MyPASSWORD';
 
# applica le modifiche
FLUSH PRIVILEGES;
 
SHOW GRANTS FOR '$user'@'localhost';

resettare la password di root:

mysqld_safe --skip-grant-tables &
# successivamente ripartire e reimpostare la password come al solito

script test connessione:

<?php
$u 
='aaa';
$p 'bbb';
$a_db=[ 'db1''db2' ];
foreach(
$a_db as $db){
    
$mysqli = new mysqli("localhost"$u$p$db);
    if (
$mysqli->connect_errno) {
        echo 
"Failed to connect to MySQL: (" $mysqli->connect_errno ") " $mysqli->connect_error;
    }
    echo 
'OK DB:'.$db."<br>\n";
    
$mysqli->close();
}

setup PHP connect to a mysql's unix socket, saving the TCP overhead.

// unix socket you need to use
// UTF-8 charset you can specify that in the DSN.
// You musnt not specify host when using socket.
$dsn = 'mysql:dbname=testdb;unix_socket=/path/to/socket';
$link = new PDO("$dns;dbname=DB;charset=UTF8");

allow remote connections

per prima cosa abilitare le connessioni esterne nella configurazione del server e riavviare

vi /etc/mysql/my.cnf
 
# default, secure
# bind-address = 127.0.0.1
# bind-all address
bind-address = 0.0.0.0

assicurarsi l'utente amministrativo sia abilitato alle connessioni esterne: @'%'

GRANT ALL PRIVILEGES ON *.* TO 'admin-usr'@'%' IDENTIFIED BY '$password' WITH GRANT OPTION;
FLUSH PRIVILEGES;

assicurarsi che il server stia accettando connessioni:

telnet 192.168.1.X 3306

in caso contrario il firewall sta bloccando le connessioni al servizio.

interesting queries

-- datetime
DELETE FROM x_log WHERE date_insert < DATE_SUB(NOW(), INTERVAL 14 DAY);
DELETE FROM x_history where update_date < (CURDATE() - INTERVAL $days DAY);
SELECT DATE_SUB(date_time_column, INTERVAL 1 WEEK) FROM x_table;
 
--modificare una foreign key
ALTER TABLE car_image DROP FOREIGN KEY `fk_car_images_car`;
ALTER TABLE `car_image` DROP INDEX `fk_car_images_car` ;
-- tutti i campi di una specifica tabella/db
select COLUMN_NAME from information_schema.columns where table_schema = 'your_db' and TABLE_NAME='articoli' order by table_name,ordinal_position

FOREIGN KEY

TO ADD A FOREIGN KEY TO AN EXISTING TABLE Where "favgenre" is the column of the table that has the foreign key and products_genre(gid) is the table and primary key you are referencing.

alter table users add foreign key(favGenre) references products_genre(gid);
 
ALTER TABLE car_image ADD CONSTRAINT `fk_car_images_car` FOREIGN KEY (`car_id`) REFERENCES `car` (`car_id`) ON DELETE CASCADE ON UPDATE NO ACTION;

funzioni sulle stringhe

date, date time, timestamp

you cannot set the default for a date column to be the value of a function such as NOW() or CURRENT_DATE

 
# 5.1 syntax
ALTER TABLE `table_name` MODIFY COLUMN `column_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
 
INSERT INTO Yourtable (Field1, YourDateField) VALUES('val1', NOW())
 
 
# TRIGGER
CREATE TRIGGER trigger_foo_SetCreatedAt BEFORE INSERT ON foo FOR EACH ROW IF NEW.created_at IS NOT NULL SET NEW.created_at = UTC_TIMESTAMP();
 
# 5.5 syntax
CREATE TABLE foo (
    `creation_time`     DATETIME DEFAULT CURRENT_TIMESTAMP,
    `modification_time` DATETIME ON UPDATE CURRENT_TIMESTAMP
 
    `update_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)

Old Password

dalla versione 4.1 è cambiata la gestione delle password, alcuni client hanno linkato staticamente la libreria di connessione che usa il vecchio metodo per cui non sono in grado di inviare la nuova pasword risultando in un errore tipo

Client does not support authentication protocol requested by server; consider upgrading MySQL client

occorre impostare il server per riconoscere una password nel vecchio formato per quel l'utente da cui si tenta la connessione.

UPDATE mysql.user SET Password = OLD_PASSWORD('newpwd')
WHERE Host = 'tuo_host' AND User = 'nome_user'

oppure

SET PASSWORD FOR 'some_user'@'some_host' = OLD_PASSWORD('newpwd');

my.ini assicurarsi che contenga:

#Use old password encryption method (needed for 4.0 and older clients).
old-passwords

connessioni pendenti

se non chiuse correttamente le connessioni rimangono aperte, di default vengono chiuse dopo 8 ore, ma si può abbassare a 1h il timeout

  1. nano /etc/my.cnf
wait_timeout = 3660
key_buffer_size = 2M

connessioni pendenti, server rallentato da troppe connessioni, eliminare chiudere connessioni e query troppo onerose

-- show status like '%onn%';
SHOW processlist;
-- KILL CONNECTION  2628317;
SHOW processlist;

<=> NULL-safe equal to operator

come l'operatore = na non ritorna mai NULL, ritorna 1 invece che NULL se entrambe gli operandi sono NULL, ritorna 0 invece che NULL se un operando è NULL

test query:

SELECT 1 <=> 1, NULL <=> NULL, 1 <=> NULL;
SELECT 1 = 1, NULL = NULL, 1 = NULL;

testare per un valore null:

campo IS NOT NULL

Case

lower_case_table_names

How table and database names are stored on disk and used in MySQL is affected by the lower_case_table_names system variable, which you can set when starting mysqld. lower_case_table_names can take the values shown in the following table. On Unix, the default value of lower_case_table_names is 0. On Windows the default value is 1. On Mac OS X, the default value is 2.

1: Table names are stored in lowercase on disk and name comparisons are not case sensitive. MySQL converts all table names to lowercase on storage and lookup. This behavior also applies to database names and table aliases.

http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html

UTF8

 
// settare il charset a livello di connessione
mysql_set_charset('utf8', $link);
$mysqli->set_charset("utf8")
// settare il charset a livello del db
mysql_query("SET character_set_results = 'utf8', character_set_client = 'utf8', character_set_connection = 'utf8', character_set_database = 'utf8', character_set_server = 'utf8'");
 
mysql_query("set names UTF-8;");

ricordare inoltre: in php.ini use UTF-8 as the default character set:

default_charset = "utf-8";

assicura che la risposta sia interpretata utf8

header('Content-Type: text/html; charset=utf-8');
<
meta http-equiv="Content-Type" content="text/html; charset=utf-8">

usa solo funzioni utf8 safe

CLI

bin/mysql -u root
bin/mysqldump -u root -p dbname > dbname_backup.sql
 
# mysql vuole caratteri ansi
# HeidiSql formatatta utf-8
iconv --verbose -t ISO-8859-1 -f UTF-8 $nome_file -o $nome_file_ansi

schema compare

mysqldump -h localhost -u root -p --no-data dbname1>file1
mysqldump -h localhost -u root -p --no-data dbname2>file2
meld file1 file2

restore di un database:

//  ricarica un database partendo da un dump
class CLI_Mysql {
    public static function 
db_restore($db_user $db_passwd$db_name$dump_filename){
        `
mysql -u $db_user -p $db_passwd -e "DROP DATABASE IF EXISTS $db_name"; `;
        `
mysql -u $db_user -p $db_passwd -e "CREATE DATABASE $db_name";         `;
        `
mysql -u $db_user -p $db_passwd $db_name < $dump_filename              `;
    }
}

operazioni di mantenimento

  • ogni volta che le dimensioni del db raddoppiano

isamcheck -a

  • 2 volte all'anno, per mantenere i blocchi contigui

isamcheck -d

  • 1 volte l'anno o 1 volta al mese se le eliminazioni sono frequenti, per ricostruire le tabelle e mantenere i record contigue

isamcheck -r

optimize table su tutte le tabelle di un db

mysql -u uname dbname -e "show tables" | grep -v Tables_in | grep -v "+" | gawk '{print "optimize table " $1 ";"}' | mysql -u uname dbname

Backup Dump/Restore

Dump ALL Databases

mysqldump --user=$USER --password=$PASSWORD -A > /path/to/dumpfile.sql
Dump Individual or Multiple Databases

Dump only certain tables from a Database

mysqldump --user=$USER --password=$PASSWORD --databases DB_NAME1 DB_NAME2 > /path/to/dumpfile.sql

Restore A Database

mysql --verbose --user=$USER --password=$PASSWORD DB_NAME < /path/to/dumpfile.sql

Restore All Databases

mysql --verbose --user=$USER --password=$PASSWORD < /path/to/dumpfile.sql
#dump all databases in separate file
for I in `echo "show databases;" | mysql | grep -v Database`; do mysqldump $I > "$I.sql"; done
 
# dump all databases
mysqldump --all-databases -u**** -p***** > $dump_filenames
 
# restore: @see db_restore
 
# eliminare files + vecchi di 3 giorni
find /var/backup/mysql -type f -mtime +3 -exec rm {} \;
#full backup every morning
0 6 * * * mysqldump --all-databases -u**** -p***** > /var/backups/full-`date +%F`.sql
 
# dump a table evry hour
0 * * * * mysqldump shop cust_order > /backup/cust_order_backup-`date +%F_%R`.sql

batch foreign key dependency checking :

#!/bin/sh
if [ -z "$1" ] then
    echo "\nUsage:\n\t./`uname $0` <database> [-h <host>] [-u user] [-p <passwd>]\n"
    exit
fi
 
CONSTRAINTS=`mysqldump $* | grep "CREATE\|CONSTRAINT" | sed 's/ /+/g'`
 
for c in $CONSTRAINTS do
    if [ "`echo $c | cut -d '+' -f 3`" = "CONSTRAINT" ] then
        CONSTRAINT=`echo $c | cut -d '+' -f 4 | tr -d '\`'`
        CHILD_KEY=`echo $c | cut -d '+' -f 7 | tr -d '()\`,'`
        PARENT_TABLE=`echo $c | cut -d '+' -f 9 | tr -d '\`'`
        PARENT_KEY=`echo $c | cut -d '+' -f 10 | tr -d '()\`,'`
        QUERY="select c.$CHILD_KEY from $CHILD_TABLE as c left join $PARENT_TABLE as p on p.$PARENT_KEY=c.$CHILD_KEY where c.$CHILD_KEY is not null and p.$PARENT_KEY is null;"
        echo "Checking table '$CHILD_TABLE' constraint '$CONSTRAINT'"
        mysql -verbose $* -e "$QUERY"
    else
        CHILD_TABLE=`echo $c | cut -d '+' -f 3`
    fi
done

slow query log , optimization

mysql -u root -p
 
SET GLOBAL slow_query_log = 'ON';
 
# additional options:
 
# By default logs query that takes longer than 10 s.
SET GLOBAL long_query_time = 20;
 
# the log file is located at /var/lib/mysql/hostname-slow.log
SET GLOBAL slow_query_log_file = '/path/filename';
 
# To verify, log out of the mysql program, and then log back in to reload the session
SELECT SLEEP(21);

debug e optimization

SHOW FULL PROCESSLIST;
 
SHOW ENGINE INNODB STATUS;
 
SHOW WARNINGS;

use MySQLTuner

mysqltuner

configuration:

less /etc/mysql/conf.d/mysql.cnf

Check the status of server regarding RAM and available disk space:

free -m
df -h

If you have memory available, increase the innodb_buffer_pool_size so that there is more caching.

fragmented tables: Run OPTIMIZE TABLE and defragment all for better performance

Have a look in your slow_query.log, Check

Query_time
Lock_time
Rows_sent
Rows_examined

If you find queries where ratio of Rows Sent / Rows Examined set is high, then those query is good for optimization. It may be possible that some queries in slow_query.log is utilizing a lot of CPU resources.

Your READ:WRITE ratio (eg. 61:39 ) gives the clue if engine types should be changed to Innodb. do you see queries in LOCK state.

Table locks: big limitation of MyISAM. If you have a huge number of concurrent writes and selects and query performance must be consistently fast, Innodb is the only choice due to locking mechanism (row-level)

others tune parameters in MySQL configuration

innodb_fast_shutdown=0
innodb_log_buffer_size
innodb_log_file_size
innodb_flush_method=O_DIRECT
query_cache_size=0

loook anamaly in tables

SHOW TABLE STATUS FROM database_name;
 
## list the SIZE OF every TABLE IN every DATABASE, largest FIRST:
SELECT
table_schema AS `Database`,
TABLE_NAME AS `Table`,
round(((data_length + index_length) / 1024 / 1024), 2) `Size in MB`
FROM information_schema.TABLES
ORDER BY (data_length + index_length) DESC;
##
OPTIMIZE TABLE tableName;
## TO OPTIMIZE ALL TABLES ON your server IN ALL DATABASES you can USE e.g. commandline command:
mysqlcheck --all-databases --optimize --skip-write-binlog

use query EXPLAIN on anomalous querys id (query id) select_type (type of statement) table (table referenced) type (join type) possible_keys (which keys could have been used) key (key that was used) key_len (length of used key) ref (columns compared to index) rows (amount of rows searched) Extra (additional information)

DB Engines

MyISAM (Default engine di MySQL)

Sono le tabelle storiche di MySQL, derivanti direttamente dalle vecchie ISAM che sono dismesse ormai da anni. Garantiscono affidabilità e velocità. Su versione di MySQL più vecchie della 4.1 sono sicuramente da preferirsi alle INNODB.

Forniscono un vantaggio: la possibilità di poter utilizzare indici FULLTEXT per ricerche con ranking stile Google.

Il metodo di salvataggio dei dati è basato sulla costruzione e lavorazione di 3 file binari:

  • frm: Struttura della tabella
  • MYD: File contenente tutti i dati della tabella
  • MYI: File contenente i dati relativi agli indici del database

Per maggiori informazioni: http://dev.mysql.com/doc/refman/5.0/en/myisam-storage-engine.html

è possibile fare un backup diretto dei file binari per l'importazione/esportazione della base dati. Con le tabelle INNODB è invece molto complesso in quanto i dati di TUTTI i database presenti sul server e che utilizzano tabelle INNODB vengono normalmente salvati nel file ibdata1.

Anche volendo salvare i file dei dati ognuno per tabella le transazioni e gli indici vengono comunque salvati nel TABLESPACE e quindi il recovery mediante import di tutti i file presente per esempio sotto: /var/lib/mysql/miodb/ NON FUNZIONA!

INNODB

  • Foreign Key

Le tabelle INNODB sono in grado di gestire l'integrità referenziale tra le chiave esterne del database, ora con il motore INNODB è possibile delegare questa annosa attività a MySQL stesso, potendo quindi specificare vari comportamenti a seconda della chiave esterna utilizzata.

  • Transazioni

Le transazioni sono fondamentali in situazioni tipiche da shop online dove sino a che non arriva conferma da parte della banca o di chi valida la carta di credito, le query non devono essere "eseguite realmente". Fondamentalmente si tratta di eseguire una serie di query che verranno validate o annullate mediante chiamate COMMIT o ROLLBACK.

  • Mancanza - FULLTEXT

al momento non è possibile dichiarare un campo come indice fulltext per poter eseguire ricerche con score

http://dev.mysql.com/doc/refman/5.0/en/innodb-overview.html

Come scegliere lo storage delle tabelle

conviene realizzare tabelle MYISAM o INNODB? dipende dai requirements dell'applicazione.

Mysql Utils

This package provides the following utilities:
 mysqldbcompare   : check two databases and identify any differences
 mysqldbcopy      : copy databases from one MySQL server to another
 mysqldbexport    : export a list of databases in a variety of formats
 mysqldbimport    : import object definitions or data into a database
 mysqldiff        : identify differences among database objects
 mysqldiskusage   : show disk usage for one or more databases
 mysqlfailover    : automatic replication health monitoring and failover
 mysqlindexcheck  : check for redundant or duplicate indexes
 mysqlmetagrep    : search MySQL servers for objects matching a pattern
 mysqlprocgrep    : search MySQL servers for processes matching a pattern
 mysqlreplicate   : setup replication among two MySQL servers
 mysqlrpladmin    : administration utility for MySQL replication
 mysqlrplcheck    : check prerequisities for replication
 mysqlrplshow     : show slaves attached to a master
 mysqlserverclone : start a new instance of an existing MySQL server
 mysqlserverinfo  : display common diagnostic information from a server
 mysqluserclone   : copy a MySQL user to new user(s) on another server

v 5.7.5 full_group_by

errore: of SELECT list is not in GROUP BY clause and contains nonaggregated column 'xxx' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

MySQL 5.7.5+ changed the way GROUP BY behaved in order to be SQL99 compliant (where in previous versions it was not).

check sql_mode variable of your server to ensure the flag is active.

sql_mode=only_full_group_by

solution: configure mysql with the previous behaviour on group by:

sudo nano /etc/mysql/my.cnf
[mysqld]
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
 
sudo service mysql restart

or run the query per evry connection

SET sql_mode=(SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', ''));

generic methods:

 
-- adding only one mode to sql_mode
SET sql_mode=(SELECT CONCAT(@@sql_mode, ',<mode_to_add>' ));
 
-- removing only a specific mode
SET sql_mode=(SELECT REPLACE(@@sql_mode, '<mode_to_remove>' ,''));

Full-Text search

MySQL offers FULLTEXT searching. It searches tables with columns containing text for the best matches for words and phrases.

Simple FULLTEXT search

SET @searchTerm= 'Database Programming';
SELECT
MATCH (Title) AGAINST (@searchTerm IN NATURAL LANGUAGE MODE) Score, ISBN, Author, Title
FROM book
WHERE MATCH (Title) AGAINST (@searchTerm IN NATURAL LANGUAGE MODE)
ORDER BY MATCH (Title) AGAINST (@searchTerm IN NATURAL LANGUAGE MODE) DESC;

Given a table named book with columns named ISBN , 'Title', and 'Author', this finds books matching the terms 'Database Programming' . It shows the best matches first. For this to work, a fulltext index on the Title column must be available:

ALTER TABLE book ADD FULLTEXT INDEX Fulltext_title_index (Title);

Simple BOOLEAN search

SET @searchTerm= 'Database Programming -Java';
SELECT MATCH (Title) AGAINST (@searchTerm IN BOOLEAN MODE) Score,
ISBN, Author, Title
FROM book
WHERE MATCH (Title) AGAINST (@searchTerm IN BOOLEAN MODE)
ORDER BY MATCH (Title) AGAINST (@searchTerm IN BOOLEAN MODE) DESC;

Given a table named book with columns named ISBN , Title , and Author , this searches for books with the words 'Database' and 'Programming' in the title, but not the word 'Java' . For this to work, a fulltext index on the Title column must be available:

ALTER TABLE book ADD FULLTEXT INDEX Fulltext_title_index (Title);

Multi-column FULLTEXT search

SET @searchTerm= 'Date Database Programming';
SELECT MATCH (Title, Author) AGAINST (@searchTerm IN NATURAL LANGUAGE MODE) Score,
ISBN, Author, Title
FROM book
WHERE MATCH (Title, Author) AGAINST (@searchTerm IN NATURAL LANGUAGE MODE)
ORDER BY MATCH (Title, Author) AGAINST (@searchTerm IN NATURAL LANGUAGE MODE) DESC;

Given a table named book with columns named ISBN , Title , and Author , this finds books matching the terms 'Date Database Programming'. It shows the best matches first. The best matches include books written by Prof. C. J. Date. (But, one of the best matches is also The Date Doctor's Guide to Dating : How to Get from First Date to Perfect Mate. This shows up a limitation of FULLTEXT search: it doesn't pretend to understand such things as parts of speech or the meaning of the indexed words.) For this to work, a fulltext index on the Title and Author columns must be available:

ALTER TABLE book ADD FULLTEXT INDEX Fulltext_title_author_index (Title, Author);