当前位置: 首页 > news >正文

MySQL高级第二讲

目录

二、MySQL高级02

2.1 触发器

2.1.1 触发器介绍

2.1.2 创建触发器

2.2 MySQL的体系结构

2.3 存储引擎

2.3.1 存储引擎概述

2.3.2 各种存储引擎特性

2.3.3 InnoDB

2.3.4 MyISAM

2.3.5 MEMORY

2.3.6 MERGE

2.3.7 存储引擎的选择

2.4 优化sql

2.4.1 查看sql执行效率

2.4.2 定位低效率执行sql

2.4.3 explain分析执行计划

2.4.4 用show profile分析SQL

2.4.5 trace分析优化器执行计划

2.5 索引的使用

2.5.1 验证索引提升查询效率

2.5.2 避免索引失效

2.5.3 查看索引使用情况

2.6 sql优化

2.6.1 优化insert语句

2.6.2 优化order by语句

2.6.3 优化group by语句

2.6.4 优化嵌套查询


二、MySQL高级02

2.1 触发器

2.1.1 触发器介绍

触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作 。

使用别名 OLD 和 NEW 来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。

触发器类型NEW 和 OLD的使用
INSERT 型触发器NEW 表示将要或者已经新增的数据
UPDATE 型触发器OLD 表示修改之前的数据 , NEW 表示将要或已经修改后的数据
DELETE 型触发器OLD 表示将要或者已经删除的数据

2.1.2 创建触发器

语法:
create [or replace] trigger 触发器名触发时间 {before | after}           -- view 中是 instead of触发事件 {insert | update | delete} -- dml、ddl、database
on 触发对象                            -- table、view、schema、database 触发频率 {for each row}             -- 行级触发器。默认:语句级触发器[follows 其它触发器名]               -- 多个触发器执行的 前后顺序[when 触发条件]
beginpl/sql 语句;
end;
​
​
示例:
-- 创建日志表
create table emp_logs(id int(11) not null auto_increment,operation varchar(20) not null comment '操作类型, insert/update/delete',operate_time datetime not null comment '操作时间',operate_id int(11) not null comment '操作表的ID',operate_params varchar(500) comment '操作参数',primary key(`id`)
)engine=innodb default charset=utf8;

1、 创建 insert 型触发器,完成插入数据时的日志记录 :

-- 测试添加的触发器:添加触发器生效一般都是在添加操作之后进行的
CREATE TRIGGER tri_emp_insert
AFTER INSERT ON emp
FOR EACH ROW
BEGIN
-- 在emp表操作之后,在emp_logs表中留下日志INSERT INTO emp_logs VALUES(null,'insert',NOW(),new.id,CONCAT('名字是:',new.name1,'工资是:',new.salary));
END;
​
​
-- 测试添加操作
INSERT INTO emp VALUES(null,'王中王',28,10000);
​
-- 查看emp_logs表中的数据
SELECT * FROM emp_logs;

2、 创建 update 型触发器,完成更新数据时的日志记录 :

-- 测试修改的触发器:修改触发器生效一般都是在修改操作之后进行的
CREATE TRIGGER tri_emp_update
AFTER UPDATE ON emp
FOR EACH ROW
BEGIN
-- 在emp表进行修改操作之后,在emp_logs表中留下日志,这里使用的还是旧的idINSERT INTO emp_logs VALUES(null,'update',NOW(),old.id,CONCAT('新的名字是:',new.name1,'新的工资是:',new.salary,'--旧的名字是:',old.name1,'旧的工资是',old.salary));
END;
​
-- 如果列名是关键字时,要使用反引号将其引起来 比如name在进行操作时,就需要写成`name`
UPDATE emp SET name1='test',salary=6666 WHERE id = 5;
​
-- 查看emp_logs表中的数据
SELECT * FROM emp_logs;

案例1:周六的时候不能修改emp表的数据

① DAYOFWEEK(date)
返回日期date的星期索引(1=星期天,2=星期一, ……7=星期六)。这些索引值对应于ODBC标准。
② WEEKDAY(date)
返回date的星期索引(0=星期一,1=星期二, ……6= 星期天)。
​
-- 周六的时候不能修改emp表中的数据
-- 这里使用before或者after是不影响触发器操作的
DROP TRIGGER IF EXISTS tri_emp_update_class1;
CREATE TRIGGER tri_emp_update_class1
BEFORE UPDATE ON emp
FOR EACH ROW
BEGINDECLARE wday INT;   -- 定义一个变量用于判断SELECT WEEKDAY(NOW()) INTO wday;  -- 查询当前时间是周几并赋值给wday-- 根据今天是周几进行判断IF wday=5 THEN-- 错误代码,设置错误信息SIGNAL SQLSTATE '40000' SET message_text = '今天是周六,不能修改数据';END IF;
END;
​
-- 案例1;修改测试
UPDATE emp SET name1='test1',salary=5555 WHERE id = 5;
-- 测试结果
[SQL] -- 案例1;修改测试
UPDATE emp SET name1='test1',salary=5555 WHERE id = 5;
[Err] 1644 - 今天是周六,不能修改数据

案例2:周五的时候不能修改emp表中的数据,周六可以修改emp的员工的工资不能降低

-- 周五的时候不能修改emp表中的数据
-- 周六可以修改emp的员工的工资不能降低
DROP TRIGGER IF EXISTS tri_emp_update_class2;
CREATE TRIGGER tri_emp_update_class2
BEFORE UPDATE ON emp
FOR EACH ROW
BEGINDECLARE wday INT;   -- 定义一个变量用于判断SELECT WEEKDAY(NOW()) INTO wday;  -- 查询当前时间是周几并赋值给wday-- 根据今天是周几进行判断IF wday=4 THEN-- 错误代码,设置错误信息SIGNAL SQLSTATE 'KL900' SET message_text = '今天是周五,不能修改员工的工资';ELSEIF wday=5 THENIF old.salary > new.salary THENSIGNAL SQLSTATE '50000' SET message_text = '今天是周六,员工工资不可以降低';END IF;END IF;
END;
​
-- 案例2;修改测试
UPDATE emp SET name1='test1',salary=5555 WHERE id = 5;
[SQL] UPDATE emp SET name1='test1',salary=5555 WHERE id = 5;
[Err] 1644 - 今天是周六,员工工资不可以降低

3、删除触发器

drop trigger [schema_name.]trigger_name
如果没有指定 schema_name,默认为当前数据库 。
​
​
-- 删除触发器
DROP TRIGGER IF EXISTS emp_logs_delete_trigger;
CREATE TRIGGER emp_logs_delete_trigger
AFTER DELETE 
ON emp 
FOR EACH ROW 
BEGININSERT INTO emp_logs (id,operation,operate_time,operate_id,operate_params) VALUES(null,'delete',now(),old.id,concat('删除前(id:',old.id,', name:',old.name1,', age:',old.age,', salary:',old.salary,')')); 
END;
-- 删除的触发器测试
DELETE FROM emp WHERE id = 5;
​
​
-- 查看emp_logs表中的数据
SELECT * FROM emp_logs;

触发器在获取登录用户的id时可能获取不到

4、查看触发器

可以通过执行 SHOW TRIGGERS 命令查看触发器的状态、语法等信息。语法结构 : 
-- 查看触发器测试
show triggers ;

2.2 MySQL的体系结构

官网: https://www.cnblogs.com/zh-ch/p/12803325.html

 

整个MySQL Server由以下组成

- Connection Pool : 连接池组件
​
- Management Services & Utilities : 管理服务和工具组件
​
- SQL Interface : SQL接口组件
​
- Parser : 查询分析器组件
​
- Optimizer : 优化器组件
​
- Caches & Buffers : 缓冲池组件
​
- Pluggable Storage Engines : 存储引擎
​
- File System : 文件系统

注: SQL Interface 中

DML(Data Manipulation Language)数据操作语言-数据库的基本操作,SQL中处理数据等操作统称为数据操纵语言,简而言之就是实现了基本的“增删改查”操作。包括的关键字有:select、update、delete、insert、merge

DDL(Data Definition Language)数据定义语言-用于定义和管理 SQL 数据库中的所有对象的语言,对数据库中的某些对象(例如,database,table)进行管理。包括的关键字有:create、alter、drop、truncate、comment、grant、revoke

1) 连接层最上层是一些客户端和链接服务,包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
​
2) 服务层第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如 过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等, 最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。
​
3) 引擎层存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。
​
4)存储层数据存储层, 主要是将数据存储在文件系统之上,并完成与存储引擎的交互。
和其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

2.3 存储引擎

2.3.1 存储引擎概述

存储引擎是基于表的,而不是基于库的

    和大多数的数据库不同, MySQL中有一个存储引擎的概念, 针对不同的存储需求可以选择最优的存储引擎。存储引擎就是存储数据,建立索引,更新查询数据等等技术的实现方式 。存储引擎是基于表的,而不是基于库的。所以存储引擎也可被称为表类型。Oracle,SqlServer等数据库只有一种存储引擎。MySQL提供了插件式的存储引擎架构。所以MySQL存在多种存储引擎,可以根据需要使用相应引擎,或者编写存储引擎。MySQL5.0支持的存储引擎包含 : InnoDB 、MyISAM 、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等,其中InnoDB和BDB提供事务安全表,其他存储引擎是非事务安全表。创建新表时如果不指定存储引擎,那么系统就会使用默认的存储引擎,MySQL5.5之前的默认存储引擎是MyISAM,5.5之后就改为了InnoDB。

