mysql - 学习笔记

1.数据库的基本概念

  1. 什么是数据库?为什么要使用数据库?

    1. 数据库是一个实体,是保存和管理数据的“仓库”
    2. 数据库是数据管理的技术和方法,它能更合适的组织数据,更方便的维护数据,更严密的控制数据,更有效的利用数据。
  2. 论文 “r系统:数据库关系理论” 标志着关系型数据库的诞生

  3. oracle:老板 拉里埃里森

  4. 数据库注释有三种:# 、-- (空格)、/* */

  5. 数据库的语言

    ​ DDL:数据定义语言

    ​ DML:数据操作语言

    ​ DQL:数据查询语言

    ​ DCL:数据控制语言

    ​ TPL:事务处理语言

    ​ CCL:指针控制语言

  6. 大小写不敏感

  7. mysql默认的数据库

    information_schema #元数据
    mysql              #核心库
    performance_schema #性能相关
    sys  #对DBA有用
    

2.结构性查询语言

1.DDL数据库定义语言
  1. 数据库的操作

    常用关键字:CREATE ALTER DROP(删除) TRUNCATE(清空表的相关信息)

    #进入数据库的方式
    sudo mysql -u root -p
    
    #查看数据库
    show databases#创建数据库
    create database 39th;
    
    #数据库操作每一条语句都要以;号结尾
    #操作数据成功,只会报Query OK,
    
    #查看创建的数据库属性
    show create database 39th;
    
    #修改数据库的属性
    alter database myDB 属性名 目标;
    #如修改数据库字符集为utf8
    alter database 39th CHARACTER SET utf8;
    
    #切换、使用数据库
    use + 数据库名
    
    #删除数据库
    drop database 39th;
    

    数据的类型:
    请添加图片描述

    ​ char(size),提高效率

    ​ VARCHAR(size): 降低存储容量,节约空间

    ​ VARCHAR、BLOB和TEXT类是变长类型。每个类型的存储需求取决于列值的实际长度。

    ​ TimeStamp表示时间戳,它可用于自动记录insert、update操作的时间

  2. 表的操作

    add change drop

    #创建member表
    create table member (id int, name char(20), age int, math float);
    
    #查询当前数据库所有的表
    show tables;
    
    #查看表的结构信息
    show create table member;
    desc member;
    
    #数据库中  列 = 域 = 字段
    
    #修改表, 表的字段
    alter table member modify math int;
    alter table member change name mingzi varchar(20);
    #change可以改变列名,同样可以改变类型, 但是不能单独去修改类型, modify只可以修改类型,不能修改名字
    
    #修改表名
    rename table member to chengyuan;
    alter table chengyuan rename to member;
    
    #插入字段
    alter table member add english float;
    alter table member add chinese float first; /*插入到最前面*/
    alter table member add cs float after id; /* 插入数据到id后面*/
    
    #删除表中字段
    alter table member drop cs;
    
    #删除表
    drop member;
    
2.DML数据库管理语言

DML语言

常用关键字:INSERT UPDATE DELETE

#向表中插入数据
insert into member (id, name , age , math) values (1, 'zhansan', 27, 59);

#查询刚才插入的结果
select * from member;

#可以向表中插入部分数据
insert into member (id, name) values (2, 'lisi');

#不写列名插入数据,必须把所有列都赋值
insert into member values (3, 'wangwu', 21, 86);

#快速的插入多条数据
insert into member values (4, 'zhouliu', 21, 89), (5, 'wuqi', 19, 99);

#修改表中元素值,更新数据
update member set age = 31 where id = 2;
update member set math = 77 where name = 'lisi';

#一次更新多个数据
update member set age = 18, math = 89 where name = 'wuqi';
 
#删除表中数据
delete from member where id = 5;
 
#可以对表中插入空值
insert into member (id) values (NULL);

