Mysql

基础 #

介绍
    c++编写
端口
    3306
命令
    mysql
        --max-relay-logs-size=300           # 中继日志大小(sql语句数)
        --relay-log-purge={0|1}             # 中继日志自动清空
        --relay-log-space-limit=0           # 限制中继日志大小,0表示无限制

        o->
        mysql -h 127.0.0.1 -u root -p
    mysqldump
        -uroot
        -p
        -h127.0.0.1
        -P3306
        --force
        --all-databases                     # 所有库
        --databases db1 db2                 # 多库

        o->
        mysqldump -uroot -p db1 tb1> tb1.sql
    mysqladmin -uroot -p status             # 查看当前连接数
组件
    mysql enterprise monitor documentation
    mysql enterprise monitor connector
    mysql enterprise monitor service manager
    mysql enterprise monitor agent
    mysql enterprise backup
    mysql connector
    工具
        mysql database
        mysql cluster          # 创建集群,配置复杂
        mysql cluster manager  # cluster帮助软件,配置简单
        mysql workbench        # 据库建模工具
        mysql utilities        # 提供一组命令行工具用于维护和管理 MySQL 服务器
连接参数
    root:123456@tcp(abcdefg:3306)/meiqia?
        charset=utf8mb4,utf8&
        characterEncoding=UTF-8&
        loc=UTC&
        interpolateParams=true&
        time_zone=%27%2B00%3A00%27&
        sql_mode=%27NO_ENGINE_SUBSTITUTION%2CSTRICT_TRANS_TABLES%27