可以通过指定 show engines , 来查询当前数据库支持的存储引擎 :

 

查看Mysql数据库默认的存储引擎 , 指令  show variables like '%storage_engine%' ;

 

2.3.2 各种存储引擎特性

下面重点介绍几种常用的存储引擎, 并对比各个存储引擎之间的区别, 如下表所示 :

特点InnoDBMyISAMMEMORYMERGENDB
存储限制64TB没有
事务安全==支持==
锁机制==行锁(适合高并发)====表锁==表锁表锁行锁
B树索引支持支持支持支持支持
哈希索引支持
全文索引支持(5.6版本之后)支持
集群索引支持
数据索引支持支持支持
索引缓存支持支持支持支持支持
数据可压缩支持
空间使用N/A
内存使用中等
批量插入速度
支持外键==支持==

表锁:

一般存在于MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
我们在编辑表,或者执行修改表的语句的时候,一般都会给表加上表锁,可以避免一些不同步的事情出现,表锁分为两种,一种是读锁,一种是写锁。
​
语法:
加锁:lock table 表名 read(write);
释放所有表的锁:unlock tables;
查看加锁的表:show open tables where in_use>0;

 

行锁:

一般存在于InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 
​
行锁支持事务,行锁只要是靠索引来运行的
​
行为: 
1、当我们对一行进行更新但是不提交的时候,其他进程也对该行进行更新则需要进行等待,这就是行锁。 
2、如果我们对一行数据进行更新,其他进程更新别的行是不会受影响的。
测试之前:我们需要把事务提交方式改为手动提交
show variables like 'autocommit'; 
set autocommit=0;     
​
mysql> show variables like 'autocommit';-- 查看事务是否自动提交
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)
-- 这里也可以使用 set autocommit='off';
mysql> set autocommit=0; -- 将事务的提交方式改为手动提交
Query OK, 0 rows affected (0.00 sec)
​
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)
​
​
-----------------------测试行锁-------------------
for update
如果在一条select语句后加上for update,则查询到的数据会被加上一条排它锁,其它事务可以读取,但不能进行更新和插入操作
​
start transaction;  -- 开始事务
select * from emp where id=1 for update;-- A用户对id=1的记录进行加锁
-- B用户无法对该记录进行操作
update user set name1='test' where id=1;
-- A用户commit以后则B用户可以对该记录进行操作
如下图所示:

 

 

行锁升级为表锁:

当我们的行锁涉及到索引失效的时候,会触发表锁的行为。

由于在column字段id上面建了索引,如果没有正常使用,会导致行锁变表锁
比如加单引号导致索引失效,行锁变表锁;
使用主键索引进行查询:  -- 使用主键id
select * from emp where id=1;
不使用主键索引进行查询  -- 不使用主键id
select * from emp where id='1'; -- 类型发生变化

2.3.3 InnoDB

InnoDB存储引擎是Mysql的默认存储引擎。InnoDB存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全。但是对比MyISAM的存储引擎,InnoDB写的处理效率差一些,并且会占用更多的磁盘空间以保留数据和索引。

InnoDB存储引擎不同于其他存储引擎的特点 :

1、事务控制

create table goods_innodb(id int NOT NULL AUTO_INCREMENT,name varchar(20) NOT NULL,primary key(id)
)ENGINE=innodb DEFAULT CHARSET=utf8;
-- ENGINE=INNODB 就是设置当前表的引擎为innodb,不设置默认也是innodb
-- 设置字符编码格式为utf8;
​
-- 开始事务
start transaction;  -- begin
insert into goods_innodb(id,name)values(null,'Meta22');
-- 提交事务   commit
commit;

 

 

2、外键约束

MySQL支持外键的存储引擎只有InnoDB , 在创建外键的时候, 要求父表必须有对应的索引 , 子表在创建外键的时候, 也会自动的创建对应的索引。

下面两张表中 , country_innodb是父表 , country_id为主键索引,city_innodb表是子表,country_id字段为外键,对应于country_innodb表的主键country_id 。

-- 创建国家表(父表)
create table country_innodb(country_id int NOT NULL AUTO_INCREMENT,country_name varchar(100) NOT NULL,primary key(country_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 创建城市表(子表)
create table city_innodb(city_id int NOT NULL AUTO_INCREMENT,city_name varchar(50) NOT NULL,country_id int NOT NULL,primary key(city_id),key idx_fk_country_id(country_id),CONSTRAINT `fk_city_country` FOREIGN KEY(country_id) REFERENCES country_innodb(country_id) ON DELETE RESTRICT ON UPDATE CASCADE-- 设置外键在删除是对应的操作时RESTRICT,修改时对应的操作是CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 添加数据
insert into country_innodb values(null,'China'),(null,'America'),(null,'Japan');
insert into city_innodb values(null,'Xian',1),(null,'NewYork',2),(null,'BeiJing',1);

在创建索引时, 可以指定在删除、更新父表时,对子表进行的相应操作,包括 RESTRICT、CASCADE、SET NULL 和 NO ACTION。

1)RESTRICT和NO ACTION相同, 是指限制在子表有关联记录的情况下, 父表不能更新;如果外键中对应修改的操作是RECTRICT或者NO ACTION,那么如果子表中有对应的数据,则父表不能进行修改如果外键中对应删除的操作是RECTRICT或者NO ACTION,那么字表中有数据时,父表不能删除eg:如果子表中有country_id=1的数据,那么父表中country_id=1的country_id这一列就不能修改;2)CASCADE表示父表在更新或者删除时,更新或者删除子表对应的记录;如果外键中对应修改的操作是CASCADE,那么父表修改country_id这一列时,会一并修改子表中对应的country_id如果外键中对应删除的操作是CASCADE,那么父表删除country_id这一列时,会一并删除子表中对应的行eg:如果父表将country_id=1修改为country_id=10,那么子表中country_id=1的列也会修改为103)SET NULL 则表示父表在更新或者删除的时候,子表的对应字段被SET NULL 。注:对应的子表里面的字段不能设置为not null。如果外键中对应修改或的操作是SET NULL,那么父表进行修改或删除时,会将子表中对应的列设置为null,所以字表中的外键字段不能设置为not null;
​
​
-- 查看这一张表的外键信息
show create table city_innodb ;
​
--删除外键
ALTER TABLE city_innodb DROP FOREIGN KEY idx_fk_country_id
​
--添加外键
ALTER TABLE `city_innodb` ADD CONSTRAINT `idx_fk_country_id` FOREIGN KEY ( `country_id` )
REFERENCES `country_innodb` ( `country_id` )
ON DELETE CASCADE ON UPDATE CASCADE

3、存储方式

InnoDB 存储表和索引有以下两种方式 :

①. 使用共享表空间存储, 这种方式创建的表的表结构保存在.frm文件中, 数据和索引保存在 innodb_data_home_dir 和 innodb_data_file_path定义的表空间中,可以是多个文件。

②. 使用多表空间存储, 这种方式创建的表的表结构仍然存在 .frm 文件中,但是每个表的数据和索引单独保存在 .ibd 中。

使用命令查看存储地址show global variables like "%datadir%"windows系统中位置:
mysql> show global variables like '%datadir%';
+---------------+---------------------------------------------+
| Variable_name | Value                                       |
+---------------+---------------------------------------------+
| datadir       | C:\ProgramData\MySQL\MySQL Server 5.7\Data\ |
+---------------+---------------------------------------------+
1 row in set, 1 warning (0.00 sec)
​
​
Linux系统中位置:
mysql> show global variables like "%datadir%";
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| datadir       | /var/lib/mysql/ |
+---------------+-----------------+
1 row in set (0.00 sec)

 

.opt文件代表着这是一个备份文件
MySQL中.frm文件:保存了每个表的元数据,包括表结构的定义等,该文件与数据库引擎无关。  
MySQL中.ibd文件:InnoDB引擎开启了独立表空间(my.ini中配置innodb_file_per_table = 1)产生的存放该表的数据和索引的文件。

2.3.4 MyISAM

MyISAM 不支持事务、也不支持外键,其优势是访问的速度快,对事务的完整性没有要求或者以SELECT、INSERT为主的应用基本上都可以使用这个引擎来创建表 。有以下两个比较重要的特点:

1、不支持事务

create table goods_myisam(id int NOT NULL AUTO_INCREMENT,name varchar(20) NOT NULL,primary key(id)
)ENGINE=myisam DEFAULT CHARSET=utf8;
​
insert into goods_myisam values(null,'test');

 

2、文件存储方式

每个MyISAM在磁盘上存储成3个文件,其文件名都和表名相同,但拓展名分别是 :

①  .frm (存储表定义) -- *.frm--表定义,是描述表结构的文件。
②  .MYD(MYData , 存储数据) -- *.MYD--"D"数据信息文件,是表的数据文件。
③  .MYI(MYIndex , 存储索引) -- *.MYI--"I"索引信息文件,是表数据文件中任何索引的数据树

 

2.3.5 MEMORY