#删除表中空值
delete from member where id is NULL;
3.DQL数据库查询语言
  1. 简单查询

    #查询表中单个数据的信息
    select id from member;
    
    #查询表中部分数据的情况
    select name, math from member;
     
    #对查询结果去重distinct
    select distinct age from member;
      
    #对查询结果做表达式运算
    select name, math + 10  from member;
      +---------+-----------+
    | name    | math + 10 |
    +---------+-----------+
    | zhansan |        69 |
    | lisi    |        87 |
    | wangwu  |        96 |
    | zhouliu |        99 |
    +---------+-----------+
    4 rows in set (0.00 sec)
    
    #对表达式计算取别名
    select name, math + 10 + 19*21 -29+18  as total  from member;
    +---------+-------+
    | name    | total |
    +---------+-------+
    | zhansan |   457 |
    | lisi    |   475 |
    | wangwu  |   484 |
    | zhouliu |   487 |
    +---------+-------+
    
    #查询某些特定的列
    select * from member where math > 80;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    3 | wangwu  |   21 |   86 |
    |    4 | zhouliu |   21 |   89 |
    +------+---------+------+------+
    2 rows in set (0.00 sec)
    
      
     select * from member where math <> 89;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    1 | zhansan |   27 |   59 |
    |    2 | lisi    |   31 |   77 |
    |    3 | wangwu  |   21 |   86 |
    +------+---------+------+------+
    3 rows in set (0.01 sec)
    
    select * from member where math != 89;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    1 | zhansan |   27 |   59 |
    |    2 | lisi    |   31 |   77 |
    |    3 | wangwu  |   21 |   86 |
    +------+---------+------+------+
    3 rows in set (0.00 sec)
    
      
    #查询某范围的数据between ...  and ... 查询范围是闭闭区间
    select * from member where math between 60 and 89;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    2 | lisi    |   31 |   77 |
    |    3 | wangwu  |   21 |   86 |
    |    4 | zhouliu |   21 |   89 |
    +------+---------+------+------+
    3 rows in set (0.00 sec)
    
    #查询集合中的数据 in(set)
    select * from member where math in (10, 29, 59, 77, 100);
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    1 | zhansan |   27 |   59 |
    |    2 | lisi    |   31 |   77 |
    +------+---------+------+------+
    2 rows in set (0.00 sec)
    
    #模糊查询like
    #%代表一个或者多个任意字符,_代表一个字符
    select * from member where name like '%san';
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    1 | zhansan |   27 |   59 |
    +------+---------+------+------+
    1 row in set (0.00 sec)
    # %代表0个或任意个字符,_代表任意一个字符。
    select * from member where name like '%u';
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    3 | wangwu  |   21 |   86 |
    |    4 | zhouliu |   21 |   89 |
    +------+---------+------+------+
    2 rows in set (0.00 sec)
    
    
    select * from member where name like '%i_';
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    4 | zhouliu |   21 |   89 |
    +------+---------+------+------+
    1 row in set (0.00 sec)
    
    mysql> select * from member where name like '%i__';
    +------+------+------+------+
    | id   | name | age  | math |
    +------+------+------+------+
    |    2 | lisi |   31 |   77 |
    +------+------+------+------+
    1 row in set (0.00 sec)
    
    
    #数据库的退出方式
    1.quit;  
    2.exit;
    3.ctrl + d 
    
    #查询多个条件同时成立
    select * from member where age =21 and id =3;
    +------+--------+------+------+
    | id   | name   | age  | math |
    +------+--------+------+------+
    |    3 | wangwu |   21 |   86 |
    +------+--------+------+------+
    1 row in set (0.00 sec)
    
    #查询的结果中任一条件成立or
    select * from member where age =27 or id =3;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    1 | zhansan |   27 |   59 |
    |    3 | wangwu  |   21 |   86 |
    +------+---------+------+------+
    2 rows in set (0.00 sec)
    
    #查询条件不成立的
    select * from member where not(math = 59);
    select * from member where not math = 59;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    2 | lisi    |   31 |   77 |
    |    3 | wangwu  |   21 |   86 |
    |    4 | zhouliu |   21 |   89 |
    +------+---------+------+------+
    3 rows in set (0.00 sec)
    
    #对查询结果结果排序 (默认是升序的方式)
    select * from member order by age asc;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    3 | wangwu  |   21 |   86 |
    |    4 | zhouliu |   21 |   89 |
    |    1 | zhansan |   27 |   59 |
    |    2 | lisi    |   31 |   77 |
    +------+---------+------+------+
    4 rows in set (0.01 sec)
    
    select * from member  order by age desc;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    2 | lisi    |   31 |   77 |
    |    1 | zhansan |   27 |   59 |
    |    3 | wangwu  |   21 |   86 |
    |    4 | zhouliu |   21 |   89 |
    +------+---------+------+------+
    4 rows in set (0.00 sec)
    
    #显示固定范围的数据limit
    select * from member  order by math desc limit 3;
    +------+---------+------+------+
    | id   | name    | age  | math |
    +------+---------+------+------+
    |    4 | zhouliu |   21 |   89 |
    |    3 | wangwu  |   21 |   86 |
    |    2 | lisi    |   31 |   77 |
    +------+---------+------+------+
    3 rows in set (0.00 sec)
    
    #对查询结果偏移offset
    select * from member  order by math desc limit 1 offset 1;
    +------+--------+------+------+
    | id   | name   | age  | math |
    +------+--------+------+------+
    |    3 | wangwu |   21 |   86 |
    +------+--------+------+------+
    1 row in set (0.00 sec)
    
  2. 数据完整性

    1. 实体完整性:通过主键(类似于身份证号)保证插入的数据(一行)是表中唯一的实体,防止重复插入数据,浪费空间;

    2. 域完整性:保证列的数据类型和约束;

      一些约束:primary key / auto_increment / unique / not null

      ​ constraint ordersid_fk foreign key(ordersid) references orders(id),

    3. 参照完整性:保证一个表外键与另一个表的主键相关联,要求关系中不允许引用不存在的实体。

      数据完整性只能尽量保证插入的数据是正确的, 不能百分百保证数据正确

  3. 多表设计

    一对多:避免数据的冗余,节约空间

    多对多:避免数据的冗余,节约空间

    一对一:为了方便扩展,可以扩展成一对多,也可以扩展成多对多

  4. 复杂查询

    #复杂查询
    create table hero (
    	-> id int auto_increment,
        -> name char(20),
        -> age int,
        -> primary key(id)
        -> );
    #把某字段设置为主键后, 默认不为NULL,设置主键的列,不能有重复值
    
    insert into hero (name, age) values('yasuo', 5), ('jie', 7), ('nuoshou', 7);
    Query OK, 3 rows affected (0.01 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    mysql> select * from hero;
    +----+---------+------+
    | id | name    | age  |
    +----+---------+------+
    |  1 | timo    |   21 |
    |  2 | dema    |   29 |
    |  3 | yasuo   |    5 |
    |  4 | jie     |    7 |
    |  5 | nuoshou |    7 |
    +----+---------+------+
    5 rows in set (0.00 sec)
    
    #某字段设置为自动增长后,不赋值该字段,那么它会接着之前表中已有最大的值继续增加。
    
    #创建装备表,并且把hero_id设置为外键,关联hero表的id
    create table equipment (
        -> id int auto_increment primary key,
        -> name char(20) not NULL,
        -> price float,
        -> hero_id int,
        -> foreign key(hero_id) references hero(id)
        -> );
        
    #删除含有外键约束的表中数据,如果不解除外键,无法删除
    delete from hero where name = 'timo';
    delete from hero;
    truncate table hero;
    drop table hero;
    #以上方法都不能删除
     
    #只复制表数据,不复制表结构
    create table hero1 select * from hero;
    #只复制表结构,不复制表中数据
    create table hero2 like hero;
     
    #对一个没有设置主键的表设置主键
    alter table hero1 modify id int primary key;
     
    #对一个没有外键的表设置外键
    alter table equipment add foreign key(hero_id) references hero1(id);
    #设置外键的时候,关联的必须是另一个表的主键,而且类型需要相同,
     
    #可以主动设置外键名
    alter table equipment1 add CONSTRAINT equipment1_foreign_key FOREIGN KEY(hero_id) REFERENCES hero1(id);
     
    #删除外键
    alter table equipment1 drop FOREIGN KEY equipment1_ibfk_1;
     
    #查看当前是在哪个数据工作
    select database();
     
    #删除主键
    alter table hero3 drop primary key;
    #如果有外键关联该表的主键,是不能删除该表的主键
     
    #交叉查询
    select * from hero cross join equipment ;
      
    #内连接查询
    select * from hero inner join  equipment on hero.id = equipment.hero_id;
      
    #隐式内连接不加inner join
    select * from hero, equipment  where  hero.id = equipment.hero_id;
      
    #可以对查询的表名起别名as
    select * from hero as h inner join  equipment as e on h.id = e.hero_id;
      
    #左外连接(以左表为基准)
    select * from hero left outer join equipment on hero.id = equipment.hero_id;
      
    #右外连接(以右表为基准)
    select * from hero right outer join equipment on hero.id = equipment.hero_id;
       
    #子查询
    select *from hero where id =(select id from hero where name like '%suo');
       
    #联合查询(结果会去重)
    select age from hero where id > 3 union select age from hero where age < 10;
       
    #对联合查询结果不去重union all
    select age from hero where id > 3 union all select age from hero where age < 10;
    +------+
    | age  |
    +------+
    |    7 |
    |    7 |
    |    5 |
    |    7 |
    |    7 |
    +------+
    5 rows in set (0.00 sec)
    
    mysql> select *from hero;
    +----+---------+------+
    | id | name    | age  |
    +----+---------+------+
    |  1 | timo    |   21 |
    |  2 | dema    |   29 |
    |  3 | yasuo   |    5 |
    |  5 | nuoshou |    7 |
    |  6 | jie     |    7 |
    +----+---------+------+
    5 rows in set (0.00 sec)
    
    #报表查询
    select age from hero group by age;
     
    #统计函数和报表查询联合使用
    select count(age),age  from hero group by age;
    #group by后面的组要是同样的, 不然查不到
     
    #查询表中最大的年纪
    select max(age)  from hero;
     
    #同时查询最大值和最小值
    select min(age), max(age)  from hero;
    +----------+----------+
    | min(age) | max(age) |
    +----------+----------+
    |        5 |       29 |
    +----------+----------+
    
    #对报表查询做条件查询group by ... having ...
    select count(age), age from hero group by age having age < 10;
    +------------+------+
    | count(age) | age  |
    +------------+------+
    |          1 |    5 |
    |          2 |    7 |
    +------------+------+
    2 rows in set (0.00 sec)
    
    #对查询出来的临时表再次查询
    select count(age), age from hero group by age having count(age) > 1;
     +------------+------+
    | count(age) | age  |
    +------------+------+
    |          2 |    7 |
    +------------+------+
    1 row in set (0.00 sec)
    
  5. 数据库的备份:

    ​ 在命令行输入sudo mysqldump -u root -p 39th > 39th.sql

    数据库的恢复:

    ​ 1.进入数据库,创建该库(create database + 库名)

    ​ 2.退出数据库,在命令行输入sudo mysql -u root -p 39th < 39th.sql

    原理:通过协议连接到 MySQL数据库,将需要备份的数据查询出来并转换成对应的insert语句,当我们需要还原这些数据时,只要执行这些 insert 语句,即可将对应的数据还原。

  6. Linux和Windows数据库的传输:

    Linux下安装:sudo apt install lrzsz

    发送到Windows:sz myDataBase.sql

    删除备份库:rm myDataBase.sql

    从Windows恢复:rz

  7. 数据库编程的接口在https://www.mysql.com查找,c运行数据库代码加动态库-lmysqlclient

  8. 例子

    #include <mysql/mysql.h>
    #include <string.h>
    #include <stdio.h>
    #include <head.h>
    
    int main(int argc,char* argv[])
    {
        MYSQL *conn;
        MYSQL_RES *res;
        MYSQL_ROW row;
        char *server = "localhost";
        char *user = "root";
        char *password = "wz2021";
        char *database = "39th";    //要访问的数据库名称
        char query[300] = "select * from hero where name = '";
        unsigned int queryRet;
        sprintf(query, "%s%s%s", query, argv[1], "'");
        puts(query);
        /* strcpy(query,"select * from hero"); */
    
        //在输出前先打印查询语句
    
        //初始化
        conn = mysql_init(NULL);
        if(!conn)
        {
            printf("MySQL init failed\n");
            return -1;
        }
    
        //连接数据库,看连接是否成功,只有成功才能进行后面的操作
        if(!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0))
        {
            printf("Error connecting to database: %s\n", mysql_error(conn));
            return -1;
        }else{
            printf("MySQL Connected...\n");
        }
    
        //把SQL语句传递给MySQL
        queryRet = mysql_query(conn, query);
        if(queryRet)
        {
            printf("Error making query: %s\n", mysql_error(conn));
        }
        else
        {
            //用mysql_num_rows可以得到查询的结果集有几行
            //要配合mysql_store_result使用
            //第一种判断方式
            res = mysql_store_result(conn);
            printf("mysql_num_rows = %lu\n", (unsigned long)mysql_num_rows(res));
    
            //第二种判断方式,两种方式不能一起使用
            /* res = mysql_use_result(conn); */
    
            row = mysql_fetch_row(res);
    
            if(NULL == row)
            {
                printf("Don't find any data\n");
            }
            else
            {
                do
                {	
                    /* printf("num=%d\n",mysql_num_fields(res));//列数 */
                    //每次for循环打印一整行的内容
                    for(queryRet = 0; queryRet < mysql_num_fields(res); ++queryRet)
                    {
                        printf("%8s ", row[queryRet]);
                    }
                    printf("\n");
    
                }while(NULL != (row = mysql_fetch_row(res)));
            }
            mysql_free_result(res);
        }
        mysql_close(conn);
    
        return 0;
    }
    
    

