Mysql的延展(高级使用方法)

Mysql高级

https://blog.csdn.net/csdn_kenneth/category_6908529.html

一、mysql 的架构介绍

1、安装

(1)rpm安装

  • 检查是否安装过mysql

    rpm -qa | grep -i mysql
    
  • 安装mysql

    rmo -ivh 要安装的软件
    rpm -ivh MySQL-server-5.5.48-1.linux2.6.i386.rpm
    rpm -ivh MySQL-client-5.5.48-1.linux2.6.i386.rpm
    
  • 查看是否安装成功

    mysql --version
    cat /etc/passwd|grep mysql
    

(2)docker安装

docker run -p 3306:3306 --name mysql \
-v /lixp/mysql/conf:/etc/mysql/conf.d \
-v /lixp/mysql/logs:/logs \
-v /lixp/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root@123456 \
-d mysql:5.6
  • -p 3306:3306:将容器的 3306 端口映射到主机的 3306 端口。
  • -v $PWD/conf:/etc/mysql/conf.d:将主机当前目录下的 conf/my.cnf 挂载到容器的 /etc/mysql/my.cnf。
  • -v $PWD/logs:/logs:将主机当前目录下的 logs 目录挂载到容器的 /logs。
  • -v $PWD/data:/var/lib/mysql :将主机当前目录下的data目录挂载到容器的 /var/lib/mysql 。
  • -e MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码。

2、存储引擎

(1)参看命令

show engines;
show variables like "%storage_engine%";

(2)区别

对比项MyISAMInnoDB
主外键不支持支持
事务不支持支持
行表锁表锁,即使操作一条记录也会锁住整个表
不适合高并发
行锁,操作时只锁某一行,不对其他行有影响。
适合高并发
缓存只缓存索引,不缓存真实数据不仅缓存索引还缓存真实数据
对内存要求较高
表空间
关注点性能事务
默认安装YY

二、索引优化分析

https://blog.csdn.net/csdn_kenneth/article/details/82902156

1、性能下载原因

(1)现象:

  • 执行时间长
  • 等待时间长

(2)原因

  • 查询语句写的烂
  • 索引失效
  • 关联查询太多(设计缺陷或不得已的需求)
  • 服务器调优及各个参数设置(缓存、线程数等)
# 一张表,有以下几个字段

id    name   email    weixinNumber

# 查询语句,根据name来过滤,
select * from user where name = '';
select * from user where name = '' and email = '';

# 创建索引:

# 单值索引
create index idx_user_name on user(name)

# 复合索引
create index idx_user_nameEamil on user(name, email)

2、SQL加载顺序

(1)手写

SELECT DISTINCT
	<select_list>
FROM
	<left_table><join_type>
JOIN
	<right_table>
ON
	<join_condition>
WHERE
	<where_condition>
GROUP BY
	<group_by_list>
HAVING
	<having_condition>
ORDER BY
	<order_by_condition>
LIMIT 
	<limit_number>

(2)机读

FROM
	<left_table>
ON
	<join_condition>
	<join_type>
JOIN
	<right_table>
WHERE
	<where_condition>
GROUP BY
	<group_by_list>
HAVING
	<having_condition>
SELECT DISTINCT
	<select_list>
ORDER BY
	<order_by_condition>
LIMIT 
	<limit_number>

(3)小结

3、七种JOIN理论

(1)理论

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-crPgIcCT-1605703262226)(E:\软件学习\学习笔记\images\jion理论.png)]