Memory存储引擎将表的数据存放在内存中。每个MEMORY表实际对应一个磁盘文件,格式是.frm ,该文件中只存储表的结构,而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。MEMORY 类型的表访问非常地快,因为他的数据是存放在内存中的,并且默认使用HASH索引 , 但是服务一旦关闭,表中的数据就会丢失。

2.3.6 MERGE

1)MERGE存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,MERGE表本身并没有存储数据,对MERGE类型的表可以进行查询、更新、删除操作,这些操作实际上是对内部的MyISAM表进行的。

2)对于MERGE类型表的插入操作,是通过INSERT_METHOD子句定义插入的表,可以有3个不同的值

值为first 对应的插入操作,是作用在第一个表上
值为last  对应的插入操作,是作用在最后一个表上
值为no    不能对折个merge表执行插入操作

3)可以对MERGE表进行DROP操作,但是这个操作只是删除MERGE表的定义,对内部的表是没有任何影响的。

4)被合并之后的表是不能轻易删除的

 

-- 测试merge
-- 创建order_1990表
create table order_1990(order_id int ,order_money double(10,2),order_address varchar(50),primary key (order_id)
)engine = myisam default charset=utf8;
​
-- 创建order_1991表
create table order_1991(order_id int ,order_money double(10,2),order_address varchar(50),primary key (order_id)
)engine = myisam default charset=utf8;
​
-- 添加数据
insert into order_1990 values(1,100.0,'北京');
insert into order_1990 values(2,100.0,'上海');
​
insert into order_1991 values(10,200.0,'北京');
insert into order_1991 values(11,200.0,'上海');
​
​
-- 合并两张表
-- 三张表的字段和主键都是一样的
-- 这里的存储引擎是merge,第一张表是order_1990,第二张表是order_1991,
-- 添加的方法选择的是last,也就是在对order_all进行插入操作时,会作用到order_1991上面
create table order_all(order_id int ,order_money double(10,2),order_address varchar(50),primary key (order_id)
)engine = merge union = (order_1990,order_1991) INSERT_METHOD=LAST default charset=utf8;

查询表中的数据:

mysql> select * from order_1990;
+----------+-------------+---------------+
| order_id | order_money | order_address |
+----------+-------------+---------------+
|        1 |      100.00 | 北京          |
|        2 |      100.00 | 上海          |
+----------+-------------+---------------+
2 rows in set (0.00 sec)
​
mysql> select * from order_1991;
+----------+-------------+---------------+
| order_id | order_money | order_address |
+----------+-------------+---------------+
|       10 |      200.00 | 北京          |
|       11 |      200.00 | 上海          |
+----------+-------------+---------------+
2 rows in set (0.00 sec)
​
mysql> select * from order_all;
+----------+-------------+---------------+
| order_id | order_money | order_address |
+----------+-------------+---------------+
|        1 |      100.00 | 北京          |
|        2 |      100.00 | 上海          |
|       10 |      200.00 | 北京          |
|       11 |      200.00 | 上海          |
+----------+-------------+---------------+
4 rows in set (0.00 sec)

向order_all中添加数据,再次查询三张表的数据

-- 添加数据
insert into order_all values(100,10000.0,'西安');
​
mysql> select * from order_1990;
+----------+-------------+---------------+
| order_id | order_money | order_address |
+----------+-------------+---------------+
|        1 |      100.00 | 北京          |
|        2 |      100.00 | 上海          |
+----------+-------------+---------------+
2 rows in set (0.00 sec)
​
mysql> select * from order_1991;
+----------+-------------+---------------+
| order_id | order_money | order_address |
+----------+-------------+---------------+
|       10 |      200.00 | 北京          |
|       11 |      200.00 | 上海          |
|      100 |    10000.00 | 西安          |
+----------+-------------+---------------+
3 rows in set (0.00 sec)
​
mysql> select * from order_all; -- 合并的表是不存储数据的,即使删掉合并的表,对另外两张表也是没有影响的 
+----------+-------------+---------------+
| order_id | order_money | order_address |
+----------+-------------+---------------+
|        1 |      100.00 | 北京          |
|        2 |      100.00 | 上海          |
|       10 |      200.00 | 北京          |
|       11 |      200.00 | 上海          |
|      100 |    10000.00 | 西安          |
+----------+-------------+---------------+
5 rows in set (0.00 sec)

2.3.7 存储引擎的选择

    在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。以下是几种常用的存储引擎的使用环境。InnoDB : 是Mysql的默认存储引擎,用于事务处理应用程序,支持外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询意外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。InnoDB存储引擎除了有效的降低由于删除和更新导致的锁定, 还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB是最合适的选择。MyISAM : 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
​MEMORY:将所有数据保存在RAM中,在需要快速定位记录和其他类似数据环境下,可以提供几块的访问。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,其次是要确保表的数据可以恢复,数据库异常终止后表中的数据是可以恢复的。MEMORY表通常用于更新不太频繁的小表,用以快速得到访问结果。
​MERGE:用于将一系列等同的MyISAM表以逻辑方式组合在一起,并作为一个对象引用他们。MERGE表的优点在于可以突破对单个MyISAM表的大小限制,并且通过将不同的表分布在多个磁盘上,可以有效的改善MERGE表的访问效率。这对于存储诸如数据仓储等VLDB环境十分合适。

2.4 优化sql

在应用的的开发过程中,由于初期数据量小,开发人员写 SQL 语句时更重视功能上的实现,但是当应用系统正式上线后,随着生产数据量的急剧增长,很多 SQL 语句开始逐渐显露出性能问题,对生产的影响也越来越大,此时这些有问题的 SQL 语句就成为整个系统性能的瓶颈,因此我们必须要对它们进行优化,本章将详细介绍在 MySQL 中优化 SQL 语句的方法。当面对一个有 SQL 性能问题的数据库时,我们应该从何处入手来进行系统的分析,使得能够尽快定位问题 SQL 并尽快解决问题。

2.4.1 查看sql执行效率

MySQL 客户端连接成功后,通过 show [session|global] status 命令可以提供服务器状态信息。
show [session|global] status 
参数:
[session|global]可以不写
session:当前连接的统计结果(默认使用)
global:查看自数据库上次启动至今的统计结果
​
下面的命令显示了当前 session 中所有统计参数的值:
-- com后面是七个下划线,代表搜索操作为6个字符的操作进行的次数
mysql> show status like 'Com_______';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog    | 0     |
| Com_commit    | 3     |
| Com_delete    | 0     |
| Com_import    | 0     |
| Com_insert    | 2     |
| Com_repair    | 0     |
| Com_revoke    | 0     |
| Com_select    | 10    |
| Com_signal    | 0     |
| Com_update    | 0     |
| Com_xa_end    | 0     |
+---------------+-------+
11 rows in set (0.00 sec)

Com_xxx 表示每个 xxx 语句执行的次数,我们通常比较关心的是以下几个统计参数。

Com_***      :  这些参数对于所有存储引擎的表操作都会进行累计。
Innodb_*** :  这几个参数只是针对InnoDB 存储引擎的,累加的算法也略有不同。
​
mysql> show global status like 'Com_______';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog    | 0     |
| Com_commit    | 3     |
| Com_delete    | 2     |
| Com_import    | 0     |
| Com_insert    | 10    |
| Com_repair    | 0     |
| Com_revoke    | 0     |
| Com_select    | 93    |
| Com_signal    | 1     |
| Com_update    | 8     |
| Com_xa_end    | 0     |
+---------------+-------+
11 rows in set (0.00 sec)
​
mysql> show status like 'Innodb______________';
+----------------------+------------+
| Variable_name        | Value      |
+----------------------+------------+
| Innodb_redo_log_uuid | 1075899837 |
| Innodb_os_log_fsyncs | 312        |
| Innodb_pages_created | 206        |
| Innodb_pages_written | 1017       |
| Innodb_row_lock_time | 5222       |
| Innodb_rows_inserted | 14         |
+----------------------+------------+
6 rows in set (0.00 sec)
参数含义
Com_select执行 select 操作的次数,一次查询只累加 1。
Com_insert执行 INSERT 操作的次数,对于批量插入的 INSERT 操作,只累加一次。
Com_update执行 UPDATE 操作的次数。
Com_delete执行 DELETE 操作的次数。
Innodb_rows_readselect 查询返回的行数。
Innodb_rows_inserted执行 INSERT 操作插入的行数。
Innodb_rows_updated执行 UPDATE 操作更新的行数。
Innodb_rows_deleted执行 DELETE 操作删除的行数。
Connections试图连接 MySQL 服务器的次数。
Uptime服务器工作时间。
Slow_queries慢查询的次数。

2.4.2 定位低效率执行sql

可以通过以下两种方式定位执行效率较低的 SQL 语句。

1、慢sql日志

慢查询日志 : 通过慢查询日志定位那些执行效率较低的 SQL 语句,用--log-slow-queries[=file_name]选项启动时,mysqld 写一个包含所有执行时间超过 long_query_time 秒的 SQL 语句的日志文件。 

 