3.事务

  1. 构成单一逻辑工作单元的操作集合,我们称为事务 (transaction)。
    事务就是一组原子性的SQL查询,或者说一个独立的工作单元。
    即使有故障,数据库系统也必须保证事务的正确执行——要么执行整个事务,要么属于该事务的操作一个也不执行。

  2. 事务的特性(acid)

    • 原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。无论是操作系统崩溃,还是计算机停止运行,这项要求都要成立。
    • 一致性:事务作为一个原子性操作,它从一个一致性的数据库状态开始运行,事务结束时,数据库的状态必须再次是一致的。
    • 隔离性:尽管多个事务可能并发执行,但系统保证,对于任何一对事务Ti和Tj ,在Ti看来, Tj要么在Ti开始之前已经完成,要么在Ti完成之后才开始执行。因此,每个事务都感觉不到系统中有其他事务在并发地执行。
    • 持久性:一个事务成功完成后,它对数据库的改变必须是永久的,即使出现系统故障。
  3. 事务的基本操作

    START TRANSACTION / BEGIN:开启一个事务,标记事务的起点
    COMMIT:提交事务,表示事务成功被执行。
    ROLLBACK:回滚事务,回滚到初始状态或者回滚点。
    SAVEPOINT:回滚点
    RELEASE SAVEPOINT:删除回滚点
    SET TRANSACTION:  设置隔离级别
    
    1)注意事项:
    START TRANSACTION 标志事务的开始,在 MySQL 中可以用 set autocommit = 0 替代。
    
    2)结束事务的情况只有两种:
           a. COMMIT:表示事务成功被执行,结束事务。
           b. 发生故障:结束事务, 不管有没有设置回滚点,都会回到事务开启前的状态。
    
    3)ROLLBACK:不表示发生故障, 它也是一个数据库操作,属于事务的一部分。表示回滚事务,回滚到事务开启前的状态,或者中间的某个回滚点。要想 rollback 生效,必须要 commit
  4. 并发执行时可能引发的问题

    • 脏写:脏写是指当多个事务并发写同一数据时,先执行的事务所写的数据会被后写的数据覆盖。脏写会导致更新丢失。就好像先提交的事务根本没有执行一样。
    • 脏读:如果一个事务A向数据库写数据,但该事务还没提交或终止,另一个事务B就看到了事务A写入数据库的数据,这个现象我们称为脏读。
    • 不可重复读:一个事务有对同一个数据项的多次读取,但是在某前后两次读取之间,另一个事务更新该数据项,并且提交了。在后一次读取时,感知到了提交的更新。这个现象我们称为不可重复读。
    • 幻读:一个事务需要进行前后两次统计,在这两次统计期间,另一个事务插入了新的符合统计条件的记录,并且提交了。导致前后两次统计的数据不一致。这种现象,我们称之为幻读。
  5. 隔离级别

    • 读未提交:允许读取未提交的数据。
    • 读已提交:只允许读取已提交数据,但不要求可重复读。比如,在事务两次读取一个数据项期间,另一个事务更新了该数据项并提交。
    • 可重复读:只允许读取已提交数据,而且在一个事务两次读取一个数据项期间,其他事务不得更新该数据。但该事务不要求与其他事务可串行化。比如,在两次统计查询中,另一个事务可以插入一些记录,当这些记录中有符合查询条件的,那么就会产生幻读。
    • 可串行化:看起来事务就好像是串行执行的一样。一个事务结束后,另一个事务才开始执行。
    • 以上所有隔离级别都不允许脏写(dirty write),即如果有一个数据项已经被另一个尚未提交或中止的事务写入,则该事务不能对该数据项执行写操作。
  6. 设置隔离级别

    #查看数据库使用的隔离级别
    select @@session.transaction_isolation;
    
    #设置隔离级别
    #读未提交
    set session transaction isolation level read uncommitted;
    
    #读已提交
    set session transaction isolation level read committed;
    
    #不可重复读
    set session transaction isolation level repeatable read;
    
    #串行化
    set session transaction isolation level serializable;
    