数据类型
    int             # int(5) zerofill
    varchar(20)     # null不占空间
    decimal(10,2)   # 小数
    char(10)        # 空间已固定, 不论null与否
    date
    bool或boolean
    double
    float
    longtext
    longblob
    timestamp       # 自动在插入、修改记录时添加,用于记录更新
    enum('male','female') default male  # 枚举,只有一个
    set(('football','sleep','film')     # 集合,可以多个
架构
    服务器
        连接管理与安全验证
        解析器     # 解析到缓存
            查询缓存(修改时清出缓存),分析查询语句,生成解析树
        优化器
            查询语句优化
                选索引
                读取方式
                获取查询开销信息
                统计信息
        执行器
            执行查询语句,返回结果
            生成执行计划
    缓存
        执行计划缓存
        数据缓存
    存储引擎
        缓存管理    # 管理缓存
        锁管理      # 管理执行器
        事务管理
        文件管理
            innodb
事务
    由存储引擎决定     # 与其它数据库产品不同
    默认自动提交
        # variables autocommit, 0 off 1 on
    一些命令强制自动提交
        DLL命令
        lock tables
目录
    /var/lib/mysql              # 默认数据库
    /var/log/mariadb            # 默认日志

引擎 #

XtraDB
Memory(Heap)
    # 之前叫Heap, 存到内存
NDB
    特点
        集群设计
        share nothing, 高可用,可扩展
        存到内存, 主键查找快
        join操作在数据库层完成,不是引擎完成。需要网络开销大,查询慢
Archive
    # 适合归档数据,只支持insert和select,提供高速插入和压缩功能
Federated
    # 不存数据, 指向远程表,类似oracle的透明网关
Maria
    # 开源,用于取代M主ISAM
MyISAM
    特点
        不支持事务
        表锁
        适用联机分析(OLAP)
        不缓存数据文件,只缓存索引文件
        占较少空间保存数据与索引
    表文件
        .frm    # 存储定义
        .MYD    # MYData 存储数据
        .MYI    # MYIndex 存储索引
InnoDB
    特点
        支持事务
        行锁
        支持外键
        支持非锁定读  # 类似oracle, 默认读不产生锁
        面向联机事务(OLTP)
        数据放在一个逻辑表空间中    # 类oracle
        实现4种隔离级别
            默认可重复读repeatable, 使用next-key locking的策略避免幻读
        索引组织表(Clustered)的方式进行存储     # 类oracle
        内存池维护并发线程
    注意
        自增id最大值放在内存中,重启后会再查找。MyISAM的自增id最大值在文件中,重启不丢失。

函数 #

count(*)
count(name)                        # 不统计null,所以不推荐,会漏数据
sum(english)/count(*) as 英语平均分
avg(chinese)
max(english)
min(english)
password('jiaoningbo')
concat('jiao','ning','bo')                concat(38.8) # 连接                转换数字到字符串
cast(38.8 as char)                        # 转换数字到字符串  同 concat(38.8) 但这个更正式
strcmp('text','text2')                # 比较字符串,相同则返回0,不同则返回-1
abs('-1')                                        # 绝对值
crc32('mysql')                                # 计算循环冗余码校验值并返回一个 32比特无符号值
pi()                                                # pi
floor(1.2)                                        # 取整 返回一个bigint
mod(29,9)                                        # 取摩
is                                                        # select 1 is true,0 is false,null is unknown,null is not unknown  结果 :1,1,1,0
coalesce(null,1)                        # 返回第一个非空值,全空则返回null
greatest(0,2,66,34)                        # 返回不定参数个数的 参数的最大值,个数为0时返回null
least(2,0)                                        # 返回不定参数个数的 参数的最小值,个数为0时返回null
1 xor 0 xor 1                                # 异或,两个1消为0
ascii('ab')                                        # 返回最左字符的ascii码
bin(13)                                                # 返二进制
bit_length('text')                        # 返回二进制长度
char_length('str')                        # 返回字符长度
left('foobar',5)                        # 截取左数前5个
right('foobar',4)                        # 截取右数前4个
locate('bar','foobar')                # 返回位置4
locate('bar','foobarbar',5)        # 返回从5开始数的子串的位置,这里是7
ltrim(str)                                        # 去左空格
rtrim(str)                                        # 去右空格
trim(str)                                        # 去空格
make_set(1|4,'hello','nice','world')                # 第一位是bit,1|4 表示1+4=5 即二进制101,所以显示结果为"hello,world", 0 输出空字符串
repeat('m',3)                                # 重复
replace('wb','w')                        # replaceAll
reverse(str)                                # 倒序
space(6)                                        # 输出6个空格
adddate('1998-01-02',31)        # 加日期  同 adddate('1998-01-02',interval 31 day)同 date_add('1998-01-02',interval 31 day)
addtime('1997-12-31 23:59:59.999999','1 1:1:1.000002')        #加时间 同 '1997-12-31 23:59:59.999999','1 1:1:1.000002');
curdate()+ 3                                # 当前日期 + 3 天
date(str)                                        # 提取日期时间表达字符串中的日期部分
date('1997-11-12','1997-11-11')                # 前面减后面 得到天数

系统库/数据字典 #

系统库
    information_schema
        # 默认内置元信息数据库
        INNODE_TRX
            # 当前开启的事务
    mysql
        # 内置安全设置数据库
    performance_schema
        # 资源消耗,资源等待等情况
    sys
        # 5.7后,数据来源performance_schema, 降低复杂度
    test
        # 5.7移除,内置测试数据库

触发器(trigger) #

new与old
    # 指代新数据
    insert只有new是合法的;
    delete只有old才合法;
    update可同时使用。
语句
    show triggers [from schema_name];
    drop trigger [if exists] [schema_name.]trigger_name
例1
    create trigger tr1
    after                   # before, after
    insert on tb1           # insert, update, delete
    for each row
    update tb2 set field1 = field1+char_length(new.name);
        # 当更新表tb1的name字段时,更新表tb2 field1加上name的长度
例2      # UPDATE同时使用NEW和OLD
    create trigger tr1
    before update on t22
    for each row
    begin
    set @old = old.s1;
    set @new = new.s1;
    end;

存储过程(stored procedure) #

# 5.0加入
结束符号(delimiter) //
语法
    create procedure 存储过程名(参数类型 参数名 参数数据类型)
    begin
            业务逻辑
    end//

    call pro1()//
        # 执行

    drop function pro1;

    语句
        # 所有sql语句都是合法的

        declare a int default 2;

        set @name = 'admin';
            # @name 为局部变量        @@name 为全局变量

        if
        then
        else
        end if;

        case variable1
        when 0 then
        when 1 then
        else
        end case

        while true do
        end while

        loop_label:LOOP
        LEAVE[ITERATE] loop_label;
        end loop [end loop_label]        # iterate是迭代

        LABEL label_name
        GOTO label_name

        repeat until
        end repeat # 执行后检查结果

存储过程名
    大小写不敏感
    可以包含空格,最长为64位
    最好数据库名.过程名
    不要使用内建函数名
参数类型
    IN参数    # 不修改传递进来的参数
    OUT参数   # 参数无法传进来,只修改参数
    INOUT参数 # 可读可写

特征子句(characteristics clauses)
查看
    show procedure status;
    show create procedure proc_name;
    show create function func_name;
    

异常
    DECLARE
    { EXIT | CONTINUE }
    HANDLER FOR
    { error-number | { SQLSTATE error-string } | condition }
    SQL statement

    DECLARE EXIT HANDLER FOR 1216
    DECLARE CONTINUE HANDLER FOR not found        # 写在存储过程的begin后,当前程序出错后会自动触发代码 MySQL允许两种处理器,
                                                                                    ## 一种是EXIT处理,上面所用的就是这种。另一种就是我们将要演示的,CONTINUE处理,
                                                                                    ## 它跟EXIT处理类似,不同在于它执行后,原主程序仍然继续运行
    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; # 如果下面将@x2的值赋为1就会出错
    DECLARE EXIT HANDLER FOR `Constraint Violation` ROLLBACK;        # 回滚

    DECLARE `Constraint Violation` CONDITION FOR SQLSTATE '23000';
    DECLARE EXIT HANDLER FOR `Constraint Violation` ROLLBACK;                        # 先声明条件

游标
    declare cur_1 cursor for select s1 from t;
    open cur_1
    fetch cur_1 into a
    close cur_1

函数
    # 与存储过程唯一不同必须有RETURN语句
    # 不能在函数中访问表
                
    function example1:
    create function myadd(num1 int, num2 int)
    returns int
    begin
    return num1 + num2;
    end//

                
例1
    create procedure pro(in name varchar(20))
    begin
    select name;
    set name='hello world';
    select name;
    end//

    set @name='jnb'//
    call pro(@name)//

例2      # if语句,根据输入0/1 判断 男/女
    create procedure findGender(in op int)
    begin
    declare gendar varchar(20);                                # 在函数中定义变量gendar和其类型
    if op=0 then                                                        # =表示判断
    set gendar='male';
    else set gendar='female';
    end if;
    select gendar;
    end//

例3      # switch语句,判断星期
    create procedure findday(in day int)
    begin
    declare findday varchar(20);
    case day
    when 1 then set findday='星期一';
    when 2 then set findday='星期二';
    when 3 then set findday='星期三';
    when 4 then set findday='星期四';
    when 5 then set findday='星期五';
    when 6 then set findday='星期六';
    when 7 then set findday='星期七';
    else set findday='无效';
    end case;
    select findday;
    end//
        
例4      # while循环
    create procedure findsum(in n int)
    begin
    declare sum int default 0;
    declare i int default 0;
    while i<=n do
    set sum=sum+i;
    set i=i+1;
    end while;
    select sum;
    end//
        
例5      # 验证登录
    create procedure prologin(in user_name varchar(20), in user_psw varchar(100), out flag int)
    begin
    if exists(select name from users where name=user_name and psw=password(user_psw)) then
            set flag = 1;
    else
            set flag = 0;
    end if;
    end//

配置 #

o-> my.cnf文件
[client]

port=3306
socket=/tmp/mysql.sock

[mysql]
default-character-set=gbk

[mysqld]
character-set-server=utf8

port=3306
socket=/tmp/mysql.sock
log-bin=mysql-bin
server-id=1
skip-name-resolve
        # 远程访问时非常慢解决
innodb-flush-log-at-trx-commit=2
innodb_log_file_size=268435456
sync-binlog=1
        # 这两个配置为了使用事务的InnoDB在复制中最大的持久性和一致性
master-connect-retry=60
        # 从服务器断开重连时间
binlog-do-db=test
        # 主从都可以设置,复制的数据库
binlog-ignore-db=mysql
        # 主从都可以设置,不复制的数据库
lower_case_table_names=1
        # 设置大少写不敏感
interactive_timeout=3600
sql_mode=IGNORE_SPACE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

安全性 #

跨域访问,使用ssh隧道加密通信
set password设置密码
用grant和revoke对用户授权
    少开权限
    只有启动mysql的用户有写权限
    只授权process和super权限给管理用户
        mysqladmin processlist 可列举当前执行的查询
        super 可切断连接,改变服务器运行参数,控制从库
    不信任dns时,权限表只设置ip
常见攻击
    防偷听
    篡改
    回放
    拒绝服务
acl控制接口权限
设置只有root可访问mysql数据库和user表
不用明文密码,密码强度高
数据库放在防火墙后,或在DMZ(demilitarized zone, 隔离区)
防火墙设置3306端口不可访问
sql预编译,避免sql注入
存数据时检查大小
以普通用户启动mysql
tcpdump检查传输数据的安全性
    tcpdump -l -i eth0 -w -src or dst port 3306 strings
max_user_connections变量限制指定帐户连接数
打开mysqld安全开关
    --local-infile=0     # 0时客户端无法使用local load data
    --skip-grant-tables     # 对用户不做访问控制
        mysqladmin flush-privileges # 运行中开启访问控制
        mysqladmin reload           # 运行中开启访问控制
    --skip-show-databases   # 禁止show databases

方案 #

初始化
    systemctl start mariadb
    mysql_secure_installation
    etc/my.cnf文件
        [mysqld]
        default-storage-engine = innodb
        innodb_file_per_table
        max_connections = 4096
        collation-server = utf8_general_ci
        character-set-server = utf8
        [mysql]
        default-character-set=utf8
    systemctl restart mariadb
移数据库
    同mysql版本, 新目录下替换(/var/lib/mysql/)ibdata1、数据库名目录
    error: mysqld does not have the access rights to the directory. File name ./ibdata1
        chcon -R --reference=/var/lib/mysql 新目录
        或
        chcon -R -t mysqld_db_t -u system_u -r object_r 新目录
重初始化数据库
    rm -r /var/lib/mysql/*
    mysql_install_db
    chown mysql:mysql -R /var/lib/mysql
    systemctl restart mariadb
    mysql -uroot mysql
    update user set password=password('asdf') where user='root';
    flush privileges;
授权远程登录
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'asdf' WITH GRANT OPTION;
创建用户
    CREATE USER 'username'@'host' IDENTIFIED BY 'password';
删除用户
    DROP USER 'username'@'host';
免密登录
    mysql_safe --user=mysql --skip-grant-tables --skip-networking
修改密码
    问题
        error 1045(28000) access denied for user 'root'@'localhost' (using password:no)
    不能登录时
        mysqld_safe方式
            mysqld_safe --user=mysql --skip-grant-tables --skip-networking &
            mysql -u root mysql
            update user set password=password('newpassword') where user='root';
                    # 新版中password改为authentication_string
            flush privileges;
        mysqld_safe方式2
            创建a.sql
                update mysql.user set password=password('mysql') where user='修改用户';
                flush privileges
            mysqld_safe --defaults-file="a.sql"
        文件方式
            使用/etc/mysql/my.cnf中[client]下的user与password
    能登录时
        mysqladmin方式
            mysqladmin -uroot -p password 新密码
        update方式
            update mysql.user set password=password('root') where user='root';
            flush privileges;
        set方式
            SET PASSWORD FOR 'username'@'host' = PASSWORD('newpassword');
            flush privileges;
编码问题
    查询
        show variables like "%colla%"       # 字符串排序规则
        show variables like "%char%"
    修改
        set character_set_client='utf8'
    创建db时指定
        create database `db1` character set 'utf8' collate 'utf8_general_ci'
    创建表时指定
        create table (...) engine=innodb default charset=utf8
    查看db和表编码
        show database `db1`
        show create table `tb1`
    修改db和表编码
        alter database `db1` default character set utf8 collate utf8_general_ci
        alter table `tb1` default character set utf8 collate utf8_general_ci
导库
    导出
        o->
        mysqldump -uroot -p db1 > db1.sql
        o->
        mysqldump -uroot -p db1 tb1 > db1_tb1.sql
    导入
        o->
        mysql -uroot -p db1 < db1.sql
        o-> 数据库中
        source db1.sql
主从复制
    mysql配置文件my.cnf
    [mysqld]
        log-bin=mysql-bin
        server-id=222
        log-slave-updates =1
            # m-m-s结构中第二个m配置
    主服务器授权从服务器
        GRANT REPLICATION SLAVE ON *.* to 'slave'@'%' identified by 'asdf';
    从服务器设置主服务器
        change master to master_host='192.168.56.14', master_user='slave', master_password='asdf', master_log_file='mysql-bin.000001', master_log_pos=319;
    命令
        主服务器
            flush tables with read lock;
            grant replication slave on *.* to 'slave'@'%' identified by 'asdf'
            show master status\G
            unlock tables;
        从服务器
            stop slave
            change master to master_host='192.168.0.42', master_user='slave', master_passowrd='asdf', master_log_file='mysql-bin.000001', master_log_pos=120;
            start slave
            show slave status\G
    问题
        The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work.
            # 随意修改data/auto.cnf中的uuid的值
        主A复制到主B后,主B不会把数据复制到主B的从