-- 查看是否开启慢sql日志
SHOW global VARIABLES LIKE '%query%';
-- 开启慢sql
set global slow_query_log=on;
-- 设置慢sql的时间:代表超过1秒就是一个慢sql
set global long_query_time =1;
​
​
mysql> SHOW global VARIABLES LIKE '%query%';
+------------------------------+-----------------------------------+
| Variable_name                | Value                             |
+------------------------------+-----------------------------------+
| binlog_rows_query_log_events | OFF                               |
| ft_query_expansion_limit     | 20                                |
| have_query_cache             | NO                                |
| long_query_time              | 10.000000                         |
| query_alloc_block_size       | 8192                              |
| query_prealloc_size          | 8192                              |
| slow_query_log               | OFF                               |
| slow_query_log_file          | /var/lib/mysql/localhost-slow.log |
+------------------------------+-----------------------------------+
8 rows in set (0.00 sec)
​
mysql> set global slow_query_log='on';
Query OK, 0 rows affected (0.01 sec)
​
mysql> set global long_query_time=1;
Query OK, 0 rows affected (0.00 sec)

2、线程列表(show processlist)

show processlist  : 慢查询日志在查询结束以后才纪录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题,可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化 

 

1) id列,用户登录mysql时,系统分配的"connection_id",可以使用函数connection_id()查看
​
2) user列,显示当前用户。如果不是root,这个命令就只显示用户权限范围的sql语句
​
3) host列,显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户
​
4) db列,显示这个进程目前连接的是哪个数据库
​
5) command列,显示当前连接的执行的命令,一般取值为休眠(sleep),查询(query),连接(connect)等
​
6) time列,显示这个状态持续的时间,单位是秒
​
7) state列,显示使用当前连接的sql语句的状态,很重要的列。
state描述的是语句执行中的某一个状态。一个sql语句,以查询为例,
可能需要经过copying to tmp table、sorting result、sending data
等状态才可以完成
​
8) info列,显示这个sql语句,是判断问题语句的一个重要依据

开启慢sql之后就会在日志文件中记录慢sql,查询慢sql的日志的位置

-- 查询慢sql日志的名字
show variables like '%slow%';
​
-- 查看慢sql日志存放的位置
show variables like '%datadir%';

 

2.4.3 explain分析执行计划

通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN或者 DESC命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELE

查询sql语句的执行计划:
explain  select * from emp where id = 1;
或 desc  select * from emp where id = 1;

CT 语句执行过程中表如何连接和连接的顺序。

 

 

字段含义
idselect查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。
select_type表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个 SELECT)等
table输出结果集的表
type表示表的连接类型,性能由好到差的连接类型为( system ---> const -----> eq_ref ------> ref -------> ref_or_null----> index_merge ---> index_subquery -----> range -----> index ------> all ) 如果是all,代表没使用索引,效率过低
possible_keys表示查询时,可能使用的索引
key表示实际使用的索引
key_len索引字段的长度
rows扫描行的数量
extra执行情况的说明和描述

案例:

数据库表准备
-- 创建t_role表
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (`id` int NOT NULL AUTO_INCREMENT,`role` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,`status` int DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3;
​
-- 创建t_user表
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (`id` int NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '用户名',`password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '用户密码',`birthday` date DEFAULT NULL COMMENT '用户生日',`sex` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '用户性别',`rid` int DEFAULT NULL,`status` int DEFAULT NULL COMMENT '状态',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb3;
​
-- 创建t_user_role表
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (`id` int NOT NULL AUTO_INCREMENT,`uid` int DEFAULT NULL COMMENT '用户的id',`rid` int DEFAULT NULL COMMENT '角色的id',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3;
​
-- 添加数据
INSERT INTO `t_user_role` VALUES ('1', '1', '1');
​
INSERT INTO `t_user` VALUES ('1', 'zs', '123456', '2023-01-18', '男', '1', '2');
INSERT INTO `t_user` VALUES ('7', '666', '666', '2023-02-08', '女', '2', '1');
INSERT INTO `t_user` VALUES ('8', '3', '3', '2022-09-06', '男', '1', '1');
​
INSERT INTO `t_role` VALUES ('1', 'admin', '1');
INSERT INTO `t_role` VALUES ('2', 'user', '1');
INSERT INTO `t_role` VALUES ('10', 'superUser', '1');

1、explain之id

id 字段是 select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。
id 情况有2种 :
1) id 相同表示加载表的顺序是从上到下。
2) id 不同id值越大,优先级越高,越先被执行。
​
explain select * from t_role r, t_user u, t_user_role ru where r.id = ru.rid and u.id = ru.uid ;
​
explain select * from t_role where id in(select rid from t_user_role where uid =(select id from t_user where username='张三'));

 

2、explain之select_type

表示 SELECT 的类型,常见的取值,如下表所示:

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

3、 explain 之 table

展示这一行的数据是关于哪一张表的

4、 explain 之 type

type 显示的是访问类型,是较为重要的一个指标,可取值为:

type含义
NULLMySQL不访问任何表,索引,直接返回结果
system表只有一行记录(等于系统表),这是const类型的特例,一般不会出现
const表示通过索引一次就找到了,const 用于比较primary key 或者 unique 索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL 就能将该查询转换为一个常亮。const于将 "主键" 或 "唯一" 索引的所有部分与常量值进行比较
eq_ref类似ref,区别在于使用的是唯一索引,使用主键的关联查询,关联查询出的记录只有一条。常见于主键或唯一索引扫描
ref非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回所有匹配某个单独值的所有行(多个)
range只检索给定返回的行,使用一个索引来选择行。 where 之后出现 between , < , > , in 等操作。
indexindex 与 ALL的区别为 index 类型只是遍历了索引树, 通常比ALL 快, ALL 是遍历数据文件。
all将遍历全表以找到匹配的行

结果值从最好到最坏以此是:

NULL > 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

一般来说, 我们需要保证查询至少达到 range 级别,再不济也应该是index, 最好达到ref

5、 explain 之 key

key_len : 表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下, 长度越短越好 。
​
key : 实际使用的索引, 如果为NULL, 则没有使用索引。
​
possible_keys : 显示可能应用在这张表的索引, 一个或多个。 

6、 explain 之 rows

扫描行的数量。

7、 explain 之 extra

其他的额外的执行计划信息,在该列展示 。

extra含义
using filesort说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取, 称为 “文件排序”, 效率低。
using temporary使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于 order by 和 group by; 效率低
using index表示相应的select操作使用了覆盖索引, 避免访问表的数据行, 效率不错。

2.4.4 用show profile分析SQL

Mysql从5.0.37版本开始增加了对 show profiles 和 show profile 语句的支持。show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。

1、通过 have_profiling 参数,能够看到当前MySQL是否支持profile:
​
mysql> select @@have_profiling ;
+------------------+
| @@have_profiling |
+------------------+
| YES              |
+------------------+
1 row in set, 1 warning (0.00 sec)
​
​
2、默认profiling是关闭的,可以通过set语句在Session级别开启profiling:
mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           0 |
+-------------+
1 row in set, 1 warning (0.00 sec)
​
3、开启profiling 开关;
mysql> set  profiling=1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
​
mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           1 |
+-------------+
1 row in set, 1 warning (0.00 sec)
通过profile,我们能够更清楚地了解SQL执行的过程。1、先随便执行两条命令
select * from t_user ;
select count(*) from t_user;
​
2、再执行查看命令查看耗时
show profiles;
​
​
mysql> show profiles;
+----------+------------+-----------------------------+
| Query_ID | Duration   | Query                       |
+----------+------------+-----------------------------+
|        1 | 0.00015800 | select @@profiling          |
|        2 | 0.00023600 | select * from t_user        |
|        3 | 0.00017600 | select * from t_user        |
|        4 | 0.00012925 | commit                      |
|        5 | 0.00028350 | select * from t_user        |
|        6 | 0.05431525 | select count(*) from t_user |
+----------+------------+-----------------------------+
6 rows in set, 1 warning (0.00 sec)
​
3、通过show  profile for  query  query_id 语句可以查看到该SQL执行过程中每个线程的状态和消耗的时间:
mysql> show  profile for  query  6;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000067 | -- 
| Executing hook on transaction  | 0.000003 | -- 
| starting                       | 0.000007 | -- 开启
| checking permissions           | 0.000005 | -- 检查权限
| Opening tables                 | 0.000024 | -- 打开表
| init                           | 0.000005 | -- 初始化
| System lock                    | 0.000007 | -- 检查系统锁
| optimizing                     | 0.000005 | -- 分析
| statistics                     | 0.000014 | -- 统计
| preparing                      | 0.000010 | -- 准备
| executing                      | 0.054088 | -- 执行
| end                            | 0.000017 | -- 结束
| query end                      | 0.000005 | -- 结束查询
| waiting for handler commit     | 0.000012 | -- 
| closing tables                 | 0.000010 |
| freeing items                  | 0.000025 |
| cleaning up                    | 0.000012 |
+--------------------------------+----------+
17 rows in set, 1 warning (0.00 sec)
​
​
注:Sending data 状态表示MySQL线程开始访问数据行并把结果返回给客户端,而不仅仅是返回给客户端。由于在Sending data状态下,MySQL线程往往需要做大量的磁盘读取操作,所以经常是整各查询中耗时最长的状态。
 在获取到最消耗时间的线程状态后,MySQL支持进一步选择all、cpu、block io 、context switch、page faults等明细类型类查看MySQL在使用什么资源上耗费了过高的时间。例如,选择查看CPU的耗费时间  
​
mysql> show profile cpu for query 6;
+--------------------------------+----------+----------+------------+
| Status                         | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting                       | 0.000067 | 0.000035 |   0.000026 |
| Executing hook on transaction  | 0.000003 | 0.000002 |   0.000001 |
| starting                       | 0.000007 | 0.000003 |   0.000003 |
| checking permissions           | 0.000005 | 0.000003 |   0.000002 |
| Opening tables                 | 0.000024 | 0.000014 |   0.000010 |
| init                           | 0.000005 | 0.000003 |   0.000002 |
| System lock                    | 0.000007 | 0.000004 |   0.000003 |
| optimizing                     | 0.000005 | 0.000003 |   0.000002 |
| statistics                     | 0.000014 | 0.000008 |   0.000006 |
| preparing                      | 0.000010 | 0.000006 |   0.000004 |
| executing                      | 0.054088 | 0.053940 |   0.000000 |
| end                            | 0.000017 | 0.000009 |   0.000000 |
| query end                      | 0.000005 | 0.000005 |   0.000000 |
| waiting for handler commit     | 0.000012 | 0.000012 |   0.000000 |
| closing tables                 | 0.000010 | 0.000010 |   0.000000 |
| freeing items                  | 0.000025 | 0.000025 |   0.000000 |
| cleaning up                    | 0.000012 | 0.000012 |   0.000000 |
+--------------------------------+----------+----------+------------+
17 rows in set, 1 warning (0.00 sec)
字段含义
Statussql 语句执行的状态
Durationsql 执行过程中每一个步骤的耗时
CPU_user当前用户占有的cpu
CPU_system系统占有的cpu

2.4.5 trace分析优化器执行计划

MySQL5.6提供了对SQL的跟踪trace, 通过trace文件能够进一步了解为什么优化器选择A计划, 而不是选择B计划。

1、打开trace , 设置格式为 JSON,并设置trace最大能够使用的内存大小,避免解析过程中因为默认内存过小而不能够完整展示。
SET optimizer_trace="enabled=on",end_markers_in_json=on;
set optimizer_trace_max_mem_size=1000000;
​
2、执行sql语句:
select * from t_user;
​
3、检查information_schema.optimizer_trace就可以知道MySQL是如何执行SQL的 :
select * from information_schema.optimizer_trace\G;
​
​
mysql> select * from information_schema.optimizer_trace\G;
*************************** 1. row ***************************QUERY: select * from t_userTRACE: {"steps": [{"join_preparation": {"select#": 1,"steps": [{"expanded_query": "/* select#1 */ select `t_user`.`id` AS `id`,`t_user`.`username` AS `username`,`t_user`.`password` AS `password`,`t_user`.`birthday` AS `birthday`,`t_user`.`sex` AS `sex`,`t_user`.`rid` AS `rid`,`t_user`.`status` AS `status` from `t_user`"}] /* steps */} /* join_preparation */},{"join_optimization": {"select#": 1,"steps": [{"table_dependencies": [{"table": "`t_user`","row_may_be_null": false,"map_bit": 0,"depends_on_map_bits": [] /* depends_on_map_bits */}] /* table_dependencies */},{"rows_estimation": [{"table": "`t_user`","table_scan": {"rows": 10,"cost": 0.25} /* table_scan */}] /* rows_estimation */},{"considered_execution_plans": [{"plan_prefix": [] /* plan_prefix */,"table": "`t_user`","best_access_path": {"considered_access_paths": [{"rows_to_scan": 10,"access_type": "scan","resulting_rows": 10,"cost": 1.25,"chosen": true}] /* considered_access_paths */} /* best_access_path */,"condition_filtering_pct": 100,"rows_for_plan": 10,"cost_for_plan": 1.25,"chosen": true}] /* considered_execution_plans */},{"attaching_conditions_to_tables": {"original_condition": null,"attached_conditions_computation": [] /* attached_conditions_computation */,"attached_conditions_summary": [{"table": "`t_user`","attached": null}] /* attached_conditions_summary */} /* attaching_conditions_to_tables */},{"finalizing_table_conditions": [] /* finalizing_table_conditions */},{"refine_plan": [{"table": "`t_user`"}] /* refine_plan */}] /* steps */} /* join_optimization */},{"join_execution": {"select#": 1,"steps": [] /* steps */} /* join_execution */}] /* steps */
}
MISSING_BYTES_BEYOND_MAX_MEM_SIZE: 0INSUFFICIENT_PRIVILEGES: 0
1 row in set (0.01 sec)

2.5 索引的使用

索引是数据库优化最常用也是最重要的手段之一, 通过索引通常可以帮助用户解决大多数的MySQL的性能优化问题。

2.5.1 验证索引提升查询效率

在我们准备的表结构t_user中, 一共存储了 300 万记录;

对应的存储的sql语句
​
-- 建立存储过程
DROP PROCEDURE IF EXISTS proc1;
CREATE PROCEDURE proc1(cnt INT)
BEGINDECLARE i INT DEFAULT 1;START TRANSACTION;REPEATINSERT INTO t_user(id,username,`password`,sex)VALUES(null,CONCAT('lwl',i),CONCAT('123',i),'男');SET i = i + 1;UNTIL i > cnt       -- 结束条件END REPEAT;COMMIT;
END;
-- 调用存储过程
CALL proc1(3000000);

1、根据id进行查询

select * from t_user where id=1000000 \G;
explain select * from t_user where id=1000000 \G;
​
mysql> select * from t_user where id=1000000\G;
*************************** 1. row ***************************id: 1000000
username: lwl969979
password: 123969979
birthday: NULLsex: 男rid: NULLstatus: NULL
1 row in set (0.00 sec)
​
-- 查看执行计划
mysql> explain select * from t_user where id=1000000 \G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t_userpartitions: NULLtype: const
possible_keys: PRIMARYkey: PRIMARYkey_len: 4ref: constrows: 1filtered: 100.00Extra: NULL
1 row in set, 1 warning (0.00 sec)

2、根据名字进行精确查询

mysql> select * from t_user where username='lwl1000000'\G;
*************************** 1. row ***************************id: 1030021
username: lwl1000000
password: 1231000000
birthday: NULLsex: 男rid: NULLstatus: NULL
1 row in set (1.30 sec)
​
-- 查看sql执行计划
mysql> explain select * from t_user where username='lwl1000000'\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t_userpartitions: NULLtype: ALL
possible_keys: NULL    -- 什么也没有使用key: NULL    -- 什么也没有使用key_len: NULLref: NULLrows: 3025062filtered: 10.00Extra: Using where
1 row in set, 1 warning (0.00 sec)

如果想提高sql的效率,需要给username添加索引

create index idx_item_name on t_user(username);
​
再对名字进行精确查询
​
mysql> explain select * from t_user where username='lwl1000000'\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t_userpartitions: NULLtype: ref           -- 使用了引用
possible_keys: idx_item_name   -- 可能使用了索引key: idx_item_name   -- 使用了索引key_len: 768ref: const           rows: 1filtered: 100.00Extra: NULL
1 row in set, 1 warning (0.01 sec)

2.5.2 避免索引失效

-- 创建表
create table `tb_seller` (`sellerid` varchar (100),`name` varchar (100),`nickname` varchar (50),`password` varchar (60),`status` varchar (1),`address` varchar (100),`createtime` datetime,primary key(`sellerid`)
)engine=innodb default charset=utf8mb4; 
​
-- 添加数据
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('alibaba','阿里巴巴','阿里小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('baidu','百度科技有限公司','百度小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('huawei','华为科技有限公司','华为小店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itcast','传智播客教育科技有限公司','传智播客','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itheima','AAA软件教育','AAA软件教育','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('luoji','罗技科技有限公司','罗技小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('oppo','OPPO科技有限公司','OPPO官方旗舰店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('ourpalm','掌趣科技股份有限公司','掌趣小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('qiandu','千度科技','千度小店','e10adc3949ba59abbe56e057f20f883e','2','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('sina','新浪科技有限公司','新浪官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('xiaomi','小米科技','小米官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','西安市','2088-01-01 12:00:00');
​
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('yijia','宜家家居','宜家家居旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
​
-- 创建复合索引
create index idx_seller_name_sta_addr on tb_seller(name,status,address);
​
就相当于创建了三个索引 :   
name        
name + status   
name + status + address 

1、 全值匹配 ,对索引中所有列都指定具体值。

该情况下,索引生效,执行效率高。
explain select * from tb_seller where name='小米科技' and status='1' and address='北京市';

 

2、最左前缀法则

最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列。 
也就是如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列。①system > const > eq_ref > ref > range > index > All。一般而言,我们要保证查询至少达到ranag级别,最好能到达到ref。②possible_keys和key的值说明语句使用了索引index_nit。③ref值是const,即某个常量被用于查找索引列上的值。
​
匹配最左前缀法则,走索引:
一个索引生效的时候 值是403
两个索引都生效的时候值是 410
三个索引全部生效的时候值是813
​
如果符合最左法则,但是出现跳跃某一列,只有最左列索引生效。
explain select * from tb_seller where name='小米科技' and address='北京市';

 

3、 如果含有范围查询,那么右边的列就不能使用索引 。

explain select * from tb_seller where name='小米科技' and status>'1' and address='北京市';

4、 不要在索引列上进行运算操作, 索引将失效。

explain select * from tb_seller where substring(name,3,2)='科技';

5、 字符串不加单引号,造成索引失效。

explain select * from tb_seller where name='小米科技' and status=1 and address='北京市';
-- 在查询时,没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效。

 

6、尽量使用覆盖索引

覆盖索引:查询的列刚好与创建的索引列的列名及顺序全部匹配或者部分匹配
1、复合索引产生覆盖索引,减少select *
2、如果查询列,超出索引列,也会降低性能。
3、执行情况描述using index :使用覆盖索引的时候就会出现using where:在查找使用索引的情况下,需要回表去查询所需的数据using index condition:查找使用了索引,但是需要回表查询数据using index ; using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

 

7、尽量少使用or

用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
示例:
name字段是索引列 , 而createtime不是索引列,中间是or进行连接是不走索引的
explain select * from tb_seller where name='AAA软件教育' or createtime = '2088-01-01 12:00:00';
​
注:使用and的时候有索引

 

8、 以%开头的Like模糊查询,索引失效。

如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
​
explain select * from tb_seller where name like '教育%';
​
可以使用覆盖索引来解决

 

9、is null 和is not null

is null 和is not null都是走索引的;
is null:不会使索引失效
is not null :在不使用覆盖索引时,会使索引失效在使用覆盖索引时,不会使索引失效
explain select * from tb_seller where name is  null;
explain select name from tb_seller where name is  null;
​
explain select * from tb_seller where name is not null;
explain select name from tb_seller where name is not null;

10、in 走索引, not in 索引失效。

 

11、单列索引和复合索引

尽量使用复合索引,而少使用单列索引 。
1、创建复合索引create index idx_name_sta_address on tb_seller(name, status, address);就相当于创建了三个索引 :   name        name + status   name + status + address
2、创建单列索引create index idx_seller_name on tb_seller(name);create index idx_seller_status on tb_seller(status);create index idx_seller_address on tb_seller(address);数据库会选择一个最优的索引(辨识度最高索引)来使用,并不会使用全部索引 。

2.5.3 查看索引使用情况

-- 查看本次会话的索引使用情况
show status like 'Handler_read%';
-- 查看从上次开启至今的索引使用情况
show global status like 'Handler_read%'; 
​
mysql> show status like 'Handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 3     |
| Handler_read_key      | 319   |
| Handler_read_last     | 0     |
| Handler_read_next     | 236   |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 7     |
+-----------------------+-------+
7 rows in set (0.01 sec)
​
mysql> show global status like 'Handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 52    |
| Handler_read_key      | 3950  |
| Handler_read_last     | 0     |
| Handler_read_next     | 4852  |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 32183 |
+-----------------------+-------+
7 rows in set (0.01 sec)
​
​
Handler_read_first:索引中第一条被读的次数。如果较高,表示服务器正执行大量全索引扫描(这个值越低越好)。
​
Handler_read_key:如果索引正在工作,这个值代表一个行被索引值读的次数,如果值越低,表示索引得到的性能改善不高,因为索引不经常使用(这个值越高越好)。
​
Handler_read_next :按照键顺序读下一行的请求数。如果你用范围约束或如果执行索引扫描来查询索引列,该值增加。
​
Handler_read_prev:按照键顺序读前一行的请求数。该读方法主要用于优化ORDER BY ... DESC。
​
Handler_read_rnd :根据固定位置读一行的请求数。如果你正执行大量查询并需要对结果进行排序该值较高。你可能使用了大量需要MySQL扫描整个表的查询或你的连接没有正确使用键。这个值较高,意味着运行效率低,应该建立索引来补救。
​
Handler_read_rnd_next:在数据文件中读下一行的请求数。如果你正进行大量的表扫描,该值较高。通常说明你的表索引不正确或写入的查询没有利用索引。

2.6 sql优化

2.6.1 优化insert语句

创建一张表

CREATE TABLE `tb_user_2` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(45) NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `unique_user_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

原始添加方案

insert into tb_user_2 values(2,'Cat');
insert into tb_user_2 values(3,'Jerry');
insert into tb_user_2 values(1,'Tom');

1、连续添加

如果需要同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,这种方式将大大的缩减客户端与数据库之间的连接、关闭等消耗。使得效率比分开执行的单个insert语句快。
​
-- mybatis 进行批量添加的时候
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');

2、使用事务进行添加

start transaction; -- 或者使用begin
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
commit;

3、数据有序插入

insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');

2.6.2 优化order by语句

-- 创建一张表
CREATE TABLE `emp` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(100) NOT NULL,`age` int(3) NOT NULL,`salary` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;
​
-- 添加数据
insert into `emp` (`id`, `name`, `age`, `salary`) values('1','Tom','25','2300');
insert into `emp` (`id`, `name`, `age`, `salary`) values('2','Jerry','30','3500');
insert into `emp` (`id`, `name`, `age`, `salary`) values('3','Luci','25','2800');
insert into `emp` (`id`, `name`, `age`, `salary`) values('4','Jay','36','3500');
insert into `emp` (`id`, `name`, `age`, `salary`) values('5','Tom2','21','2200');
insert into `emp` (`id`, `name`, `age`, `salary`) values('6','Jerry2','31','3300');
insert into `emp` (`id`, `name`, `age`, `salary`) values('7','Luci2','26','2700');
insert into `emp` (`id`, `name`, `age`, `salary`) values('8','Jay2','33','3500');
insert into `emp` (`id`, `name`, `age`, `salary`) values('9','Tom3','23','2400');
insert into `emp` (`id`, `name`, `age`, `salary`) values('10','Jerry3','32','3100');
insert into `emp` (`id`, `name`, `age`, `salary`) values('11','Luci3','26','2900');
insert into `emp` (`id`, `name`, `age`, `salary`) values('12','Jay3','37','4500');
​
-- 创建复合索引
create index idx_emp_age_salary on emp(age,salary);

两种排序方式

1). 第一种是通过对返回数据进行排序,也就是通常说的 filesort 排序,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
explain select * from emp order by age desc;
​
​
2). 第二种通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。
explain select id,age from emp order by age desc;
​
​
多字段排序
explain select id,age from emp order by age desc,salary desc;

 