4.索引

  1. 索引(Index)是帮助MySQL高效获取数据的数据结构。
    查询是数据库最主要的功能之一,我们都希望查询的速度尽可能快。

  2. 显示表的索引的命令

    SHOW INDEX FROM tablename;
    
    SHOW INDEX FROM tablename\G
    
  3. 索引的分类

    • 主键索引

      ALTER TABLE tablename ADD PRIMARY KEY(field_name);
      
    • 普通索引

      CREATE INDEX index_name ON table_name(field_name);
      
      ALTER TABLE table_name ADD INDEX index_name(field_name);
      
    • 唯一索引

      CREATE UNIQUE INDEX index_name ON table(field);
      
    • 组合索引

      CREATE INDEX index_name ON table(field1, field2 ,......);
      
      ALTER TABLE mytable ADD INDEX name_city_age (name,city,age);
      
    • 删除索引

      ALTER TABLE table_name DROP INDEX index_name;
      
      DORP INDEX IndexName ON TableName;
      
  4. 最左前缀:针对组合索引。

  5. 索引的好处和坏处

    • 好处
      • 提高数据检索的效率,降低数据库的IO成本。
    • 坏处
      • 占用额外的空间。有时候索引占用的空间甚至比数据占用的空间还多。
      • 虽然索引大大提高了查询的速度,但同时也降低了更新表的速度。因为数据库不仅仅要更新数据,还要更新对应的索引信息。
    • 索引不是越多越好!索引太多,应用程序的性能可能会受到影响; 索引太少,查询速度会变慢。我们应该建立合适的索引,找到一个平衡点!