(2)案例

  • 建表语句

    CREATE TABLE `tbl_dept`(
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `deptName` VARCHAR(30) DEFAULT NULL,
    `locAdd`  VARCHAR(40) DEFAULT NULL,
    PRIMARY KEY (`id`)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
     
    CREATE TABLE `tbl_emp`(
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(30) DEFAULT NULL,
      `deptId` INT(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `fk_dept_id` (`deptId`)
      #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `tbl_dept` (`id`)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
    insert into tbl_dept(deptName,locAdd) VALUES('RD',11);
    insert into tbl_dept(deptName,locAdd) VALUES('HR',12);
    insert into tbl_dept(deptName,locAdd) VALUES('MK',13);
    insert into tbl_dept(deptName,locAdd) VALUES('MIS',14);
    insert into tbl_dept(deptName,locAdd) VALUES('FD',15);
     
    insert into tbl_emp(name,deptId) VALUES('Z3',1);
    insert into tbl_emp(name,deptId) VALUES('Z4',1);
    insert into tbl_emp(name,deptId) VALUES('Z5',1);
    insert into tbl_emp(name,deptId) VALUES('w5',2);
    insert into tbl_emp(name,deptId) VALUES('w6',2);
     
    insert into tbl_emp(name,deptId) VALUES('s7',3);
    insert into tbl_emp(name,deptId) VALUES('s8',4);
    insert into tbl_emp(name,deptId) VALUES('s9',51);
    
    
    
  • 查询语句

    • inner join

      select * from tbl_emp a inner join tbl_dept b on a.deptId=b.id;
      
      
    • left join

      select * from tbl_emp a left join tbl_dept b on a.deptId=b.id;
      
      
    • right join

      select * from tbl_emp a right join tbl_dept b on a.deptId=b.id;
      
      
select * from tbl_emp a left join tbl_dept b on a.deptId=b.id where b.id is NULL

select * from tbl_emp a right join tbl_dept b on a.deptId=b.id where a.deptId is NULL

  • full outer join

  • mysql不支持这种语法。oracle支持。用一种变通的方式来实现:

    select * from tbl_emp a left join tbl_dept b on a.deptId = b.id
    union
    select * from tbl_emp a right join tbl_dept b on a.deptId = b.id
    
    
    select * from tbl_emp a left join tbl_dept b on a.deptId = b.id
    where b.id is null
    union
    select * from tbl_emp a right join tbl_dept b on a.deptId = b.id
    where a.deptId is null
    
    

4、索引

https://blog.csdn.net/csdn_kenneth/article/details/82904111

https://www.cnblogs.com/sweet521/p/6203360.html

(1)概念

  • MySQL官方对索引的定义:索引(index)是帮助MySQL高效获取数据的数据结构。

  • 索引的本质:数据结构

  • 所以的目的:提高查找效率

  • 常用的索引:BTree

  • 排好序的快速查找数据结构
    • 索引想要查得快,本质上是排过序。
    • 索引有两大功能:1.查找快;2.排好序。也就是说索引用于快速查询和排序。我们建的索引会影响sql的两部分。
    • 索引会影响到where和order by。

(2)优缺点

  • 优点
    • 类似图书馆建数目索引,提高数据检索的效率,降低数据库的IO成本
    • 通过索引列,对数据进行排序,降低数据的排序成本,减低CPU的消耗
  • 缺点
    • 索引的实质上也是一张表,该表保存了索引字段,并指向实体表的数据,所以索引也是要占用空间的
    • 虽然索引大大提高了查询速度,但是会降低更新速度(更新表会更新索引)
    • 索引只是提高效率的一个因素,如果MySQL中有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询。

(3) 索引分类

  • 单值索引:一个索引只包含单个列,一个表可以有多个单列索引

  • 唯一索引[UNIQUE]:索引列的值必须唯一,但允许有空值

  • 符合索引:一个索引包含多个列

  • 基本语法:

    # 创建
    CREATE [UNIQUE] INDEX indexName ON mytable(columnname(length));
    ALTER mytable ADD [UNIQUE] INDEX [indexname] ON (columnname(length));
    
    # 删除
    DROP INDEX [indexName] ON mytable
    
    # 查看
    SHOW INDEX FROM table_name\G
    
    
    
  • 有四种方式来添加数据表的索引

    # 添加一个主键,意味着索引值唯一
    ALTER TABLE tbl_name ADD PRIMARY KEY (colum_list);  
    
    # 创建索引的值必须是唯一的
    ALTER TABLE tbl_name ADD UNIQUE index_name (colum_list); 
    
    # 添加普通索引
    ALTER TABLE tbl_name ADD INDEX index_name (colum_list); 
    
    # 添加全文索引
    ALTER TABLE tbl_name ADD FULLTEXT index_name (colum_list);
    
    

(4)mysql索引结构

  • BTree
  • Hash索引
  • full-text全文索引
  • R-Tree索引

(5)适合索引的情况

  • 主键自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引
  • 查询中与其他表关联的字段,外键关系建立索引
  • 频繁更新的字段不适合创建索引
  • Where条件里用不到的字段不创建索引
  • 单键/组合索引的选择(在高并发下倾向创建组合索引)
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高速度
  • 查询中统计或者分组的字段(分组前提是排序)

(6)不适合索引的情况

  • 记录太少
  • 经常增删改的表
  • 数据重复且分布平均的字段

(7)explain

https://blog.csdn.net/csdn_kenneth/article/details/82914814

  • 作用:

    • 使用explain关键字可以模拟优化器执行sql查询语句,从而知道mysql是如何处理你的SQL语句的。
  • 用途

    • 表的读取顺序
    • 数据读取操作的操作类型
    • 哪些索引可以使用
    • 哪些索引被实际使用
    • 表之间的引用
    • 每张表有多少行被优化器查询
  • 字段:

    • id------表的读取顺序

      • id相同,执行顺序由上至下
      • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
      • id相同不同,同时存在
    • select_type-------操作类型

      • simple:简单的select查询,查询中不包含子查询或者union

      • primary:查询若包含任何子查询,最外层查询则被标记为primary(最后执行)

      • subquery:子查询

      • derived:在from列表中包含的子查询被标记为derived(衍生),mysql会递归执行这些子查询,把结果放在临时表中

      • union:若第二个select出现在union之后,则被标记为union

        ​ 若 union包含在from子句的子查询中,外层select将被标记为derived

      • union result:从union表获取结果的result

    • table:显示这一行的数据是关于哪张表的

    • type---------system > const > eq_ref > ref > range > index > all

      • all:全表扫描

      • index:Full index scan

        ​ index与all的区别为index类型值遍历索引树,通常比all快,因为索引文件比较小。

      • range:只检索给定范围的行,使用一个索引来选择行。

        ​ key列只显示使用了哪个索引。

        ​ 一般就是在你的where语句中出现了between、<、>、in等查询

        ​ 这种范围扫描索引扫描比全表扫描要好

        ​ 因为它值需要开始与索引的某一点,而结束与另一点,不用全表扫描

      • ref:非唯一性索引扫描,返回匹配某个单独值的所有行。

        ​ 本质上也是索引访问,它返回所有匹配某个单独值的行

        ​ 然而它可能会找到多个符合条件的行

        ​ 所以它应该属于查找和扫描的混合体

      • eq_ref:唯一性索引扫描,对每个索引建,表中只有一条记录与之匹配。常用于主键或唯一索引扫描

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

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

      • null

    • possible_keys

      • 显示可能应用在这张表中的索引,一个或多个。
      • 查询涉及到的字段上若存在索引,则改索引将被累出,但不一定被查询实际使用。
    • key

      • 实际使用的索引。如果为NULL,则没有是使用索引
      • 查询中若使用了覆盖索引,则该索引仅出现在key列表中
      • 覆盖索引:select的数据列只冲索引中就能读取得到,不必读取数据行,mysql可以利用索引返回select列表中的字段,而不必根据索引再次读取文件,即查询列要被所建的索引覆盖。
  • ken_len

    • 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。
    • 在不损失精确性的情况下,长度越短越好。
    • key_len显示的值为索引字段的最大可能长度,并实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。
  • ref

    • 显示索引的哪一列被使用了,如果可能的话,是一个常数。那些列或常量被用于查找索引列上的值。
  • rows

    • 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
  • extra:十分重要的额外信息

    • Using filesort(九死一生)
      • 说明mysql会对数据使用一个外部的索引排序,而不是安装表内的索引顺序进行读取。
      • mysql中无法利用索引完成的排序称为“文件排序”
    • Using temporary(十死无生)
      • 使用临时表保存中间结果,MySQL在对查询结果排序时使用临时表
      • 常见于order by和group by
    • Using index(效果好)
      • 表示相应的select操作中是用了覆盖索引,避免访问了表的数据行,效率不错
      • 如果同时出现using where,表明索引被用来执行索引键值的查找
      • 如果没有同时出现using where,表明索引用来读取数据,而非执行查找动作
    • Using where
      • 使用了where连接
    • Using join buffer
      • 使用了连接缓存
    • impossible where
      • where子语句的值是false,不能用来获取任何元组
    • select tables optimized away
      • 在没有groub by 子语句的情况下,基于索引优化min、max操作或者对用myisam存储引擎优化count操作,不必等到执行阶段再进行介绍,查询执行计划生成的阶段即完成优化
    • distinct
      • 优化distinct操作,在找到第一匹的元组后,即停止找到同样值的动作
  • 案例

    https://blog.csdn.net/csdn_kenneth/article/details/82927581

(8)索引分析

单表

https://blog.csdn.net/csdn_kenneth/article/details/82929893

  • 数据准备

    create table if not exists `article`(
     `id` INT(10) unsigned NOT NULL primary key auto_increment,
     `article_id` INT(10) unsigned NOT NULL,
     `category_id` INT(10) unsigned NOT NULL,
     `views` INT(10) unsigned NOT NULL,
     `comments` INT(10) unsigned NOT NULL,
     `title` VARCHAR(255) NOT NULL,
     `content` TEXT NOT NULL
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
    insert into `article` (`article_id`, `category_id`,`views`,`comments`,`title`,`content`) values 
    (1,1,1,1,'1','1'),
    (2,2,2,2,'2','2'),
    (3,3,3,3,'3','3');
    
    
  • 查询 category_id 为1,且comments大于1的情况下,views最多的 article_id

    SELECT id, article_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
    
    
  • 优化过程

    # 开始优化
    # 1.1 新建索引+删除索引
    # 建立索引
    CREATE INDEX idx_article_ccv ON article(category_id,comments,views);
    
    SHOW INDEX from article;
    
    EXPLAIN SELECT id, article_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
    
    # 结论:type变成了range,但是extra中仍然使用using filesort
    # 原因:按照BTREE工作原理,先排序category_id,如果遇到相同的category_id,则再排序comments,如果遇到相同的comments,再排序views。
    #       当comments字段在联合索引李处于中间位置时,因comments > 1条件是一个范围值(range),mysql无法利用索引再对后面的view部分进行检索,即range类型查询后面的字段索引无效
    
    # 删除索引
    DROP INDEX idx_article_ccv ON article
    EXPLAIN SELECT id, article_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
    
    # 1.2 再建索引(不对comments进行索引)
    CREATE INDEX idx_article_cv ON article(category_id,views);
    
    SHOW INDEX from article;
    
    EXPLAIN SELECT id, article_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
    
    DROP INDEX idx_article_cv ON article;
    
    
两表

https://blog.csdn.net/csdn_kenneth/article/details/82930039

  • 数据准备

    create table if not exists `class`(
     `id` INT(10) unsigned NOT NULL auto_increment,
     `card` INT(10) unsigned NOT NULL,
     primary key(`id`)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
    create table if not exists `book`(
     `bookid` INT(10) unsigned NOT NULL  auto_increment,
     `card` INT(10) unsigned NOT NULL,
     primary key(`bookid`)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
    insert into class(card) values(floor(1+(RAND()*20)));
     
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    insert into book(card) values(floor(1+(RAND()*20)));
    
    
    
  • 查询语句

    SELECT * FROM class LEFT JOIN book ON class.card = book.card;
    
    
  • 优化过程

    SHOW INDEX FROM book;
    SHOW INDEX FROM class;
    EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
    
    # 添加book表索引优化
    ALTER TABLE book ADD INDEX Y(card);
    EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
    DROP INDEX Y ON book;
    
    # 添加class表索引优化
    ALTER TABLE class ADD INDEX Y(card);
    EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
    DROP INDEX Y ON class;
    
    # 结论:
    # LEFT JOIN条件用于确定如何从右表搜索,左边一定都有,所以右边是我们的关键点,一定需要建立索引
    # RIGHT JOIN条件用于确定如何从左表搜索,右边一定都有,所以左边是我们的关键点,一定需要建立索引
    
    
三表

https://blog.csdn.net/csdn_kenneth/article/details/82930278

  • 数据准备

    create table if not exists `phone`(
     `phoneid` INT(10) unsigned NOT NULL  auto_increment,
     `card` INT(10) unsigned NOT NULL,
     primary key(`phoneid`)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    insert into phone(card) values(floor(1+(RAND()*20)));
    
    
  • 查询语句

    SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card
    
    
  • 优化过程

    SHOW INDEX FROM book;
    SHOW INDEX FROM class;
    SHOW INDEX FROM phone;
    EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card
    
    # 建立索引
    ALTER TABLE book ADD INDEX Y(card);
    ALTER TABLE phone ADD INDEX Z(card);
    
    EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card
    
    # 结论:索引最好设置在需要经常查询的字段中
    
    
  • 尽可能减少Join语句中的NestedLoop的循环总次数;永远用小的结果集驱动大的结果集

  • 优先优化NestedLoop的内层循环;

  • 保证Join语句中被驱动表上join条件字段已经被索引。

  • 当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝啬JoinBuffer的设置

(9)索引优化

https://blog.csdn.net/csdn_kenneth/article/details/82930420

  • 数据准备

    DROP TABLE IF EXISTS `staffs`;
    CREATE TABLE `staffs` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(24) NOT NULL,
      `age` int(11) NOT NULL DEFAULT '0',
      `pos` varchar(20) NOT NULL,
      `add_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     
    insert into staffs(name,age,pos,add_time) values ('z3',22,'manager',NOW());
    insert into staffs(name,age,pos,add_time) values ('july',23,'dex',NOW());
    insert into staffs(name,age,pos,add_time) values ('2000',23,'manager',NOW());
     
    alter table staffs add index idx_staffs_nap(name,age,pos)
    
    
    
  • 案例分析

    SHOW INDEX FROM staffs
    # 1.全职匹配我最爱:所有建的索引都用上
    EXPLAIN SELECT * FROM staffs WHERE name = 'July';
    EXPLAIN SELECT * FROM staffs WHERE name = 'July' AND age = 23;
    EXPLAIN SELECT * FROM staffs WHERE name = 'july' AND age = 23 AND pos = 'dex';
    EXPLAIN SELECT * FROM staffs WHERE age = 23 AND pos = 'dex';
    EXPLAIN SELECT * FROM staffs WHERE pos = 'dex';
    # 2.最佳左前缀法则:如果索引了多列,要遵守最左前缀法则。即查询从索引的最左前列开始并不跳过索引中的列-----带头大哥不能死,中间兄弟不能断
    EXPLAIN SELECT * FROM staffs WHERE name = 'july' AND pos = 'dex';
    EXPLAIN SELECT * FROM staffs WHERE name = 'july' AND age = 23 AND pos = 'dex';
    # 3.不在索引列上做任何操作(计算、函数、(自动或手动)类型转换等),会导致索引失效而转向全表扫描
    EXPLAIN SELECT * FROM staffs WHERE left(name,4)='july';
    # 4.mysql不能使用索引中范围条件右边的列
    EXPLAIN SELECT * FROM staffs WHERE name = 'july' AND age > 22 AND pos = 'dex';
    # 5.尽量使用覆盖索引,减少select *
    EXPLAIN SELECT name,age,pos FROM staffs WHERE name = 'july' AND age = 23 AND pos = 'dex';
    EXPLAIN SELECT name,age,pos FROM staffs WHERE name = 'july' AND age > 22 AND pos = 'dex';
    # 6.使用不等于(!=或者<>)的时候会导致全表扫描
    EXPLAIN SELECT * FROM staffs WHERE name != 'July';
    # 7.is null,is not null 也无法使用索引
    EXPLAIN SELECT * FROM staffs WHERE name is null;
    EXPLAIN SELECT * FROM staffs WHERE name is not null;
    # 8.like以通配符开头,mysql索引会变成全表扫描
    EXPLAIN SELECT * FROM staffs WHERE name = 'July';
    EXPLAIN SELECT * FROM staffs WHERE name LIKE '%July%';                               
    EXPLAIN SELECT * FROM staffs WHERE name LIKE '%July';
    EXPLAIN SELECT * FROM staffs WHERE name LIKE 'July%';
    
    # 如何解决必须使用like的方法------使用覆盖索引
    # ================建表=============================
    CREATE TABLE `tbl_user`(
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(30) DEFAULT NULL,
      `age` INT(11) DEFAULT NULL,
      `email` VARCHAR(20)  DEFAULT NULL,
      PRIMARY KEY (`id`)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
    insert into tbl_user(name,age,email) values ('1aa1',21,'b@123.com');
    insert into tbl_user(name,age,email) values ('2aa2',222,'a@163.com');
    insert into tbl_user(name,age,email) values ('3aa3',265,'6@163.com');
    insert into tbl_user(name,age,email) values ('4aa4',21,'d@163.com');
    # ===================BEFORE INDEX================================
    SHOW INDEX FROM tbl_user;
    
    SELECT * FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT * FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT id FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT name FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT age FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT email FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT id,name,age FROM tbl_user WHERE name LIKE '%aa%';
    # ===================CREATE INDEX================================
    CREATE INDEX idx_user_nameAge ON tbl_user(name,age);
    
    EXPLAIN SELECT id FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT name FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT age FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT name,age FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT id,name,age FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT * FROM tbl_user WHERE name LIKE '%aa%';
    EXPLAIN SELECT email FROM tbl_user WHERE name LIKE '%aa%';
    
    # 9.字符串不加单引号索引失效
    SELECT * FROM staffs
    SHOW INDEX FROM staffs
    EXPLAIN SELECT * FROM staffs WHERE name = 2000
    
    # 10.少用or,用他连接时也会索引失效
    EXPLAIN SELECT * FROM staffs WHERE name = 'july' or name = 'z3';
    
    
    
    
  • 练习:https://blog.csdn.net/csdn_kenneth/article/details/82936235

  • 总结

    • 带头大哥不能死
    • 中间兄弟不能断
    • 索引列上无计算
    • like %加右边
    • 范围之后全失效
    • 字符串里有引号

三、查询截取分析

0、mysql优化过程

  • 观察,至少跑1天,看看生产的慢sql情况

  • 开启慢查询日志,设置阈值,比如超过5秒的就是慢sql,并将它抓取出来

  • explain+慢sql分析

  • show profile

  • 进行sql数据库服务器的参数调优


  • 慢查询的开启并捕获

  • explain+慢sql分析

  • show profile 查重sql在mysql服务器里面的执行细节和生命周期情况

  • sql数据库服务去参数调优

1、查询优化

(1)永远小表驱动大表

https://blog.csdn.net/csdn_kenneth/article/details/82937322

  • 优化原则:小表驱动大表,即小的数据集驱动大的数据集

    select * from A where id in (selecet id from B);
    # 等价于
    for select id from B
    for select * from where A.id = B.id
    
    #当B表的数据集必须小于A表的数据集的时候,用in优于exists
    
    select * from A where exists (select 1 from B where B.id = A.id)
    # 等价于
    from select * from A
    from select * from B where B.id = A.id
    
    # 当A表的数据集小于B表的数据集是,用exist优于in
    
    
    
  • in 和 exists的区别: 如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in, 反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了 ,另外IN时不对NULL进行处理。

(2)ORDER BY关键字优化

https://blog.csdn.net/csdn_kenneth/article/details/82942137

  • order by子句,尽量使用Index排序,避免使用filesort方式排序

    • 数据准备
    CREATE TABLE `tblA`( 
      `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,  
       `age` INT(11) DEFAULT NULL,   
       `birth` timestamp not NULL 
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     
    insert into tblA(age,birth) values (22,now());
    insert into tblA(age,birth) values (23,now());
    insert into tblA(age,birth) values (24,now());
     
    create index idx_A_ageBirth on tblA(age,birth);
    select * from tblA;
    
    
    
    SHOW INDEX FROM tblA
    EXPLAIN SELECT * FROM tblA WHERE age>20 ORDER BY age;
    EXPLAIN SELECT * FROM tblA WHERE age>20 ORDER BY age,birth;
    EXPLAIN SELECT * FROM tblA WHERE age>20 ORDER BY birth;
    EXPLAIN SELECT * FROM tblA WHERE age>20 ORDER BY birth,age;
    
    EXPLAIN SELECT * FROM tblA ORDER BY birth;
    EXPLAIN SELECT * FROM tblA WHERE birth> '2016-01-28 00:00:00' ORDER BY birth;
    EXPLAIN SELECT * FROM tblA WHERE birth> '2016-01-28 00:00:00' ORDER BY age;
    EXPLAIN SELECT * FROM tblA ORDER BY age ASC,birth DESC;`
    
    
  • order by满足两情况,会使用Index方式排序

    • order by 语句使用索引最左前列
    • 使用where子句与order by子句条件列组合满足索引最左前列
  • 尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀

  • filesort有两种算法

    • 双路排序:两次扫描磁盘,最终得到数据
    • 单路排序:从磁盘读取查询需要的所有列,按照order by列在buffer上对他们进行排序,然后扫描排序后的列表进行输出,避免了第二次读取数据。
  • 优化策略

    • order by时select * 是一个大忌,应该只query需要的字段。
    • 尝试提高sort_buffer_size
    • 尝试提高max_length_for_sort_data
  • 为排序使用索引

    • mysql两种排序方式:文件排序(filesort)或扫描有序索引排序(using index)
    • mysql能为排序与查询使用相同的索引

(3)GROUP BY关键字优化

  • group by实质是先排序后进行分组,遵照索引建的最佳左前缀
  • 当无法使用索引列,增大max_length_for_sort_data和sort_buffer_size
  • where高于having,能写在where限定的条件就不要写在having中

2、慢查询日志

https://blog.csdn.net/csdn_kenneth/article/details/82942398

https://blog.csdn.net/qq_40884473/article/details/89455740

(1)概念

  • MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句。具体环境中,运行时间超过long_query_time值(默认为10秒)的SQL语句,则会被记录到慢查询日志中。

(2)如何使用

  • 默认情况下,mysql数据库没有开启慢查询日志,需要手动设置。
  • 如果不是调优需要的话,不建议启动,因为开启慢查询日志会影响性能。
  • 查看慢查询是否开启
    • SHOW VARIABLES LIKE ‘%slow_query_log%’;
  • 开启慢查询,只对当前数据库有效,并且mysql重启后会失效
    • SET GLOBAL slow_query_log = 1;
  • 永久生效(不建议):修改my.cnf文件,在[mysqld]下增加或修改参数
    • slow_query_log = 1;
    • slow_query_log_file = /var/lib/mysql/lixp-slow.log
  • 查看阈值(默认10秒)
    • SHOW VARIABLES LIKE ‘%long_query_time%’;
  • 设置阈值
    • set global long_query_time=3;
    • 重新连接数据库
  • 慢查询分析工具:mysqldumpslow

3、批量数据脚本

https://blog.csdn.net/csdn_kenneth/article/details/82942888

(1)建表

create table dept(
`id` INT unsigned primary key NOT NULL AUTO_INCREMENT,
`deptno` mediumint unsigned not NULL DEFAULT 0,
`dname` varchar(20) not null default "",
`loc`  VARCHAR(13)  not NULL DEFAULT ""
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
 
create table emp(
 id int unsigned primary key not null auto_increment,
 empno int unsigned not null default 0,
 ename varchar(20) not null default "",
 job varchar(20) not null default "",
 mgr int unsigned not null default 0,
 hiredate date not null,
 sal decimal(7,2) not null,
 comm decimal(7,2) not null,
 deptno int unsigned not null default 0
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;


(2)设置参数

log_bin_trust_function_creators = 1

SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
SET GLOBAL log_bin_trust_function_creators = 1;

(3)创建函数,保证每条数据都不同

  • 产生随机字符串

    delimiter $$
    create function rand_string(n int) returns varchar(255)
    begin 
     declare chars_str varchar(100) default 'qazwsxedcrfvtgbyhnujmikolpQAZWSXEDCRFVTGBYHNUJMIKOLP';
     declare return_str varchar(255) default '';
     declare i int default 0;
     while i < n do
       set return_str = concat(return_str,substring(chars_str,floor(1+rand()*52),1));
       set i = i + 1;
     end while;
     return return_str;
    end $$
    
    
  • 随机产生部门编号

    delimiter $$
     create function rand_num() returns int(5)
     begin  
       declare i int default 0;
       set i = floor(100 + rand()*10);
     return i;
    end$$ 
    
    

(4)创建存储过程

  • 创建往emp表中插入数据的存储过程

    create procedure insert_emp(in start int(10),in max_num int(10))
    begin 
     declare i int default 0;
     set autocommit = 0;
     repeat
      set i = i + 1;
      insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
      values
      ((start+i),rand_string(6),'saleman',00001,curdate(),2000,400,rand_num());
      until i = max_num
      end repeat;
      commit;
    end
    
    
  • 创建往dept表中插入数据的存储过程

    create procedure insert_dept(in start int(10),in max_num int(10))
    begin 
     declare i int default 0;
     set autocommit = 0;
     repeat
     set i = i + 1;
      insert into dept(deptno,dname,loc)
      values((start+i),rand_string(10),rand_string(8));
     until i = max_num
     end repeat;
     commit;
    end
    
    
    

(5)调用存储过程

  • dept

    call insert_dept(100,10);
    
    
  • emp

    call insert_emp(100001,500000);
    
    

4、SHOW PROFILE

https://blog.csdn.net/csdn_kenneth/article/details/82948957

(1)概念

  • 是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于sql的调优测量
  • 默认情况下,参数处于关闭状态,并保存最近15次的运行结果

(2)分析步骤

  • 是否支持,看看当前的mysql版本是否支持

    SHOW VARIABLES LIKE 'profiling';
    
    
  • 开启功能,默认是关闭,使用前需要开启

    SET profiling = on;
    
    
  • 运行sql

  • 查看结果

    show profiles
    
    
  • 诊断sql

    show profiles cpu,block io for query,上一步的问题sql数字号码
    
    
  • 参数设置

    • ALl:显示所有的开销信息
    • block io :显示io相关开销
    • context switches:上下文切换相关开销
    • cpu:显示cpu相关开销
    • ipc:显示发送和接收相关开销信息
    • memory:显示内存相关开销信息
    • page faults:显示页面错误相关开销信息
    • source:显示和source相关的开销信息
    • swaps:显示交换次数相关开销的信息
  • 日常开发需要注意的结论

    • converting Heap to myISAM:查询结果太大,内存都不够用了,往磁盘上搬了
    • creating tmp table:创建临时表
      • 拷贝数据到临时表
      • 用完再删除
    • copying to tmp table on disk:把内存中临时表复制到磁盘
    • locked

5、全局查询日志

https://blog.csdn.net/csdn_kenneth/article/details/82948957

  • 只允许在测试环境用,不能在生产环境中使用
  • 配置启用
    • general_log=1
    • general_log_file = /path/logfile
  • 编码启用
    • set global general_log = 1;
    • set global log_output = ‘Table’;

四、mysql锁机制

1、分类

  • 从对数据操作的类型
    • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会相互影响
    • 写锁(排他锁):当前写操作没有完成前,它会阻断其他写锁和读锁
  • 从对数据操作的粒度分
    • 表锁
    • 行锁
    • 页锁

2、表锁

https://blog.csdn.net/csdn_kenneth/article/details/82949269

(1)特点

  • 偏向MyISAM存储引擎,开销小,加锁快;
  • 无死锁
  • 锁定粒度大,发送锁冲突的概率高,并发度低

(2)案例分析

  • 建表

    CREATE TABLE mylock (
    	id INT NOT NULL PRIMARY KEY auto_increment,
    	NAME VARCHAR (20)
    ) ENGINE myisam;
     
    INSERT INTO mylock (NAME) VALUES('a');
    INSERT INTO mylock (NAME) VALUES('b');
    INSERT INTO mylock (NAME) VALUES('c');
    INSERT INTO mylock (NAME) VALUES('d');
    INSERT INTO mylock (NAME) VALUES('e');
     
    select * from mylock;
    
    
  • 手动增加表锁

    lock table 表名字 read(write),表名字2 read(write), 其他;
    
    
  • 手动解锁

    unlock tables;
    
    
  • 查看加过锁的表

    show open tables;
    
    
  • 加读锁

    # session1
    lock table mylock read;
    SELECT * from mylock;  # 执行成功
    UPDATE mylock SET name = 'a2' WHERE id = 1; # 执行不成功
    SELECT * from book; # 执行不成功
    
    
    # session2
    SELECT * from mylock;  # 执行成功
    UPDATE mylock SET name = 'a2' WHERE id = 1; # 阻塞,等待session1中释放
    SELECT * from book; # 执行成功
    
    
  • 加写锁

    # session1
    lock table mylock write;
    SELECT * from mylock;  # 执行成功
    UPDATE mylock SET name = 'a2' WHERE id = 1; # 执行成功
    SELECT * from book; # 执行不成功
    
    
    # session2
    SELECT * from mylock;  # 阻塞,等待session1中释放
    UPDATE mylock SET name = 'a2' WHERE id = 1; # 阻塞,等待session1中释放
    SELECT * from book; # 执行成功
    
    

(3)案例结论

  • MyISAM在执行查询语句前,会自动给所涉及的所有表加读锁,在执行修改操作前,会自动给所涉及的表加写锁

  • Mysql的表级锁有两种模式

    • 表共享锁(Table Read Lock)

    • 表独占锁(Table Write Lock)

      锁类型可否兼容读操作写操作
      读锁
      写锁
  • 结论

    • 对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其他进程的写操作。
    • 对MyISAM表的写操作(加写锁),不阻塞其他进程对同一表的读、写请求。只有当写锁释放后,才会执行其他进程的写操作。
  • 读锁会阻塞写,但是不会阻塞读。写锁会把读写都阻塞

2、行锁

https://blog.csdn.net/csdn_kenneth/article/details/82949960

(1)特点

  • 偏向InnoDB存储引擎,开销大,加锁慢;
  • 会出现死锁;
  • 锁定粒度最小,发生锁冲突的概率最低,并发度最高。
  • InnoDB与MyISAM最大的不同
    • 支持事务
    • 采用行锁

(2)复习

  • 事务(ACID)

    • 原子性:事务是一个原子操作,其对数据的修改,要么都执行,要么都回滚
    • 一致性:在事务的开始和完成时,数据都必须保持一致状态。
      • 所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。
      • 事务结束时,所有的内部数据结构也都必须是正确的。
    • 隔离性:多个事务同时操作相同数据库的同一个数据时,一个事务的执行不受另外一个事务的干扰
      • 事务处理过程中的中间状态对外部是不可见的。
    • 持久性:一个事务一旦提交,则数据将持久化到本地,除非其他事务对其进行修改
      • 它对数据的修改是永久性的
  • 并发事务带来的问题

    https://blog.csdn.net/weixin_43797885/article/details/103486585

    • 更新丢失:当两个多多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生更新丢失的问题
    • 脏读:读取未提交数据
      • A事务读取B事务尚未提交的数据,此时如果B事务发生了错误并执行了回滚,那么A事务读取到的数据就是脏数据.
    • 不可重复读:前后多次读取,数据内容不一致
      • 事务A在执行读取操作,由于整个事务A比较大,前后读取同一条数据需要的时间比较长,在事务A第一次读取某条数据之后,事务B对这条数据执行了修改操作,此时事务A第二次读取这条数据,读取到的数据内容和第一次读取到的内容不一致.
    • 幻读:前后多次读取,数据总量不一致
      • 事务A想统计数据的总量,事务A在执行读取操作统计查询到数据总量后,此时事务B执行了新增数据的操作并且对数据进行了提交.这个时候事务A读取统计的数据总量和之前统计的数据总量不一样,平白无故多了几条数据,我们称为幻读.
  • 事务的隔离级别

    • READ UNCOMMITTED:读未提交
    • READ COMMITTED :读已提交,可以避免脏读
    • REPEATABLE READ :可重复读,可以避免脏读、不可重复读和一部分幻读
    • SERIALIZABLE:可序列化,可以避免脏读、不可重复读和幻读
    读数据一致性脏读不可重复读幻读
    read uncommitted最低级别
    read committed语句级×
    repeatable read事务级××
    serializable最高级别,事务级×××
  • 查看事务的隔离级别

    SHOW VARIABLES LIKE 'tx_isolation'
    
    

(3)案例分析

  • 建表

    CREATE TABLE test_innodb_lock (a INT(11), b VARCHAR(16)) ENGINE = INNODB;
     
    INSERT INTO test_innodb_lock VALUES(1, 'b2');
    INSERT INTO test_innodb_lock VALUES(3, '3');
    INSERT INTO test_innodb_lock VALUES(4, '4000');
    INSERT INTO test_innodb_lock VALUES(5, '5000');
    INSERT INTO test_innodb_lock VALUES(6, '6000');
    INSERT INTO test_innodb_lock VALUES(7, '7000');
    INSERT INTO test_innodb_lock VALUES(8, '8000');
    INSERT INTO test_innodb_lock VALUES(9, '9000');
    INSERT INTO test_innodb_lock VALUES(1, 'b1');
     
    create index test_innodb_a_ind on test_innodb_lock(a);
    create index test_innodb_lock_b_ind on test_innodb_lock(b);
     
    select * from test_innodb_lock;
    
    
    
  • 行锁定演示

    # session1
    SET autocommit = 0;
    # case1
    SELECT * from test_innodb_lock;
    UPDATE test_innodb_lock set b = '4001' WHERE a = 4;  # 更新数据
    SELECT * from test_innodb_lock; # 数据已更新
    COMMIT; # 事务递交后数据可以使用
    
    # case2
    UPDATE test_innodb_lock set b = '4002' WHERE a = 4;  # 更新数据
    SELECT * from test_innodb_lock; # 数据已更新
    COMMIT;
    
    
    # session2
    # case1
    SELECT * from test_innodb_lock; # 数据未更新
    # session1 commint 后再执行
    SELECT * from test_innodb_lock; # 数据已更新
    
    # case2
    UPDATE test_innodb_lock set b = '4003' WHERE a = 9;  # 可以执行
    UPDATE test_innodb_lock set b = '4003' WHERE a = 4;  # 阻塞,等待session1中释放
    
    
  • 无索引行升级为表锁

  • 间隙锁危害

    • 间隙锁:
      • 当我们用范围条件而不是相等条件检索数据,请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围但并不存在的记录,叫做“间隙(GAP)”,InnoDB,也会对这个间隙加锁。
    • 危害:
      • 因为query执行过程中通过范围查找的话,他会锁定整个范围内所有的索引值,即使这个键值并不存在。
      • 当锁定一个范围键值后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。
  • 面试题:常考如何锁定一行

    begin;
    select * from 表名 where a =for update; # 手动上锁
    commit;
    
    

(4)案例结论

  • InnoDB存储引擎由于采用了行级锁定,虽然在锁定机制实现方面所带来的性能损耗会比表级锁定要更高一些,但是在整体并发处理能力方面要比MyISAM更好。

(5)行锁分析

  • 查询语句

    SHOW STATUS LIKE 'innodb_row_lock%'
    
    
    • Innodb_row_lock_current_waits:当前正在等待锁定的数量
    • Innodb_row_lock_time(等待的总时长):从系统启动到现在锁定总时间长度
    • Innodb_row_lock_time_avg(等待的平均时长):每次等待锁花费的平均时间
    • Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花费的时间
    • Innodb_row_lock_waits(等待的总次数):系统启动后到现在总共等待的次数

(6)优化建议

  • 尽可能让所有的数据检索都通过索引来完成,避免无索引行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能减少检索调剂,避免间隙锁
  • 尽量控制事务大学,减少锁定资源量和时间长度
  • 尽可能低级别事务隔离

五、主从复制

https://my.oschina.net/u/3773384/blog/1810111

https://www.cnblogs.com/gl-developer/p/6170423.html

  • 主机修改配置文件

    # mysql
    [mysqld]
    port = 3306
    #设置server-id
    server-id = 1
    #开启二进制日志
    log-bin=mysql-bin 
    
    socket = /var/run/mysqld/mysqld.sock
    
    
  • 从机器修改配置文件

    # mysql2
    [mysqld]
    port = 3306
    ## 设置server_id,注意要唯一
    server-id=101  
    ## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
    log-bin=mysql-slave-bin   
    ## relay_log配置中继日志
    relay_log=edu-mysql-relay-bin 
    socket = /var/run/mysqld/mysqld.sock
    
    
  • 重启服务器

  • 关闭防火墙

  • 在主机上建立账户并授权slave

    CREATE USER 'repl'@'123.57.44.85' IDENTIFIED BY 'slavepass';#创建用户
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'123.57.44.85';#分配权限
    flush privileges;   #刷新权限
    SHOW MASTER STATUS; # 查看状态
    
    
  • 在从机上配置需要复制的主机

    CHANGE MASTER TO 
    MASTER_HOST='ip',
    MASTER_PORT=3306,
    MASTER_USER='backup',
    MASTER_PASSWORD='backup';
    
    # MASTER_HOST 填Navicat连接配置中的ip应该就可以
    # MASTER_PORT 主容器的端口
    # MASTER_USER 同步账号的用户名
    # MASTER_PASSWORD 同步账号的密码
    
    show slave status; # 查看状态
    START SLAVE; # 启动主从复制
    STOP SLAVE; # 停止主从复制
    
    
    
  • 查看错误日志: docker logs 容器ID -f

  • 在主机新建库、表并操作,查看从机内容

  • 读写分离

    https://blog.csdn.net/fsx2550553488/article/details/80575698

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值