了解了MySQL的排序方式,优化目标就清晰了:尽量减少额外的排序,通过索引直接返回有序数据。where 条件和Order by 使用相同的索引,并且Order By 的顺序和索引顺序相同, 并且Order  by 的字段都是升序,或者都是降序。否则肯定需要额外的操作,这样就会出现FileSort。

2.6.3 优化group by语句

由于GROUP BY 实际上也同样会进行排序操作,而且与ORDER BY 相比,GROUP BY 主要只是多了排序之后的分组操作。
当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在GROUP BY 的实现过程中,与 ORDER BY 一样也可以利用到索引。
如果查询包含 group by 但是用户想要避免排序结果的消耗, 则可以执行order by null 禁止排序。
如下:
drop index idx_emp_age_salary on emp;
​
-- 优化前
explain select age,count(*) from emp group by age;
​
-- 优化后
explain select age,count(*) from emp group by age order by null;
​
上面的例子可以看出,第一个SQL语句需要进行"filesort",而第二个SQL由于order  by  null 不需要进行 "filesort", 而上文提过Filesort往往非常耗费时间。
create index idx_emp_age_salary on emp(age,salary);
​
如果创建了索引,那么就会使用覆盖索引进行查询。

2.6.4 优化嵌套查询

Mysql4.1版本之后,开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询是可以被更高效的连接(JOIN)替代。

示例 ,查找有角色的所有的用户信息
​
优化前:
explain select * from t_user where id in (select uid from t_user_role);
​
优化后:
explain select * from t_user u,t_user_role ru where u.id=ru.uid;

 

2.7 练习面试题

MySQL数据库中有1000万条数据,现在需要删除500万条。DROP PROCEDURE IF EXISTS DeleteOverDueData;
CREATE PROCEDURE `DeleteOverDueData`()BEGINdeclare i INT DEFAULT 1;while i<=50 doDELETE FROM t_user  LIMIT 50000;SET i=i+1;END WHILE;
END;
CALL DeleteOverDueData();
​
​
-- 效率较高
DROP PROCEDURE IF EXISTS DeleteOverDueData2;
CREATE PROCEDURE `DeleteOverDueData2`(IN min INT,IN max INT)BEGINSELECT min from t_user;select max from t_user;WHILE min<max DODELETE FROM t_user WHERE id>min AND id<max LIMIT 50000; ##一次性删除五万条CALL DeleteOverDueData2();END WHILE;
END;
CALL DeleteOverDueData2(50,3030021);

相关文章:

MySQL高级第二讲

目录 二、MySQL高级02 2.1 触发器 2.1.1 触发器介绍 2.1.2 创建触发器 2.2 MySQL的体系结构 2.3 存储引擎 2.3.1 存储引擎概述 2.3.2 各种存储引擎特性 2.3.3 InnoDB 2.3.4 MyISAM 2.3.5 MEMORY 2.3.6 MERGE 2.3.7 存储引擎的选择 2.4 优化sql 2.4.1 查看sql执行…...

凸优化专题1

多变量函数的求导与求梯度/矩阵求导 1. 导数 定义: 设f:Rn→Rm,且x∈intdomf,则f在点x的导数(或称Jacobian)记为矩阵Df(x)∈Rmnf:\R^n \rightarrow \R^m, 且x\in \mathbf{int}\ \mathbf{dom} f, 则f 在点x的导\\数(或称Jacobian)记为矩阵 Df(x) \in \R^{m\times n}f:Rn→Rm,且…...