5.mysql优化

  1. mysql逻辑架构

    请添加图片描述

  2. 大体来说,MySQL可以分为 Server 层和存储引擎层。

    请添加图片描述
    Server 层包括连接器、查询缓存、解析器、优化器和执行器等,涵盖了 MySQL 大多数核心服务功能。

    存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Mermory 等多个存储引擎。

  3. mysql如何工作

    • 连接器
      当你在客户端输入 mysql –u $user –p $pwd 连接 mysql 的时候,接待你的就是连接器。连接器的作用就是和客户端建立连接、获取权限、维持和管理连接。
    • 查询缓存
      建立连接后,就可以执行select语句了。首先MySQL会去查看查询缓存,看下之前是否已经执行过这条查询语句。如果命中缓存,就直接返回。否则就扔给解析器。
    • 解析器
      MySQL需要知道你想做什么,因此我们就来到了解析器。解析器会做词法分析和语法分析。词法分析主要是分析每个词的含义,语法分析会判断你写的 SQL 语句是否满足 SQL 语法。
    • 优化器
      经过解析器,MySQL就知道你想做什么了。但是在开始执行之前,还需要经过优化器的处理。优化器会优化你的SQL语句。生成最终的执行方案 (execution plan)。然后进入执行器阶段。
    • 执行器
      执行器首先会判断你对这张表有没有相应的权限。如果没有,就报错。如果有,就调用相应的存储引擎接口,执行语句。然后将结果集返回给客户端。
  4. 数据的存储和提取是由存储引擎负责的,它负责和文件系统打交道。

    MySQL 的存储引擎是插件式的。不同的存储引擎支持不同的特性。

    #查看MySQL支持哪些存储引擎
    SHOW ENGINES;
    
    #查看默认存储引擎
    SHOW VARIABLES LIKE%storage_engine%;
    
    #查看某张表的存储引擎
    SELECT ENGINE FROM information_schema.TABLES
    WHERE TABLE_SCHEMA='$db'
    AND TABLE_NAME='$table';
    
  5. MyISAM

    MySQL 5.5 之前默认的存储引擎。
    特点:

    • 查询速度很快
    • 支持表锁
    • 支持全文索引
    • 不支持事务

    使用 MyISAM 存储表,会生成三个文件,索引和数据是分开存放的,这样的索引叫非聚集索引。

    • .frm # 存储表结构,是任何存储引擎都有的
    • .myd # 存放数据
    • .myi # 存放索引
    • 可以切换到root用户到 /var/lib/mysql/下查看文件。
  6. InnoDB

    MySQL 5.5 以及以后版本默认的存储引擎。没有特殊应用,推荐使用InnoDB引擎。

    特点:

    • 支持事务
    • 支持行锁和表锁
    • 支持MVCC(多版本并发控制)
    • 支持崩溃恢复
    • 支持外键一致性约束

    使用 InnoDB 存储表,会生成两个文件,索引和数据存放在一起,这样的索引叫聚集索引。

    • .frm #存储表结构,是任何存储引擎都有的
    • .ibd #存放数据和索引
  7. Memory

    特点:

    • 所有数据都存放在内存中,因此数据库重启后会丢失
    • 支持表锁
    • 支持Hash和BTree索引
    • 不支持Blob和Text字段

    Memory由于数据都放在内存中,以及支持Hash索引,它的查询速度是最快的。

    一般使用Memory存放临时表。

    临时表:在单个连接中可见,当连接断开时,临时表也将不复存在。

    #创建临时表,用show tables;显示不出来,但是可以进行insert,update,select,存储引擎还是InnoDB
    create temporary table tablename (id int,name char(20));
    
    
    #指定存储引擎创建普通表
    create table tablename (id int,name char(20)) engine=memory;
    #存储引擎是memory的表,断电重启后,数据消失,表名存在,与临时表不同。
    
  8. 如何选择存储引擎

    功能MyISAMMEMORYInnoDB
    存储限制256TBRAM64TB
    支持事务nonoyes
    支持全文索引yesnoyes
    支持树索引yesyesyes
    支持哈希索引noyesyes
    支持数据缓存noN/Ayes
    支持外键nonoyes

6.锁机制

  1. 锁是计算机协调多个进程或线程并发访问某一资源的机制。
    在数据库中,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

    使用锁可以对有限的资源进行保护,解决隔离和并发的矛盾。

  2. MySQL中的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking); BDB 存储引擎采用的是页面锁(page-level locking),但也支持表级锁; InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

  3. 锁的分类

    从对数据操作的粒度划分:

    • 表级锁
    • 行级锁

    从对数据操作的类型划分:

    • 读锁(共享锁)
    • 写锁(排它锁)
  4. MyISAM的表锁

    1. MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作 (UPDATE、DELETE、INSERT 等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。

    2. 显式加锁解锁

      加锁:
      	lock table 表名  read; (加读锁)
      
      	lock table 表名  write; (加写锁)
      
      解锁:
      	unlock tables;
      
      查看加锁的表:
      	show open tables;
      
      

      一个客户端对一个表加了读锁,该客户端只能操作本张表,但是可以通过再次给另外一张表加锁,来进行操作;另外一个客户端对该表进行更新和插入操作时,会卡住,也就是能进行读,不能进行写。

    3. MyISAM的表锁分为读锁(共享锁)和写锁(独占锁)。

      • 加读锁(共享锁):
        我们给表加上读锁会有什么效果呢?
        1、我们加读锁的这个进程可以读加读锁的表,但是不能读其他的表。
        2、加读锁的进程不能对这张表做写操作。
        3、其他进程可以读加读锁的表(因为是共享锁),也可以读其他表。
        4、其他进程update加读锁的表会一直处于等待锁的状态,直到锁被释放后才会update成功。

      • 加写锁(独占锁):
        1、加锁进程可以对加锁的表做任何操作。
        2、其他进程则不能查询加锁的表,需等待锁释放。

      • 总结:
        读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞。

      • 特点:开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

    4. 查看锁的争用情况:
      show open tables;
      in_use:表示当前被查询使用的次数,如果该数为0,则表是打开的,当前没有被使用。
      Name_locked:表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作。

  5. InnoDB的行锁

    1. 行级锁是Mysql中锁定粒度最细的一种锁,行级锁能大大减少数据库操作的冲突。

    2. 用法

      SELECT ... LOCK IN SHARE MODE;
      #在查询语句后面增加LOCK IN SHARE MODE,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。
      
      SELECT ... FOR UPDATE;
      #在查询语句后面增加FOR UPDATE,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
      
    3. InnoDB还有两个表锁:

      意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁

      意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。

      意向锁是InnoDB自动加的,不需要用户干预。

      对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X)。

    4. 间隙锁:一个会话锁定的是一个范围,另外一个会话不能在该范围内进行操作。

      #一个进程操作:
      select * from test_lock where id > 2 and id < 6 in share mode;
      
      #另一个进程操作:
      insert into test_lock values(3,'zhangfei',25);
      #因为第一个进程锁了一个间隙,所以这个操作会卡住。
      
  6. 锁的总结

    不同的存储引擎支持不同的锁机制。

    数据库中的锁从锁定的粒度上分可以分为行级锁、页级锁和表级锁。

    MySQL的MyISAM引擎支持表级锁。

    表级锁分为两种:共享读锁、互斥写锁。这两种锁都是阻塞锁。

    可以在读锁上增加读锁,不能在读锁上增加写锁。在写锁上不能增加写锁。

    默认情况下,MySql在执行查询语句之前会加读锁,在执行更新语句之前会执行解锁。

    如果想要显式的加锁/解锁的花可以使用LOCK TABLES和UNLOCK来进行。

    在使用LOCK TABLES之后,在解锁之前,不能操作未加锁的表。