【蓝桥杯每日一题】递推算法

&#x1f34e; 博客主页&#xff1a;&#x1f319;披星戴月的贾维斯 &#x1f34e; 欢迎关注&#xff1a;&#x1f44d;点赞&#x1f343;收藏&#x1f525;留言 &#x1f347;系列专栏&#xff1a;&#x1f319; 蓝桥杯 &#x1f319;我与杀戮之中绽放&#xff0c;亦如黎明的花…...

Unity性能优化: 性能优化之内存篇

前言 本文和传统的内存优化不一样&#xff0c;不是讲如何降低内存占用&#xff0c;而是讲编程开发中要注意的内存问题以及一些内存技术的演变与原理。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点击进来一起交流一下开发经验呀 1: Application进程…...

华为OD机试题,用 Java 解【内存资源分配】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…...

微服务之Nacos注册与配置

&#x1f3e0;个人主页&#xff1a;阿杰的博客 &#x1f4aa;个人简介&#xff1a;大家好&#xff0c;我是阿杰&#xff0c;一个正在努力让自己变得更好的男人&#x1f468; 目前状况&#x1f389;&#xff1a;24届毕业生&#xff0c;奋斗在找实习的路上&#x1f31f; &#x1…...

Android 动画详解

Android动画的分类与使用学习Android必不可少的就是动画的使用了&#xff0c;在Android版本迭代的过程中&#xff0c;出现了很多动画框架&#xff0c;这里做一个总结。Android动画类型分类逐帧动画【Frame Animation】&#xff0c;即顺序播放事先准备的图片。补间动画【Tween A…...

Linux -- 程序 进程 线程 概念引入

程序与进程 &#xff1a;程序 &#xff1a;什么是程序 &#xff1f;&#xff1f;&#xff1f;伪官方 &#xff1a; 二进制文件&#xff0c;文件存储在磁盘中&#xff0c;例如 /usr/bin 目录下 。 是静态。 简单讲 &#xff1a;# 我们都学习了语言&#xff0c;比如下面这串代…...

Android ART dex2oat

一、什么是dex2oat Dex2oat (dalvik excutable file to optimized art file) &#xff0c;是一个对 dex 文件进行编译优化的程序&#xff0c;在我们的 Android 手机中的位置是 /system/bin/dex2oat&#xff0c;对应的源码路径为 android/art/dex2oat/dex2oat.cc&#xff0c;通…...

「RISC-V Arch」RISC-V 规范结构

日期&#xff1a;20230228 规范分类 根据 RISC-V 设计哲学&#xff0c;其规范文档也是高度模块化的&#xff1a; ISA 规范&#xff08;2 篇&#xff09; 非特权规范特权规范 非 ISA 规范&#xff08;6篇&#xff09; Trace规范ABI 规范外部调试规范PLIC 规范SBI 规范UEFI 协…...

【C】线程控制

创建线程 #include <pthread.h>int pthread_create(pthread_t * thread,const pthread_attr_t * attr,void *(*start_routine)(void*), void * arg);返回值&#xff1a;成功返回0&#xff0c;失败返回错误号。 thread&#xff1a;成功返回后&#xff0c;新创建的线程的…...

Maven工程打jar包的N种方式

Maven工程打jar包 一、IDEA自带打包插件二、maven插件打包2.1 制作瘦包&#xff08;直接打包&#xff0c;不打包依赖包&#xff09;2.2 制作瘦包和依赖包&#xff08;相互分离&#xff09;2.3 制作胖包&#xff08;项目依赖包和项目打为一个包&#xff09;2.4 制作胖包&#xf…...

一文了解GPU并行计算CUDA

了解GPU并行计算CUDA一、CUDA和GPU简介二、GPU工作原理与结构2.1、基础GPU架构2.2、GPU编程模型2.3、软件和硬件的对应关系三、GPU应用领域四、GPUCPU异构计算五、MPI与CUDA的区别一、CUDA和GPU简介 CUDA&#xff08;Compute Unified Device Architecture&#xff09;&#xf…...

全网资料最全Java数据结构与算法(1)

一、数据结构和算法概述 1.1什么是数据结构&#xff1f; 官方解释&#xff1a; 数据结构是一门研究非数值计算的程序设计问题中的操作对象&#xff0c;以及他们之间的关系和操作等相关问题的学科。 大白话&#xff1a; 数据结构就是把数据元素按照一定的关系组织起来的集合&a…...

【项目实战】SpringMVC拦截器HandlerInterceptor入门介绍

一、拦截器介绍 拦截器是应用程序级框架中常用的拦截用户请求、实施业务流程控制的模式,它可以将一些公共的、重复发生的业务逻辑从业务处理代码中独立出来,使系统的结构更加清晰,程序的复杂度也减小了。 拦截器是一个常见的特性,它可以实现任何自定义功能,而无需调整业…...

阿里淘宝新势力造型合伙人P8、年薪百万的欧阳娜娜也躲不过的魔鬼面试,看的我心服口服

阿里淘宝新势力造型合伙人P8、年薪百万的欧阳娜娜跳槽了&#xff0c;这不是关键。 她参加了网易有道明星语音录音员/代言人的面试&#xff0c;这也不是关键。 关键是她教科书式的面试过程&#xff0c;狠狠地给我们上了一课。 我是无意间刷到的这个视频的时候&#xff0c;就一…...

深度学习笔记:不同的反向传播迭代方法

1 随机梯度下降法SGD 随机梯度下降法每次迭代取梯度下降最大的方向更新。这一方法实现简单&#xff0c;但是在很多函数中&#xff0c;梯度下降的方向不一定指向函数最低点&#xff0c;这使得梯度下降呈现“之”字形&#xff0c;其效率较低 class SGD:"""随机…...

ElasticSearch 学习笔记总结(三)

文章目录一、ES 相关名词 专业介绍二、ES 系统架构三、ES 创建分片副本 和 elasticsearch-head插件四、ES 故障转移五、ES 应对故障六、ES 路由计算 和 分片控制七、ES集群 数据写流程八、ES集群 数据读流程九、ES集群 更新流程 和 批量操作十、ES 相关重要 概念 和 名词十一、…...

深入理解border以及应用

深入border属性以及应用&#x1f44f;&#x1f44f; border这个属性在开发过程中很常用&#xff0c;常常用它来作为边界的。但是大家真的了解border吗&#xff1f;以及它的形状是什么样子的。 我们先来看这样一段代码&#xff1a;&#x1f44f; <!--* Author: syk 185901…...

如何复现论文?什么是论文复现?

参考资料&#xff1a; 学习篇—顶会Paper复现方法 - 知乎 如何读论文&#xff1f;复现代码&#xff1f;_复现代码是什么意思 - CSDN 我是如何复现我人生的第一篇论文的 - 知乎 在我看来&#xff0c;论文复现应该有一个大前提和分为两个层次。 大前提是你要清楚地懂得自己要…...

22.2.28打卡 Codeforces Round #851 (Div. 2) A~C

A题 One and Two 题面翻译 题目描述 给你一个数列 a1,a2,…,ana_1, a_2, \ldots, a_na1​,a2​,…,an​ . 数列中的每一个数的值要么是 111 要么是 222 . 找到一个最小的正整数 kkk&#xff0c;使之满足&#xff1a; 1≤k≤n−11 \leq k \leq n-11≤k≤n−1 , anda1⋅a2⋅……...

Learining C++ No.12【vector】

引言&#xff1a; 北京时间&#xff1a;2023/2/27/11:42&#xff0c;高数考试还在进行中&#xff0c;我充分意识到了学校的不高级&#xff0c;因为题目真的没什么意思&#xff0c;虽然挺平易近人&#xff0c;但是……&#xff0c;考试期间时间比较放松&#xff0c;所以不能耽误…...

【数电基础】——逻辑代数运算

目录 1.概念 1.基本逻辑概念 2.基本逻辑电路&#xff08;与或非&#xff09; 逻辑与运算 与门电路&#xff1a; 逻辑或运算 或门电路&#xff1a; ​逻辑非运算&#xff08;逻辑反&#xff09; 非门电路​编辑 3.复合逻辑电路&#xff08;运算&#xff09; 与非逻辑…...

【Redis】什么是缓存与数据库双写不一致?怎么解决?

1. 热点缓存重建 我们以热点缓存 key 重建来一步步引出什么是缓存与数据库双写不一致&#xff0c;及其解决办法。 1.1 什么是热点缓存重建 在实际开发中&#xff0c;开发人员使用 “缓存 过期时间” 的策略来实现加速数据读写和内存使用率&#xff0c;这种策略能满足大多数…...

互联网衰退期,测试工程师35岁之路怎么走...

国内的互联网行业发展较快&#xff0c;所以造成了技术研发类员工工作强度比较大&#xff0c;同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高&#xff0c;超过35岁的基层研发类员工&#xff0c;往往因为家庭原因、身体原因&#xff0c;比较难以跟得上工作…...

动态规划(以背包问题为例)