7.业务设计

  1. 范式:数据库设计的规则

  2. 范式设计,数据库设计的三大范式

    • 第一范式:确保每列保持原子性。
    • 第二范式:确保表中每列都和主键相关。
    • 第三范式:确保每列与主键直接相关,而不是间接相关。
  3. 范式设计的优缺点

    • 优点:

      1)可以尽量得减少数据冗余
      2)范式化的表通常比反范式化的表更小
      3)范式化的数据库更新起来更加快;

    • 缺点:

      1)范式化的表,在查询的时候经常需要很多join关联,增加让查询的代价。
      2)更难进行索引优化

  4. 反范式设计

    反范式化是针对范式化而言得,在前面介绍了数据库设计得范式;

    所谓得反范式化就是为了性能和读取效率得考虑而适当得对数据库设计范式的要求进行违反;

    允许存在少量得冗余,换句话来说反范式化就是使用空间来换取时间。

  5. 反范式设计的优缺点

    • 优点:
      1)可以减少表的关联
      2)可以更好的进行索引优化
    • 缺点:
      1)存在数据冗余及数据维护异常
      2)对数据的修改需要更多的成本
  6. 三大范式只是一般设计数据库的基本理念,可以建立冗余较小、结构合理的数据库。如果有特殊情况,当然要特殊对待,数据库设计最重要的是看需求跟性能,需求>性能>表结构。所以不能一味的去追求范式建立数据库。在真实世界中很少会极端地使用。在实际应用中经常需要混用,可能使用部分范式化的技巧。

  7. 物理设计

    根据所选择的关系型数据库的特点对逻辑模型进行存储结构的设计。

    • 定义数据库、表及字段的命名规范
    • 选择合适的存储引擎
    • 为表中的字段选择合适的数据类型
    • 建立数据库结构

8.索引再探

  1. 分类

    • 聚集索引:数据与索引存放再一起.frm .ibd,如InnoDB。
    • 非聚集索引:数据与索引是分开存放的 .frm .myd .myi,如MyISAM。
    • 主键索引:以主键列作为索引。
    • 辅助索引:以非主键列作为索引。
      • 普通索引,唯一索引,组合索引,全文索引。
  2. InnoDB的索引

    • 主键索引:以主键建立B+树,页结点放数据;

    • 辅助索引:以辅助字段建立B+树,页结点放辅助字段对应主键。

    • InnoDB的聚集索引:

      InnoDB聚集索引的叶子节点存储行记录,因此InnoDB必须要有且只有一个聚集索引。

      1. 如果表定义了PK(Primary Key,主键),那么PK就是主键索引。
      2. 如果表没有定义PK,则第一个NOT NULL UNIQUE的列就是主键索引。
      3. 否则InnoDB会另外创建一个隐藏的ROWID作为主键索引。
    • InnoDB普通索引:
      InnoDB普通索引的叶子节点存储主键值(MyISAM则是存储的行记录头指针)

  3. MyISAM的索引

    • 主键索引:以主键建立B+树,页结点放主键对应数据的地址;
    • 辅助索引:以辅助字段建立B+树,页结点放辅助字段对应数据的地址。
  4. 创建语法

    创建:
             1) 创建表的时候指定。会自动给 primary keyunique 创建索引。	        
             2) CREATE [UNIQUE] INDEX 索引名 ON 表名(字段列表);
             3) ALTER 表名 ADD [UNIQUE] INDEX 索引名 (字段列表);
    删除:
            DROP INDEX 索引名 ON 表名;
            
    查看:
            SHOW INDEX FROM 表名;
    
  5. 回表

    当通过辅助索引来寻找数据的时候,InnoDB会遍历辅助索引,并通过辅助索引的叶子节点,获取主键。然后再通过聚集索引来找到一个完整的行记录。这个过程我们称之为回表。

    如果一个辅助索引的高度为3,聚集索引的高度为3。那么我们需要6次IO操作,才可以访问最终的数据。

  6. 最左前缀:当使用联合索引时,首先使用最左边字段作为索引建立B+树索引,当最左边的字段相同时,然后使用后续字段排序。

    鉴于此来建立的索引,所以查询时把最左边字段的筛选字段放在前面。

  7. 覆盖索引(索引覆盖)

    InnoDB 存储引擎支持覆盖索引 (covering index), 即从辅助索引中就可以得到要查询的信息,而不需要回表。

    使用覆盖索引的好处是,覆盖索引不包含整行记录的信息,故其大小远小于聚集索引,因此可以减少大量的 IO 操作。


9.mysql查询性能优化

1.慢查询

  1. 慢查询

    该日志能为SQL语句的优化带来很好的帮助。默认情况下,慢查询日志是关闭的,要使用慢查询日志功能,首先要开启慢查询日志功能。如果不是调优需要,一般不建议启动这个参数,因为会带来一定性能上的影响。

  2. 慢查询相关命令

    #查看是否开启慢查询日志
    mysql> show variables like '%slow%';
    +---------------------------+--------------------------------------------+
    | Variable_name             | Value                                      |
    +---------------------------+--------------------------------------------+
    | log_slow_admin_statements | OFF                                        |
    | log_slow_slave_statements | OFF                                        |
    | slow_launch_time          | 2                                          |
    | slow_query_log            | OFF                                        |
    | slow_query_log_file       | /var/lib/mysql/wz-virtual-machine-slow.log |
    +---------------------------+--------------------------------------------+
    5 rows in set (0.01 sec)
    
    #查看记录慢查询日志记录的门槛时间
    mysql> show variables like '%query_time%';
    +-----------------+-----------+
    | Variable_name   | Value     |
    +-----------------+-----------+
    | long_query_time | 10.000000 |
    +-----------------+-----------+
    1 row in set (0.00 sec)
    
    #开启慢查询日志,只是临时生效,如果要永久生效,必须修改配置文件
    set global slow_query_log=1;
    
    #修改记录慢查询日志记录的门槛时间,为什么修改之后看不到变化?要重新连接
    set global long_query_time=0.1;  
    
    #查看慢查询SQL记录数
    Show global status like%slow_queries’;
    

    slow_query_log 启动/停止技术慢查询日志
    slow_query_log_file 指定慢查询日志得存储路径及文件(默认和数据文件放一起)
    long_query_time 指定记录慢查询日志SQL执行时间的阈值(单位:秒,默认10秒)
    log_queries_not_using_indexes 是否记录未使用索引的SQL
    log_output 日志存放的地方【TABLE】【FILE】【FILE,TABLE】

  3. 常用的慢查询日志分析工具(mysqldumpslow)

    语法:mysqldumpslow -s r -t 10 slow-mysql.log

    ​ -s order (c,t,l,r,at,al,ar)
    ​ c:总次数
    ​ t:总时间
    ​ l:锁的时间
    ​ r:总数据行
    ​ at,al,ar :t,l,r平均数 【例如:at = 总时间/总次数】
    -t top 指定取前面几天作为结果输出

2.执行计划

  1. EXPLAIN 是解释 SQL 语句的执行计划,即显示该 SQL 语句怎么执行的;

    用法:explain | desc | describe + sql

  2. 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。从而分析你的查询语句或是表结构的性能瓶颈。
    通过EXPLAIN,我们可以分析出以下结果:

    • 表的读取顺序
    • 数据读取操作的操作类型
    • 哪些索引可以使用
    • 哪些索引被实际使用
    • 表之间的引用
    • 每张表有多少行被优化器查询
  3. explain执行计划的输出内容

    含义
    id执行计划的id标志
    select_typeselect的类型
    table输出记录的表
    partitions匹配的分区
    typejoin的类型
    possible_keys优化器可能选择的索引
    key优化器实际选择的索引
    key_len使用索引的最大可能字节长度
    ref与索引进行比较的字段
    rows优化器预估的记录数量/大概要检索的行数
    filtered根据条件过滤得到的记录的百分比
    extra额外的显示选项
  4. 执行计划的id

    select 查询的序列号,标识执行的顺序,表示查询中执行select子句或操作表的顺序:

    • id相同,执行顺序由上至下;
    • id不同,id值越大,优先级越高,越先被执行。
  5. 执行计划的select_type

    查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询。

    类型描述
    simple简单的 select 查询,查询中不包含子查询或者UNION
    primary查询中若包含任何复杂的子部分,最外层查询则被标记为此
    subquery在SELECT或WHERE列表中包含了子查询
    derived在FROM列表中包含的子查询被标记为DERIVED(衍生)
    MySQL会递归执行这些子查询, 把结果放在临时表里。
    union若第二个SELECT出现在UNION之后,则被标记为UNION;
    若UNION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED
    union result从UNION表获取结果的SELECT
  6. 执行计划的type

    • type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:
      system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

    • 需要记忆的:
      system > const > eq_ref > ref > range > index > ALL

    • 具体

      • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计。

      • const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。

        const 扫描的条件为:

        1. 命中主键(primary key)或者唯一(unique)索引
        2. 被连接的部分是一个常量(const)值
      • eq_ref:eq_ref 扫描的条件为,对于前表的每一行(row),后表只有一行被扫描。

        1. join 查询;
        2. 命中主键(primary key)或者非空唯一(unique not null)索引;
        3. 等值连接。
      • ref:如果把上例 eq_ref 案例中的主键索引,改为普通非唯一(non unique)索引(辅助索引)。就由 eq_ref 降级为了 ref,此时对于前表的每一行(row),后表可能有多于一行的数据被扫描。

        当 id 改为普通非唯一索引后,常量的连接查询,也由 const 降级为了 ref,因为也可能有多于一行的数据被扫描。

        ref 扫描,可能出现在 join 里,也可能出现在单表普通索引里,每一次匹配可能有多行数据返回,虽然它比 eq_ref 要慢,但它仍然是一个很快的 join 类型。

      • range:range扫描就比较好理解了,它是索引上的范围查询,它会在索引上扫码特定范围内的值。

      • index:查询需要通过扫描索引上的全部数据来计数,它仅比全表扫描快一点。扫描整个索引文件,只是不去扫描真实的数据文件。

      • all:如果查询时设定的查询条件列上没有建立索引,就是用全表扫描的方式。

    • 总结

      1. explain 结果中的 type 字段,表示(广义)连接类型,它描述了找到所需数据使用的扫描方式

      2. 常见的扫描类型有:system>const>eq_ref>ref>range>index>ALL,其扫描速度由快到慢

      3. 各类扫描类型的要点是:

        • system 最快:不进行磁盘 IO
        • const:PK 或者 unique 上的等值查询
        • eq_ref:PK 或者 unique 上的 join 查询,等值匹配,对于前表的每一行,后表只有一行命中
        • ref:非唯一索引,等值匹配,可能有多行命中
        • range:索引上的范围扫描,例如:between、in、>
        • index:索引上的全集扫描,例如:InnoDB 的 count
        • ALL 最慢:全表扫描
        • 建立正确的索引,非常重要
      4. 使用 explain 了解并优化执行计划,非常重要

  7. 执行计划-key_len

    1. 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好。

    2. key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。

    3. key_len表示索引使用的字节数,根据这个值,就可以判断索引使用情况,特别是在组合索引的时候,判断所有的索引字段是否都被查询用到。

    4. 变长字段需要额外的2个字节(VARCHAR值保存时只保存需要的字符数,另加一个字节来记录长度(如果列声明的长度超过255,则使用两个字节),所以VARCAHR索引长度计算时候要加2),固定长度字段不需要额外的字节。
      NULL都需要1个字节的额外空间,所以索引字段最好不要为NULL,因为NULL让统计更加复杂并且需要额外的存储空间。

    5. 复合索引有最左前缀的特性,如果复合索引能全部使用上,则是复合索引字段的索引长度之和,这也可以用来判定复合索引是否部分使用,还是全部使用。

    6. varchar(n)变长字段+允许Null=n*(utf8mb4=4,utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)

      datetime类型在5.6中字段长度是5个字节,datetime类型在5.5中字段长度是8个字节

  8. 执行计划ref:显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。

  9. 执行计划-rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。

  10. 执行计划-Extra

    包含不适合在其他列中显示但十分重要的额外信息。

    描述
    using filesort说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”。
    using temporary使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。
    using index是否用了覆盖索引
    using where表明使用了where过滤
    using join buffer使用了连接缓存
    impossible wherewhere子句的值总是false,不能用来获取任何元组。