1) 要求达到的目标为装入的背包的总价值最大&#xff0c;并且重量不超出2) 要求装入的物品不能重复动态规划(Dynamic Programming)算法的核心思想是&#xff1a;将大问题划分为小问题进行解决&#xff0c;从而一步步获取最优解的处理算法。动态规划算法与分治算法类似&#xff…...

Java异常

异常的体系结构 在java的Throwable下有Error和Exception两个子类 Error(错误):程序运行中出现了严重的问题,非代码性错误,无法处理,常见的有虚拟机运行错误和内存溢出等Exception(异常):是由于代码本身造成的问题,可以进行处理,异常一个可以分为运行时异常和编译时异常 运行…...

别克GL8改装完工,一起来看看效果

①豪华商务头等舱 别克GL8作为商务车&#xff0c;不管是家用还是商务接待&#xff0c;原车内饰都太掉档次了&#xff0c;所以车主要求全部换掉。>>织布座椅换成航空座椅 主副驾&#xff1a;改装纳帕皮 中排&#xff1a;改装水晶宝座豪华版航空座椅&#xff0c;带通风、加…...

mac 中 shell 一些知识

mac 设置环境变量首先得看你所使用的 shell shell 是一个命令行解释器&#xff0c;顾名思义就是机器外面的一层壳&#xff0c;用于人机交互&#xff0c;只要是人与电脑之间交互的接口&#xff0c;就可以称为 shell。表现为其作用是用户输入一条命令&#xff0c;shell 就立即解…...

CentOS 配置FTP(开启VSFTPD服务)

网上已经有很多关于VSFTPD的配置&#xff0c;但有两个通病&#xff0c;要么就是原理介绍太多&#xff0c;要么就是不完整&#xff0c;操作下来又要查询多篇文章才能用。 我这里不讲原理&#xff0c;只记录操作&#xff0c;尽可能通过复制下面的操作可以实现FTP读写功能。方便自…...

Http的请求方法

Http的请求方法对应的数据传输能力把Http请求分为Url类请求和Body类请求 1.Url类请求包括但不限于GET、HEAD、OPTIONS、TRACE 等请求方法 2.Body类请求包括但不限于POST、PUSH、PATCH、DELETE 等请求方法。 3.原因&#xff1a;get请求没有请求体&#xff08;好像也可以…...

Python字典-- 内附蓝桥题:统计数字

字典 ~~不定时更新&#x1f383;&#xff0c;上次更新&#xff1a;2023/02/28 &#x1f5e1;常用函数&#xff08;方法&#xff09; 1. dic.get(key) --> 判断字典 dic 是否有 key&#xff0c;有返回其对应的值&#xff0c;没有返回 None 举个栗子&#x1f330; dic …...

文本处理工具

Grep工具的基本使用grep作用&#xff1a;grep是行过滤工具&#xff1b;用于根据关键字进行行过滤提示&#xff1a;通过alias命令设置grep别名&#xff0c;搜索参数时带颜色显示alias grepgrep colorauto 命令语法格式&#xff1a;grep [选项] 参数 文件名grep命令选项&#xff…...

C++STL详解(三)——vector的介绍和使用

文章目录vector的介绍vector的使用vector的定义方式vector的空间增长问题reserve和resizevector的迭代器使用begin 和endrbegin和rendinsert 和erasefind函数元素访问vector迭代器失效问题1&#xff1a;inserse插入扩容时空间销毁造成野指针问题2&#xff1a;erase删除或者inse…...

GEBCO海洋数据下载

一、数据集简介 GEBCO&#xff08;General Bathymetric chart of the Oceans&#xff09;旨在为世界海洋提供最权威的、可公开获取的测深数据集。 目前的网格化测深数据集&#xff0c;即GEBCO_2022网格&#xff0c;是一个全球海洋和陆地的地形模型&#xff0c;在15角秒间隔的…...

【C++容器】vector、map、hash_map、unordered_map四大容器的性能分析【2023.02.28】

摘要 vector是标准容器对数组的封装&#xff0c;是一段连续的线性的内存。map底层是二叉排序树。hash_map是C11之前的无序map&#xff0c;unordered_map底层是hash表&#xff0c;涉及桶算法。现对各个容器的查询与”插入“性能做对比分析&#xff0c;方便后期选择。 测试方案…...

ACM-蓝桥杯训练第一周

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石.CSDN &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​ &#x1f4e3;系列专栏&#xff1a;ACM周训练题目合集.CSDN &#x1f4ac;总结&#xff1a…...

python基础—字符串操作

&#xff08;1&#xff09;字符串&#xff1a; Python内置了一系列的数据类型&#xff0c;其中最主要的内置类型是数值类型、文本序列&#xff08;字符串&#xff09;类型、序列&#xff08;列表、元组和range&#xff09;类型、集合类型、映射&#xff08;字典&#xff09;类型…...

【Spring】通过JdbcTemplate实现CRUD操作

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 通过JdbcTemplate实现 增删查改一、添加相关依…...

实战|掌握Linux内存监视:free命令详解与使用技巧

文章目录前言一. free命令介绍二. 语法格式及常用选项三. 参考案例3.1 查看free相关的信息3.2 以MB的形式显示内存的使用情况3.3 以总和的形式显示内存的使用情况3.4 周期性的查询内存的使用情况3.5 以更人性化的形式来查看内存的结果输出四. free在脚本中的应用总结前言 大家…...

嵌入式入门必看!调试工具安装——基于 AM64x核心板

本章节内容是为评估板串口安装USB转串口驱动程序。驱动适用于CH340、CH341等USB转串口芯片。 USB转串口驱动安装 适用安装环境:Windows 7 64bit、Windows 10 64bit。 本文测试板卡为创龙科技SOM-TL64x核心板,它是一款基于TI Sitara系列AM64x双核ARM Cortex-A53 + 单/四核Cort…...

JAVA开发(java类加载过程)

1、java语言的平台无关性。 因为java语言可以跑在java虚拟机上&#xff0c;所以只要能装java虚拟机的地方就能跑java程序。java语言以后缀名 .java为文件扩展名。通过java编译器javac编译成字节码文件.class 。java字节码文件通过java虚拟机解析运行。所以java语言可以说是编译…...

【vulhub漏洞复现】Thinkphp 2.x 任意代码执行

一、漏洞详情影响版本 thinkphp 2.x但是由于thinkphp 3.0版本在Lite模式下没有修复该漏洞&#xff0c;所以也存在该漏洞漏洞原因&#xff1a;e 和 /e模式匹配路由&#xff1a;e 配合函数preg_replace()使用, 可以把匹配来的字符串当作正则表达式执行; /e 可执行模式&#xff0c…...

LeetCode 1145. 二叉树着色游戏 -- 简单搜索

二叉树着色游戏 提示 中等 199 相关企业 有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中&#xff0c;给出二叉树的根节点 root&#xff0c;树上总共有 n 个节点&#xff0c;且 n 为奇数&#xff0c;其中每个节点上的值从 1 到 n 各不相同。 最开始时&#xff1a; 「一…...

HyperGBM的三种Early Stopping方式

本文作者&#xff1a;杨健&#xff0c;九章云极 DataCanvas 主任架构师 很多机器学习框架如都提供了Early Stopping策略&#xff0c;主要用来防止模型过拟合。和模型训练提前停止的目标不同&#xff0c;AutoML的Early Stopping策略更多考虑的是算力消耗和模型质量的平衡。 通…...

心系区域发展,高德用一体化出行服务平台“聚”力区域未来

交通&#xff0c;是城市的血脉。通过对人、资源、产业的连接&#xff0c;交通建设往往是城市和区域经济发展的前提。不过&#xff0c;在度过了“要想富&#xff0c;先修路”的初级建设阶段后&#xff0c;交通产业内部也出现了挑战&#xff0c;诸如城市秩序、发展成本、用户使用…...

AI画图_stable-diffusion-webui安装使用指南(1)

本文章适用于: 有一定学习能力和钻研能力&#xff0c;遇到问题能合理使用搜索引擎尝试解决问题的人想在windows系统中尝试使用AI作画工具stable-diffusion-webui进行绘画的人有一定的计算机基础&#xff08;会魔法上网、知道 python和Git&#xff09;和英文阅读能力的人显卡为…...

浅谈MySQL主从复制

目录 1.MySQL主从复制是什么 2.MySQL主从复制的意义 3.MySQL主从复制原理 4.数据同步一致性问题 5.实现方式 1.MySQL主从复制是什么 MySQL主从复制就是指数据可以从一台MySQL的主节点复制到一个或多个从节点。 MySQL默认采用异步复制方式&#xff0c;这样从节点不用一直访…...

docker-compose安装kafka和php简单测试

docker-compose.yml内容&#xff1a; version: 3.1 services: zookeeper: container_name: zookeeper image: zookeeper:3.6 ports: - 2181:2181 kafka: image: wurstmeister/kafka container_name: kafka depends_on: - zookeeper …...

【蓝桥云课】快速幂

问题描述&#xff1a;快速求aba^bab 方法一&#xff1a;常规方法相乘a∗a∗a∗a∗...∗aa*a*a*a*...*aa∗a∗a∗a∗...∗a 方法二&#xff1a;分治方法求aba^bab ab{1,b0a,b1ab2⋅ab2,b为偶数ab−12⋅ab12,b为奇数a^b\begin{cases} 1& \text{,b0}\\ a& \text{,b1}\\ a…...