10.索引优化

  1. 优化器选择不使用索引的情况

    有些情况虽然建立了索引,但是优化器并没有选择索引去查找数据,而是通过扫描聚集索引,也就是全表扫描来获取数据。

    • 对索引列进行运算
      select * from t where a = 1;
      select * from t where a + 1 = 2;
    • 对索引进行不等于判断
      select * from t where a = 1;
      select * from t where a != 1;
      select * from t where a is null;
      select * from t where a is not null;
    • 范围查找过大 (对表t的c字段建立索引)
      explain select * from t where c between 1 and 20000;
      explain select * from t where c between 1 and 300000;
    • 查找时select *
  2. 何时该创建索引

    • 主键和唯一键会自动创建索引,外键要求必须是primary key 或者 unique。

    • 频繁作为查询条件的字段应该创建索引。

    • 与其他表关联的字段应该创建索引。

    • 查询中用于经常用于排序的字段。

    • 查询中经常用于分组的字段。

  3. 何时不该创建索引?

    • 表的记录太少

    • 经常更新的表

    • 数据字段中包含太多的重复值,比如国籍,性别等字段。

  4. 实践策略

    • 对于辅助索引,尽量使用覆盖索引(避免回表), 比如尽量不要使用 select *。

    • 尽量使用最左前缀法则(组合索引,复合索引)

    • 不要在索引列上做运算

    • 范围查找尽量不要太大

    • 尽量不要使用不等于

  5. 一些规范

    • 尽量使用InnoDB存储引擎
      解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高。

    • 禁止存储大文件或者大照片
      解读:为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统,数据库里存 URI 多好。

    • 控制单表数据量,单表记录控制在千万级。

    • 平衡范式与冗余,为提高效率可以牺牲范式设计,冗余数据。

    • 表必须有主键,例如自增主键,推荐使用 UNSIGNED 整数为主键。

      • 主键递增,数据行写入可以提高插入性能,可以避免 page 分裂,减少表碎片,提升空间和内存的使用;
      • 主键要选择较短的数据类型,InnoDB 引擎普通索引都会保存主键的值,较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率。
    • 必须把字段定义为 NOT NULL 并且提供默认值。

      • null 的列使索引/索引统计/值比较都更加复杂,对 MySQL 来说更难优化;
      • null 这种类型 MySQL 内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多;
      • null 值需要更多的存储空,无论是表还是索引中每行中的 null 的列都需要额外的空间来标识;
      • 对 null 的处理时候,只能采用 is null 或 is not null,而不能采用 =、in、<、<>、!=、not in 这些操作符号。如:where name != ‘shenjian’,如果存在 name 为 null 值的记录,查询结果就不会包含 name 为 null 值的记录;
    • 禁止使用 TEXT、BLOB 类型。

      会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能。

    • 根据业务区分使用 char/varchar

    • 单表索引建议控制在5个以内

    • 禁止在更新十分频繁、区分度不高的属性上建立索引

      • 更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能;
      • 【性别】这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似;
    • 建立组合索引,必须把区分度高的字段放在前面。

    • 理解组合索引最左前缀原则,避免重复建设索引,如果建立了(a,b,c),相当于建立了(a), (a,b), (a,b,c)。

    • 禁止使用 SELECT *,只获取必要的字段,需要显示说明列属性。

    • SQL WHERE 条件的顺序不一定需要按照索引的顺序。

      比如一个联合索引是 name, age,查询的时候 WHERE 条件可以写成 age=10 and name=‘张三’。会有查询优化器优化。

11.mysql主从复制

  1. 为什么需要主从复制?

    1. 在业务复杂的系统中,有这么一个情景,有一句sql语句需要锁表,导致暂时不能使用读的服务,那么就很影响运行中的业务,使 用主从复制,让主库负责写,从库负责读,这样,即使主库出现了 锁表的情景,通过读从库也可以保证业务的正常运作。

    2. 做数据的热备

    3. 架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。

  2. MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从 节点不用一直访问主服务器来更新自己的数据,数据的更新可以在 远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。

  3. mysql主从复制原理请添加图片描述

    原理:

    1. master服务器将数据的改变记录二进制binlog日志,当master上的数据发生改变时,则将其改变写入二进制日志中;

    2. slave服务器会在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/OThread请求master二进制事件 。

    3. 同时主节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中,从节点将启动SQL线程从中继日志中读取二进制日志,在本地存放,使得其数据和主节点的保持一致,最后I/OThread和SQLThread将进入睡眠状态,等待下一次被唤醒。

    也就是说:

    • 从库会生成两个线程,一个I/O线程,一个SQL线程;
    • I/O线程会去请求主库的binlog,并将得到的binlog写到本地的relay-log(中继日志)文件中;
    • 主库会生成一个log dump线程,用来给从库I/O线程传binlog;
    • SQL线程,会读取relay log文件中的日志,并解析成sql语句逐一执行;
  4. mysql主从形式

    • 一主一从
    • 主主复制
    • 一主多从
    • 多主一从
    • 联级复制
  5. mysql主从同步延时分析

    mysql的主从复制都是单线程的操作,主库对所有DDL和DML产生的日志写进binlog,由于binlog是顺序写,所以效率很高,slave的sql thread线程将主库的DDL和DML操作事件在slave中重放。DML和DDL的IO操作是随机的,不是顺序,所以成本要高很多,另一方面,由于sql thread也是单线程的,当主库的并发较高时,产生的DML数量超过slave的SQL thread所能处理的速度,或者当slave中有大型query语句产生了锁等待,那么延时就产生了。

    解决方案:

    1. 业务的持久化层的实现采用分库架构,mysql服务可平行扩展,分散压力。

    2. 单个库读写分离,一主多从,主写从读,分散压力。这样从库压力比主库高,保护主库。

    3. 服务的基础架构在业务和mysql之间加入memcache或者redis的cache层。降低mysql的读压力。

    4. 不同业务的mysql物理上放在不同机器,分散压力。

    5. 使用比主库更好的硬件设备作为slave,mysql压力小,延迟自然会变小。

    6. 使用更加强劲的硬件设备。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值