一文解决MySQL突击面试,关键知识点总结

2023-05-16

文章目录

  • MySQL重要知识点回顾
    • 一、索引
      • 1. 为什么需要索引
      • 2. 索引的结构
      • 3. 避免索引失效
        • 3.1 联合索引不满足最左匹配原则
        • 3.2 隐式转换
        • 3.3 like查询
        • 3.4 索引列存在运算或者使用函数
        • 3.5 优化器
      • 4. 执行计划
        • 4.1 type
        • 4.2 key
        • 4.3 rows
        • 4.4 extra
      • 5. 建立索引
        • 5.1 什么情况下应该建索引?
        • 5.2 在哪些列上建索引?
        • 5.3 建什么样的索引?
          • 5.3.1 唯一索引、普通索引
          • 5.3.2 联合索引
          • 5.3.3 联合索引要注意什么?
          • 5.3.4 全文索引
        • 5.4 建多少索引?
        • 5.5 索引的长度
        • 5.6 关于区分度的问题
    • 二、SQL语句的优化
      • 1. 执行计划分析
      • 2. join大表驱动还是小表驱动?
      • 3. limit语句优化
        • 分页
        • 限制查询条数
      • 4. 索引合并
      • 5. select * from table 不建议使用
      • 6. distinct 和 group by
    • 三、大表问题
      • 1. 大表会带来哪些问题
        • 1.1 增删改查
        • 1.2 添加索引
        • 1.3 添加字段
        • 1.4 数据同步
      • 2. 怎么解决大表问题?
        • 2.1 垂直拆分
        • 2.2 水平拆分
          • 2.2.1 按范围
          • 2.2.2 按区域
          • 2.2.3 按时间
          • 2.2.4 hash取模
        • 2.3 分表后带来的问题
          • 2.3.1 事务
          • 2.3.2 排序、分组、分页
      • 3. 数据归档
      • 4. 冷热分离
    • 四、内存缓冲区
      • 1. buffer pool
        • 1.1 buffer pool的空间管理方式
        • 1.2 buffer bool使用情况
      • 2. Change Buffer
    • 五、ACID特性
      • 1. 什么是ACID
      • 2. 如何实现ACID
        • A (Atomicity, 原子性)
        • C(Consistency,一致性)
        • I(Isolation,隔离性)
        • D(Durability,持久性)
    • 六、脏读、幻读,不可重复读
      • 1. 概念解释
      • 2. 案例演示
        • 2.1 脏读问题演示
        • 2.2 不可重复读问题演示
        • 2.3 幻读问题演示
    • 七、日志
      • 1. binlog
        • 1.1 binlog的写入频率
        • 1.2 binlog日志记录格式
      • 2. Redo Log
      • 3. Undo Log
    • 八、主从复制
      • 主从复制的方式
      • 主从复制大致流程
    • 九、常见笔试题
      • 1. 部门工资最高的员工
      • 2. 部门工资第N高的员工
      • 3. 行转列
      • 4. 列转行
      • 5.树结构查询
        • 5.1 查询树的根节点
        • 5.2 查询树的叶子节点
        • 5.3 查询既不是叶子也不是根的节点
        • 5.4 输出完整的树
      • 6. 排名问题

MySQL重要知识点回顾

一、索引

1. 为什么需要索引

在我们日常生活中,常见的索引应用场景有:字典、货柜、楼层索引等等,这些都是按序归类,比如字典中:0-500页都是以“a”开头的字母,501-800页都是以“b”开头的字母,这样如果我们要查询的字母是“b”开头的,我们就可以直接从第501页开始查找,如果没有这种按序归类,那么你就必须找完800页。同样的,货柜上的物品按照某些分类规则来摆放也是一样的道理,都是为了能够快速定位到目标数据。

所以,使用索引的目的就是为了能够快速查找具有某个特定值的数据,对于MySQL来说,没有索引通常就需要遍历全表数据才能确定相关的数据行,而有了索引,又因为索引是有序的,所以就能快速定位到目标数据,就像查字典一样,从而避免全表扫描。

2. 索引的结构

从索引的使用场景可以理解,索引本身必须是要有序的,因此理论上只要是有序的数据结构,都可以用来建立索引,那MySQL为什么在那么多的数据结构类型中,选择B+Tree呢?

实际上,一个非常重要的原因就是因为MySQL的数据是存储在磁盘的,而我们知道每次与磁盘交互是相对较慢的,所以我们是希望能够尽量少的发生磁盘I/O事件,而B+Tree的每个节点可以分叉出N个分支,因此只要单节点容量够大,就能大大减少与磁盘交互的次数,从而提升整体响应。

举个简单的例子:

如果节点度数为3,也就是3叉树,那么保存:20,4,13,7,1,17,9,2,6,11这组数字的效果就如下:

image.png

如果我们把节点度数增加到5,则树的高度则减少了一层

image.png

假设我们现在要在这20个数中,找到19这个数

image.png

大致查询逻辑是:

  1. 从磁盘中加载第一个节点,数据为7和13,因为19大于13,因此继续找13指向的下一个节点
  2. 从磁盘中加载13指向的节点,数据为15和17,因为19大于17,因此继续找17指向的下一个节点
  3. 从磁盘中加载17指向的节点,数据为17、18、19、20,依次遍历(实际上可以采用更高效的查找法,比如二分)找到19

可以肯定,节点中存放的数据量多少,直接影响了产生磁盘I/O的次数,而在B+Tree中,每个节点(实际上对应的参数为:innodb_page_size)默认的大小是16KB,所以了为了能够存放更多的数据,才会有索引字段尽量不要太长的建议。

既然这样,是不是可以直接调整innodb_page_size的大小,比如innodb_page_size支持你调整到32KB或者64KB,其实之所以默认是16KB,主要是因为当前主流的磁盘设置,扇区大小就是16KB,保持页的大小与扇区大小一致,可以最大限度的减少将未更改的数据重写到磁盘,此外如果是经常用于频繁写入和读取的场景,较小的page_size也是可以减少单个节点被频繁重写到磁盘的情况。

3. 避免索引失效

索引失效指的是,虽然建立了索引,但无法使用的情况,我们可以准备一张表,插入一些数据,来演示几种索引失效的场景

准备一张表

CREATE TABLE `t_user_demo` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
`name` VARCHAR(20) DEFAULT NULL COMMENT '姓名',
`phone` char(11) DEFAULT NULL COMMENT '手机号',
`age` TINYINT DEFAULT NULL COMMENT '年龄',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name_phone` (`name`, `phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入一些数据

image.png

3.1 联合索引不满足最左匹配原则

严格按照索引顺序执行,肯定走索引

select * from t_user_demo where name = 'zz' and phone = '13933333333';

这个语句虽然没有按照索引的建立顺序,但是依旧可以走索引,因为mysql会对这样的语句进行排序优化,或者交换两个条件的顺序并不影响结果。

select * from t_user_demo where phone = '13933333333' and name = 'zz';

这个语句就不会走索引了,因为不满足最左匹配原则。

select * from t_user_demo where phone = '13911111111'

交换一下顺序,只要符合最左匹配原则,单查联合索引中的列也是可以的。

select * from t_user_demo where name = 'zz'

同样最左查询条件索引中断了,也不能走索引了。

select * from t_user_demo where name like '%zz%' and phone = '13933333333';

建立一个phone,age,name的联合索引。

CREATE INDEX idx_phone_age_name ON t_user_demo (phone,age,name);

走索引没问题。

select * from t_user_demo where phone = '13944444444' and age = 15 and name = 'qq'

范围条件之后的列不走索引了。

select * from t_user_demo where phone = '13944444444' and age > 15 and name = 'qq';

3.2 隐式转换

当把phone的引号去掉,则只走一个name列的索引,这是因为发生了隐式转换,phone列是char类型需要转换成相应类型进行比较。(隐式转换的根本原因是因为索引列上发生了函数计算)

select * from t_user_demo where name = 'zz' and phone = 13933333333;

3.3 like查询

左右都有%的查询,是不会走索引的。

select * from t_user_demo where name like '%zz%'

左边没有%的情况下,会走范围类型的索引。

select * from t_user_demo where name like 'zz%'

3.4 索引列存在运算或者使用函数

在age列上建立索引

CREATE INDEX idx_age ON t_user_demo (age);

索引列存在计算,不走索引

select * from t_user_demo where age - 1 = 15;

一样不会走索引

select * from t_user_demo where ABS(age) = 15;

3.5 优化器

有些情况下,优化器会认为走索引还不如全表扫描来的快,那么就会直接使用全表扫描的方式,比如索引的区分度非常低,即便使用了索引,也几乎要扫描全索引的数据,那还不如直接全表扫描来的快。

当然,有些情况下也会遇到优化器误判定的情况,此时你可以使用force index(idx_name)的方式来强制使用某个索引。

4. 执行计划

执行计划可以帮助你快速分析SQL执行的情况,在你的SQL语句前面直接加上explain关键字即可,如:explain select * from table

执行输出的列如下图:
image.png

这里面比较容易分析出问题的列一般有:type、key、rows、extra

4.1 type

一般按照结果最好到最坏依次是:system > const > eq_ref > ref > range > index > all

  • system:表示改表为系统表,业务上一般不会出现。
  • const:一般在使用主键或者唯一键作为查询条件时出现,它表示最多只会出现一个匹配行。
  • eq_ref:当使用主键或者唯一键作为表关联条件时出现。
  • ref:非主键或者唯一键作为查询条件时,或者表关联条件时出现。
  • range:用作范围查询时,比如:<>,is, null, between, like, in等。
  • index:当利用到覆盖索引时出现,也就是仅通过扫描索引树就能得到结果。
  • all:all就是全表扫描了,是我们要尽量避免出现的类型。

4.2 key

key表示MySQL实际使用到的索引

4.3 rows

rows虽然是一个估算值,但它可以直接体现出查询的效率,rows的数值代表着本次查询要检索的行数,因此rows越大检索的时间就越长。

4.4 extra

虽然是额外信息,但其通常展示的数据都非常有意义,通过它就可以大致了解本次查询的方式。

常见的extra有下面几种:

  • Using filesort:一般情况下是由于查询语句中出现了排序的要求,且排序字段又没有建立索引,必须通过扫描全表来实现。
  • Using index:直接通过检索索引树就能够完成查询的情况。
  • Using where:简单的使用到了where的条件过滤
  • Using temporary:必须通过建立临时表才能满足查询的情况,场景的有,order by 和 group by
  • Using join buffer (Block Nested Loop):在没有使用索引键做表关联查询时,为了减少磁盘I/O的交互,MySQL可以把某张表的数据先读取到内存中,然后在内存中和另外一张表进行结果匹配,从而实现查询的优化。
  • Using MRR:MRR简称Multi-Range Read,主要解决的就是当在非聚集索引中过滤出结果集之后,再回到聚集索引中查询数据行时,由于非聚集索引的排序结果与聚集索引的排序结果存在差异而导致回聚集索引查询时产生较多的磁盘随机I/O的问题。

5. 建立索引

建立索引,主要考虑的是什么情况下应该建索引,在哪些列上建索引,建什么样的索引,建多少索引等方面,下面我们可以简单分析一下。

5.1 什么情况下应该建索引?

实际上在innodb存储引擎下,每张表至少都有一个主键索引(没主键会用rowid代替),对业务上使用来说,建索引就是为了提升查询效率,因此在你可预见表数据规模的前提下,应该提前建立好索引,毕竟,如果你事先没有建索引,等你发现问题再去建索引时,通常都已经对业务造成一定影响了。

5.2 在哪些列上建索引?

首先,索引列最好要有比较高的区分度,因为只有足够高的区分度才能更好的体现出索引的优势。
其次,一般就是参与where、group by、order by、关联查询等情况时需要考虑建立索引。
最后,有一点比较重要,要尽量避免在频繁更新的列上建立索引,因为每一次更新都需要对索引的存储进行维护。

5.3 建什么样的索引?

按索引类型可以分为:主键索引、唯一索引、普通索引、联合索引、全文索引。

每张表只有一个主键索引,而根据主键索引查询是非常快的,因为它是唯一的且无需回表。

5.3.1 唯一索引、普通索引

理论上能建立唯一索引的优先考虑建立唯一索引,唯一索引不但检索效率高于普通索引,且还能在业务上防止重复数据的产生,至于说因为唯一索引在写入因为有唯一性的校验,因此无法利用Change Buffer的问题,对于绝大多数场景差异其实并不是很大。

关于Change Buffer的问题后面会单独提到。

5.3.2 联合索引

正如前面索引合并的问题产生一样,如果明确有多个索引列就是会一块查询时,那肯定是改为联合索引比较高效,就这好比原本你需要到多个索引文件中挨个检索的情况,现在只需要在一个索引文件中就能完成。

5.3.3 联合索引要注意什么?
  1. 遵守最左匹配原则
  2. 区分度高的尽量放在最左边
  3. 联合索引列数不宜过多,很明显,列数过多就像字段长度太长一样,从对磁盘和内存的使用来说,都不是好的选择。
5.3.4 全文索引

全文索引在MySQL中目前只能对文本类型的字段建立,在实际业务场景中几乎用不到,毕竟有像ES这种专门干这事的组件存在,为什么还要用MySQL呢?况且限制还很多,检索也不精确。

5.4 建多少索引?

显然,索引并不是越多越好,否则也不多想,直接给所有列都加上索引就好了,我记得在阿里开发手册中也有提及到,单表建立索引不要超过5个,之所以要限制索引的数量,主要是为了对新增、修改、删除等会对表数据进行修改的操作带来影响,我们知道每一个索引都会有一份独立的存储,索引越多也就意味着每添加一行数据,除了要记录本身的行数据之外,要写的索引文件也就越多,因此一定会对写入的性能造成一定的影响,尤其是那种写多读少的场景。
此外,索引越多也就意味着要存储的内容也就越多,就需要更多的磁盘空间来存储。

所以,我认为建多少索引主要还是需要结合实际的业务场景来定夺,你只要清楚的了解其中的利弊就可以了。

5.5 索引的长度

为什么有时候建立索引的时候还需要考虑索引的长度呢?说白了就是为了减少索引占用的空间,使得一次I/O能够读取更多的索引列到内存中,提高其查询效率。

比如,有些业务场景会要求在数据的前面或者后面拼接上某种固定的值:
1230001
2340001
8210001
8740001
像这样最后拼接上0001的情况,实际上就没有必要把所有数据都存储到索引文件中了,只需要记录前三位就可以了,因为后4位完全没有区分度。

值得一提的是,在阿里开发手册中,还专门提到了,如果是在varchar类型的字段上建立索引时,必须根据文本区分度来指定索引长度。

下面我准备了一些数据来看看文本区分度的效果:

假设order_no字段长度为32位,那么通过如下计算可以得出建立的索引长度为26位是比较合适的

SELECT 
count(DISTINCT LEFT(order_no, 20)) / count(*) AS '20', 
count(DISTINCT LEFT(order_no, 22)) / count(*) AS '22', 
count(DISTINCT LEFT(order_no, 24)) / count(*) AS '24', 
count(DISTINCT LEFT(order_no, 26)) / count(*) AS '26', 
count(DISTINCT LEFT(order_no, 28)) / count(*) AS '28', 
count(DISTINCT LEFT(order_no, 30)) / count(*) AS '30', 
count(DISTINCT LEFT(order_no, 32)) / count(*) AS '32' 
FROM test;

结果如下:
image.png

5.6 关于区分度的问题

首先区分度肯定是越高越好,当然越接近100%,也就意味着索引长度越接近于字段长度,这需要你来平衡。

那么分区度过低,到底有什么不好的呢?

很明显,我们是希望能够通过尽可能少的索引空间就能获得足够精确的查询结果,举个例子:

假设有这么两条数据,一个字段值为:10001,一个字段值为:10002,他们的前4位都是1000,所以如果我们索引只保留了前4位,那无论你是根据索引查10001还是查10002,都会查得两条满足1000的数据,此时只能拿着主键ID再利用回表的方式,在主键索引中过滤,如果索引是全字段记录,那就可以直接在二级索引(非聚集索引)精确检索,甚至有时候还可以享受到覆盖索引的红利。

二、SQL语句的优化

1. 执行计划分析

上面说过执行计划可以帮助我们快速分析SQL语句的情况,一般情况下,你首先可以看看是否使用到了索引,用到的索引类型,大概需要扫描的行数,以及extra列中的一些信息。

2. join大表驱动还是小表驱动?

简单来说,在关联字段可以走索引的情况下,谁作为驱动表谁就会全表扫描,例如如下的SQL语句:

select * from t2 left join t1 on t2.a = t1.a;

其中t1是小表,t2是大表,执行流程大致如下:

  1. 先从t2表取a字段。
  2. 用a字段与t1表进行匹配,类似于:select * from t1 where t1.a = ?
  3. 记录结果集。
  4. 然后重复1~3过程,直到t2全部扫描结束。

如果用:M(表示t2表的行数)、 N(表示t1表的行数)
时间复杂度大致如下:M * logN(索引扫描)

所以,很明显M的大小更能影响整体的复杂度,因此才会有建议用小表作为驱动表。

原则上,关联字段必须要建立索引,如果没有,MySQL虽然也会采用Block Nested Loop的方式进行优化,但实际上效果并不明显。

3. limit语句优化

分页

当使用limit分页时,比如像这样查询:select * from table LIMIT M,N

M越大查询的越慢,一般可以利用覆盖索引的特性来进件优化

select * from page_test a, ( select id from page_test LIMIT 100000,5) b where a.id = b.id;

限制查询条数

如果你只需要查询一条数据,那么就没有必要让MySQL扫描全表,建议加上limit 1

4. 索引合并

索引合并是指,对于单表通过多个索引过滤出来的数据,进行合并处理(一般包含:并集、交集、差集等合并处理逻辑)

比如像如下几种:

交集合并

select * from table where 主键ID < ? and 普通索引 = ?

并集合并

select * from table where 主键ID = ? or 普通索引 = ?

交集+并集

select * from table where 普通索引 = ? and 主键ID < ? or 普通索引 = ?

并集+排序

select * from table where 主键ID < ? or 普通索引 < ?

一般有索引合并必然会对CPU有一定的消耗,理论上应该尽量避免索引合并的情况发生,考虑通过建立联合索引来解决。

5. select * from table 不建议使用

select * from table很明显查的数据越多就越慢,这不仅仅与行数相关,列数也是会有一定影响的,并且如果可以利用覆盖索引的也利用不上了。

6. distinct 和 group by

大多数情况下,下面两个SQL语句查询是等效的。

SELECT DISTINCT c1, c2, c3 FROM t1 WHERE c1 > ?;

SELECT c1, c2, c3 FROM t1 WHERE c1 > ? GROUP BY c1, c2, c3;

三、大表问题

究竟多大的数据量算大表,这个数量本身就不太好定义,更重要的是你应该清楚表越大越会对什么样的事情造成影响,这才是真正需要关心的。

为了方便有个好理解的概念,我们先认为单表数据量到达千万级别,就算是大表。

1. 大表会带来哪些问题

1.1 增删改查

首先,毫无疑问,随着数据量的不断增长,对增删改查肯定是最直接的影响,但我想说的是,如果你能命中区分度较高的索引时,实际上还是非常快的,这个只针对无法通过索引过滤或者索引区分度不够高的情况。

1.2 添加索引

给一个千万级别的表添加索引时,是需要考虑至少存在秒级别的锁表情况,对业务是否会造成影响。
或者考虑采取一些替代方案,比如无锁变更(整个过程会变得更长)之类的,网上都有介绍,这里就不多说了。

1.3 添加字段

和添加索引一样,对大表进行DDL操作,都有可能导致锁表,这点需要额外的注意,替代方案和上面的一样。

1.4 数据同步

表越大,对于之后如果要做表数据备份、迁移,或者数据更新、初始化来说,都将是非常漫长的过程。

2. 怎么解决大表问题?

2.1 垂直拆分

垂直拆分就是针对表的列做拆分,通常是为了检索数据时能尽量减少单行数据的大小以及让数据页一次能够加载更多的数据,而我们通常所说的大表指的是单表的数据行数,因此垂直拆分并不是针对解决大表问题的。

2.2 水平拆分

水平拆分是解决大表问题的常见方案,简单来说就是把单表拆分成多表,被拆分后的每张表数据不重复,所有表数据加起来就是全集,比如我们可以将一张有1W条数据的表,拆分为5张表,每张表记录2000条数据。

水平拆分规则

2.2.1 按范围

表1:0~2000

表2:2001~4000

表3:4001~6000

表4:6001~8000

表5:8001~10000

非常简单的拆分规则,弊端也很明显,范围一旦确认后就很难再调整,无论是调整范围大小,还是调整范围内的数据。

2.2.2 按区域

按区域划分实际上和范围划分思想类似,都是圈定一个范围,然后按这个范围来拆分,按区域拆分有个很大的问题就是热点区域,比如:上海和新疆,很明显他们的数据量并不是一个数量级的,所以一般对于上海肯定还会再细分,按区域一般更多是业务上的选择。

2.2.3 按时间

和按范围、按时间也都差不多,一般适用数据冷热分离的情况。

2.2.4 hash取模

hash取模是表拆分规则常用的方式,一般会肯定比较有特征的业务数据,比如user_id, order_id等字段,假设我们要将一张表拆分为16(0~15)张表,那我们计算数据入哪张表的公式就是:hash(user_id) % 16

hash取模最大的好处就是能够将数据拆分的足够均匀。

上面无论哪种拆分方式,我们都要尽量事先能规划好分表的数量,因为一旦按照分表的数量开始拆分之后,后面想再调整就比较麻烦了。

2.3 分表后带来的问题

2.3.1 事务

显然,从原来的操作一张表,到分表后的多张表,肯定会额外带来事务的问题。

2.3.2 排序、分组、分页

无论是排序、分组还是分页肯定都是在全量数据集中完成的,所以分表后对于需要扫描全表才能得到的结果也需要额外处理。

3. 数据归档

数据归档一般是指将部分历史数据从原表中挪出去,比如放到一张专门的历史归档表中,之所以能这样处理的前提就是因为历史数据对于业务来说,实际使用场景已经并不多了,如果你的业务场景对待几年前的数据和刚产生的数据没有任何区别,那数据归档也没有任何意义了。

4. 冷热分离

冷热分离实际上更强调的是对热数据检索的支持,本质上被分离出来的冷数据,也是数据归档的思想,将更多的资源倾向分配给热数据以支持快速的检索。

四、内存缓冲区

1. buffer pool

buffer pool是一种常见的加速查询的优化方式,在一些专用的服务器上,差不多80%的内存都会用来构建buffer pool,可以大大减少访问磁盘的次数,提升查询效率。

1.1 buffer pool的空间管理方式

可以大致认为buffer pool采用的就是LRU的算法来进行空间管理,头部为最近访问过的数据,尾部为访问较少的数据。

举个简单的例子:假设现在只能存在3条数据。

第一次访问1,加入头部。

1 -> null

第二次访问2,加入头部。

2 -> 1 -> null

第三次访问3,加入头部。

3 -> 2 -> 1 -> null

第四次访问4,加入头部,1被淘汰出去。

4 -> 3 -> 2 -> null

这个应该不难理解,但考虑MySQL的实际业务场景,如果此时来了一次全表访问,那么4,3,2可能全部都会被淘汰出去,也就是说因此一次较大量的数据访问,将一些热点数据给淘汰了,这显然是不合理的。

所以,针对这个问题,MySQL也在原有的LRU算法上做了一些改进,按照3:5比例把buffer pool分为两块,其中3/8称为old区,5/8称为new区,new区的尾部指向old区的头部,new区的头部为整个链表的头部,old区的尾部为整个链表的尾部。

image.png

改变过的LRU算法,假设我们可以存储8页数据,大致淘汰过程如下:

image.png

此时,访问数据页2,直接按照LRU方式处理

image.png

但如果要访问一个不存在的数据页时,则只会被放在old区,比如访问数据页9,变成像下图这样

image.png

如果要访问数据页存在于old区,则每次要做如下判断:

  1. 若被访问的数据页在old区超过了1秒(可通过innodb_old_blocks_time指定),则把他挪到整个链表的头部(new区)。
  2. 若被访问的数据页在old区未超过1秒,则保持不变。

这样,我们看看如果再来一次全表扫描,会是什么样的:

  1. old区的数据页可能全部会被置换掉,同时因为同一数据页连续访问的时间间隔肯定不会超过1秒(因为全表扫描肯定是顺序的,扫描耗时肯定用不到1秒),所以这些数据页不会被挪到new区。
  2. 最终可以认为最多只有old区会被“污染”,但很快又会被淘汰出去。

经过改造后的LRU算法,可以确保new区不被“污染”,从而也就保证了查询的效率。

1.2 buffer bool使用情况

执行命令:show engine innodb status

image.png

2. Change Buffer

官方介绍对于change buffer的介绍

The change buffer is a special data structure that caches changes to secondary index pages when those pages are not in the buffer pool. The buffered changes, which may result from INSERT, UPDATE, or DELETE operations (DML), are merged later when the pages are loaded into the buffer pool by other read operations.

image.png

简单来说,就是当发生INSERT, UPDATE, or DELETE这样的DML操作时,如果被操作的二级索引数据页并不在buffer pool中,为了提升操作效率,MySQL会直接将这些DML语句直接写在change buffer中,这样就能减少一次从磁盘加载数据到内存的过程,之后如果再访问到此数据页时,再将此数据页加载到buffer pool中,并根据change buffer中的记录进行更新即可。

当然,可以看出,如果每次刚写完数据就要查询,那change buffer就没什么好处,不过,大多数情况下并没有此类需求,所以先写入change buffer,等待一定时间后再批量写入磁盘就好了。

五、ACID特性

1. 什么是ACID

  • A (Atomicity, 原子性)
    • 一个事务中的所有操作要不全部成功,要不全部失败,不能出现部分成功,部分失败的情况。
  • C(Consistency,一致性)
    • 数据库设计上这个含义比较模糊,简单可以理解为财务的对账一样,两边数据的加加减减必须要能保持一致。
  • I(Isolation,隔离性)
    • 主要是针对在并发场景下,对于共享资源的访问要有一定的隔离性,在MySQL中隔离性也是分等级的,根据不同的业务需求选择不同的隔离性,主要依靠锁+MVCC来实现,隔离性越强,数据库的吞吐就越差。
  • D(Durability,持久性)
    • 事务一旦提交,数据将会保存到数据库中,此时如果数据库发生错误,也不会造成数据丢失。

2. 如何实现ACID

A (Atomicity, 原子性)

MySQL主要是利用undo日志来实现原子性。

undo log又称撤销日志,就是记录相反操作的日志,比如执行delete时,记录的则是对应的insert,当执行insert时,记录则是对应的delete,当执行update时,记录则是相反的update记录,在执行所有操作之前undo log会先落盘。

C(Consistency,一致性)

对于一致性的问题,它是一个比较模糊的概念,原子性、持久性、隔离性都是为了保证一致性的实现,或者说一致性更侧重于业务层面由开发人员的控制。

I(Isolation,隔离性)

隔离性的实现主要是依靠MVCC+锁来实现。

MVCC(多版本并发控制)是实现并发访问的一种方式,在MySQL的innodb引擎读已提交和可重复读两种隔离级别下,事务在select时实际上读取的是版本链中的数据。

说简单点,就是每次访问数据前会先生成一份类似快照的信息,通过控制快照记录的时机来实现不同级别下的隔离性。

D(Durability,持久性)

持久性主要是利用redo log、binlog日志来保证的。

关于redo log和binlog会在下面的章节中单独介绍。

六、脏读、幻读,不可重复读

1. 概念解释

脏读

一个事务会读到另一个未提交的事务数据。

不可重复读

一个事务内多次读取到的数据不一致,A事务第一次读到值是1, B事务把1修改成2,并且提交了,A事务第二次读到值也变成了2,针对update。

幻读

一个事务内多次读取到的数据不一致,第一次读到1条,第二次读到2条。针对insert,delete,数据行数发生了变化。

2. 案例演示

要解释脏读、幻读,不可重复读,直接用案例最合适。

准备一张表tran_test ,id为主键,使用innodb存储引擎。

CREATE TABLE tran_test ( id INT PRIMARY KEY, NAME VARCHAR ( 10 ) ) ENGINE = INNODB;

插入3条数据。

insert into tran_test values(1,'zhangsan');
insert into tran_test values(2,'wangwu');
insert into tran_test values(3,'lisi');

打开的会话窗口都需要关闭自动提交,默认为1:表示开启自动提交,设置为0:表示关闭自动提交。

select @@autocommit;

set autocommit = 0;

2.1 脏读问题演示

A:set session transaction isolation level read uncommitted;
A:start transaction;
A:insert into tran_test values(4,'zhaoliu');
A:select * from tran_test; --可以查询到最新插入的数据
B:set session transaction isolation level read uncommitted;
B:select * from tran_test; -- 也可以查询A最新插入的数据,尽管A的此时的事务还未提交

image.png

2.2 不可重复读问题演示

当把隔离级别调整到不可重复读之后,再按照脏度问题演示的流程来执行,可以确认新建的会话窗口是读不到其他会话中未提交的事务的。

image.png

但是会产生不可重复读问题。

A:set session transaction isolation level read committed;
A:start transaction;
A:select * from tran_test where id = 1; --结果为zhangsan
B:start transaction;
B:update tran_test set name = 'zs' where id = 1; --把id为1的name修改为zs
A:select * from tran_test where id = 1; -- B未提交所以A此时查询结果还是zhangsan
B:commit;
A:select * from tran_test where id = 1; -- B事务提交后,A再查询此时结果已经变成了zs

A在同一个事务中,但两次查询结果不一致。

2.3 幻读问题演示

读者可自行测试,当隔离级别调整到可重复读时,前面两个操作流程都不会再出现脏度和不可重复读的问题。

不过,幻读的问题还是存在的。

A:set session transaction isolation level read committed;
A:start transaction;
A:select * from tran_test where id = 4; --结果为空
B:start transaction;
B:insert into tran_test values(4,'zhaoliu'); -- B插入一条id=4的数据
A:select * from tran_test where id = 4; --结果依旧为空
B:commit; -- B提交事务
A:select * from tran_test where id = 4; --结果依旧为空
A:insert into tran_test values(4,'zhaoliu'); -- 插入失败,报主键冲突
insert into tran_test values(4,'zhaoliu')
> 1062 - Duplicate entry '4' for key 'PRIMARY'
> 时间: 0s

七、日志

1. binlog

binlog又称二进制日志,是MySQL中非常重要的日志,与存储引擎无关,是MySQL服务级别的日志,它记录了所有的DML和DDL语句(查询类除外),binlog的主要可以用来做数据同步和数据恢复,binlog也是MySQL实现主从复制的主要方式

1.1 binlog的写入频率

binlog的写入频率是由sync_binlog参数控制,参数值有0、1、N,默认为1,每个参数的含义如下:

0:不让MySQL主动将binlog写入磁盘,而是让操作系统的文件写入系统来决定什么时候刷入到磁盘,在此状态下,可以达到最佳性能,但也可能带来丢失部分数据写入的日志。
1:这是最安全的设置,在事务提交的同时,也将日志同步写入磁盘,当然也会对性能产生一定的影响。
N:当N大于1时,则表示每N次事务,写一次磁盘,相当于在0~1之间再找一个平衡点,但是依旧存在数据丢失的可能。

1.2 binlog日志记录格式

STATEMENT

基于语句的日志记录,顾名思义,就是按照原SQL语句来记录。

优点

  1. 减少写入日志文件的体量,尤其是影响的行数特别多时,相比ROW格式可以大大减少存储的信息,这同时也意味着,当再进行数据同步或者数据恢复时速度会更快。
  2. 由于日志记录的是完整SQL语句,因此更有利于SQL审查。

缺点

  1. 对于一些带有函数的语句,可能并不能正确记录,比如像:UUID()、SYSDATE()、RAND()等。
  2. 因为是完整语句的记录,因此原SQL语句执行所需要的消耗,相当于还需要再来一遍。

ROW

基于数据行的日志记录,根据写入事件对数据行的影响,记录影响后的行信息。

优点

  1. 基于行模式的记录,是最安全的形式,所有的更改都可以复制。
  2. 对比STATEMENT的缺点,当在副本上再次执行insert、update或者delete语句时,所需要的行锁将会更少。

缺点

  1. 正如前面所说,基于行数据的记录,会大大增加日志的存储体量,同时在数据同步和数据恢复时也需要更多的时间。
  2. 行模式并不会记录原SQL语句,因此也没办法从源头执行。

MIXED

混合模式的日志记录,默认采用STATEMENT方式记录,当遇到某些特殊条件时(比如写入语句中有用到类似SYSDATE()的函数时),则转为ROW模式。

MIXED模式就是STATEMENT和ROW模式的结合。

2. Redo Log

redo log又叫重做日志,是InnoDB存储引擎中才有的,主要是在服务崩溃时用来恢复数据的记录,redo log记录了对数据库中每个页的修改,redo log分为两部分:一部分是在内存中的缓冲日志 (redo log buffer),一部分是在磁盘上的文件日志 (redo log file),内存是会丢失的,而磁盘是永久的。

与undo log一样,redo log也是先于数据之前先写入磁盘的。

如何提升落盘效率?

常见的方式就是先写内存,然后在从内存刷到磁盘,为了提高吞吐也可以选择在内存中停留一会再刷到磁盘上,但是这样如果在停留的那段时期内,宕机或者掉电那数据可就丢了,没错,因此MySQL也给了几种方案让你选择,就看你如何取舍了。

大致落盘过程:

写数据 —> 写redo log —> 写log buffer —> 写log file

innodb_flush_log_at_trx_commit参数决定了写入时机,0、1、2时分别表示下图写入方案,默认为1,也只有1的情况下才能保证持久性。

image.png

3. Undo Log

undo log又称撤销日志,就是记录相反操作的日志,比如执行delete时,记录的则是对应的insert,当执行insert时,记录则是对应的delete,当执行update时,记录则是相反的update记录,在执行所有操作之前undo log会先落盘。

假设数据库有一条记录:age=1,现在要将age修改为2,流程如下:

1、事务开启。
2、记录undo日志,update table set age = 1 where id = 1。
3、执行update,修改 update table set age = 2 where id = 1。
4、事务提交。
5、数据写入磁盘。

如果在写入磁盘前,发生异常,则可以通过undo log进行回滚。

八、主从复制

主从复制的方式

MySQL默认采用异步复制方式,从节点可以复制主节点中的所有数据或者特定的数据库、数据表,主节点数据变化时,可以实时的同步到从节点中。

MySQL主从复制是基于binlog文件的方式,当master服务器有变化时以“事件”的形式写入binlog文件,binlog中的信息会根据记录的数据库变化以不同的日志格式存储,slave服务器读取binlog,并在自己的数据库中执行binlog中的记录。

MySQL的主从复制功能主要使用3个线程来实现,1个在master服务器上,2个在slave服务器上

主从复制大致流程

  • Binary log dump thread(二进制日志转储线程)

    当从节点连接时,主节点创建一个线程将binlog文件内容发送到从节点,这个线程在主节点的show processlist输出中标识为binlog转储线程。

  • Replication I/O thread(IO线程)

    当在从节点上发出START SLAVE语句时,从节点会创建一个I/O线程,该线程连接到主节点,并要求它发送其binlog日志中记录的更新。
    I/O线程读取主节点的binlog转储线程发送的更新,并将它们复制到本地文件中,这些文件构成了从节点的relay log(中继日志)。
    在SHOW SLAVE STATUS的输出中,该线程的状态显示为Slave_IO_running。

  • Replication SQL thread (SQL线程)

    从节点创建一个SQL线程来读取I/O线程写入的中继日志,并执行其中包含的事务。

九、常见笔试题

1. 部门工资最高的员工

Employee表

idnamesalarydepartmentId
1zhangsan80001
2lisi50001
3wangwu60002
4zhaoliu70002
5qianqi80001

Department表

idname
1研发部
2销售部

期望输出结果

departmentNameemployeeNamesalary
研发部zhangsan8000
销售部zhaoliu7000
研发部qianqi8000

方法一:

SELECT
	c. NAME AS Department,
	a. NAME AS Employee,
	a.salary AS Salary
FROM
	Employee a,
	(
		SELECT
			departmentId,
			max(salary) AS sal
		FROM
			Employee
		GROUP BY
			departmentId
	) b,
	Department c
WHERE
	a.departmentId = b.departmentId
AND a.salary = b.sal
AND a.departmentId = c.id 

方法二:

SELECT
	c. NAME AS Department,
	a. NAME AS Employee,
	a.salary AS Salary
FROM
	Employee a,
	Department c
WHERE
	(a.departmentId, a.salary) IN (
		SELECT
			departmentId,
			max(salary)
		FROM
			Employee
		GROUP BY
			departmentId
	)
AND a.departmentId = c.id

2. 部门工资第N高的员工

解题方法

SELECT
	*
FROM
	Employee a,
	(
		SELECT
			salary
		FROM
			Employee
		GROUP BY
			salary
		ORDER BY
			salary DESC
		LIMIT N,
		1
	) b
WHERE
	a.salary = b.salary

3. 行转列

表结构

mysql> desc column_to_row;
+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| name     | varchar(255) | YES  |     | NULL    |       |
| subject1 | varchar(255) | YES  |     | NULL    |       |
| subject2 | varchar(255) | YES  |     | NULL    |       |
| subject3 | varchar(255) | YES  |     | NULL    |       |
+----------+--------------+------+-----+---------+-------+

表数据


mysql> select * from column_to_row;
+------+----------+----------+----------+
| name | subject1 | subject2 | subject3 |
+------+----------+----------+----------+
| 小明 | 80       | 90       | 100      |
| 小红 | 100      | 90       | 80       |
+------+----------+----------+----------+

解题方法

mysql> select name, 'subject1' as 'subject', subject1 as score from column_to_row
    -> union all
    -> select name, 'subject2' as 'subject', subject2 as score from column_to_row
    -> union all
    -> select name, 'subject3' as 'subject', subject3 as score from column_to_row
    -> ;
+------+----------+-------+
| name | subject  | score |
+------+----------+-------+
| 小明 | subject1 | 80    |
| 小红 | subject1 | 100   |
| 小明 | subject2 | 90    |
| 小红 | subject2 | 90    |
| 小明 | subject3 | 100   |
| 小红 | subject3 | 80    |
+------+----------+-------+

4. 列转行

表结构

mysql> desc row_to_column;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| name    | varchar(255) | YES  |     | NULL    |       |
| score   | varchar(255) | YES  |     | NULL    |       |
| subject | varchar(255) | YES  |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+

表数据

mysql> select * from row_to_column;
+------+-------+---------+
| name | score | subject |
+------+-------+---------+
| 小明 | 80    | 语文    |
| 小明 | 90    | 数学    |
| 小明 | 85    | 英语    |
| 小红 | 70    | 语文    |
| 小红 | 95    | 数学    |
| 小红 | 85    | 英语    |
+------+-------+---------+

解题方法

mysql> select name, max(if(subject = '语文', score, null)) as '语文' ,max(if(subject = '数学', score, null)) as '数学',max(if(subject = '英语', score, null)) as '英语' from row_to_column group by name;  
+------+------+------+------+
| name | 语文 | 数学 | 英语 |
+------+------+------+------+
| 小明 | 80   | 90   | 85   |
| 小红 | 70   | 95   | 85   |
+------+------+------+------+

5.树结构查询

表结构

mysql> desc t_org;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | int(11)      | NO   | PRI | NULL    |       |
| p_id  | int(11)      | YES  |     | NULL    |       |
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+

表数据

mysql> insert into t_org (id, p_id, name) values (1,null,'一级机构'),(2,1,'二级机构1-1'),(3,1,'二级机构1-2'),(4,2,'三级机构2-1'),(5,3,'三级机构3-1');
Query OK, 5 rows affected (0.03 sec)
Records: 5  Duplicates: 0  Warnings: 0
mysql> select * from t_org;
+----+------+-------------+
| id | p_id | name        |
+----+------+-------------+
|  1 | NULL | 一级机构    |
|  2 |    1 | 二级机构1-1 |
|  3 |    1 | 二级机构1-2 |
|  4 |    2 | 三级机构2-1 |
|  5 |    3 | 三级机构3-1 |
+----+------+-------------+
5 rows in set (0.06 sec)

5.1 查询树的根节点

mysql> select * from t_org where p_id is null;
+----+------+----------+
| id | p_id | name     |
+----+------+----------+
|  1 | NULL | 一级机构 |
+----+------+----------+
1 row in set (0.03 sec)

5.2 查询树的叶子节点

mysql> select * from t_org where id not in (select distinct p_id from t_org where p_id is not null) and p_id is not null;
+----+------+-------------+
| id | p_id | name        |
+----+------+-------------+
|  4 |    2 | 三级机构2-1 |
|  5 |    3 | 三级机构3-1 |
+----+------+-------------+
2 rows in set (0.05 sec)

5.3 查询既不是叶子也不是根的节点

mysql> select * from t_org where id in (select distinct p_id from t_org where p_id is not null) and p_id is not null;
+----+------+-------------+
| id | p_id | name        |
+----+------+-------------+
|  2 |    1 | 二级机构1-1 |
|  3 |    1 | 二级机构1-2 |
+----+------+-------------+
2 rows in set (0.05 sec)

5.4 输出完整的树

方法一:

mysql> select id, name, 'root' as type from t_org where p_id is null union all select id, name, 'inner' as type from t_org where id in (select distinct p_id from t_org where p_id is not null) and p_id is not null union all select id, name, 'leaf' as type from t_org where id not in (select distinct p_id from t_org where p_id is not null) and p_id is not null;
+----+-------------+-------+
| id | name        | type  |
+----+-------------+-------+
|  1 | 一级机构    | root  |
|  2 | 二级机构1-1 | inner |
|  3 | 二级机构1-2 | inner |
|  4 | 三级机构2-1 | leaf  |
|  5 | 三级机构3-1 | leaf  |
+----+-------------+-------+
5 rows in set (0.07 sec)

方法二:

mysql> select id, name, case
    -> when id in (select id from t_org where p_id is null) then 'root'
    -> when id in (select p_id from t_org where p_id is not null) then 'inner'
    -> else 'leaf' end as 'type' from t_org;
+----+-------------+-------+
| id | name        | type  |
+----+-------------+-------+
|  1 | 一级机构    | root  |
|  2 | 二级机构1-1 | inner |
|  3 | 二级机构1-2 | inner |
|  4 | 三级机构2-1 | leaf  |
|  5 | 三级机构3-1 | leaf  |
+----+-------------+-------+
5 rows in set (0.04 sec)

6. 排名问题

按分数从高到低排名,

表结构

mysql> desc t_rank;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | YES  |     | NULL    |                |
| score | int(11)      | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.04 sec)

表数据

mysql> insert into t_rank (name,score) values ('xiaoming',100),('xiaozhang',98),('xiaoli',98),('xiaowang',99),('xiaohong',97); 
Query OK, 5 rows affected (0.02 sec)
Records: 5  Duplicates: 0  Warnings: 0
mysql> select * from t_rank;
+----+-----------+-------+
| id | name      | score |
+----+-----------+-------+
|  1 | xiaoming  |   100 |
|  2 | xiaozhang |    98 |
|  3 | xiaoli    |    98 |
|  4 | xiaowang  |    99 |
|  5 | xiaohong  |    97 |
+----+-----------+-------+
5 rows in set (0.04 sec)

期望结果

+-----------+------+-------+
| name      | rank | score |
+-----------+------+-------+
| xiaoming  |    1 |   100 |
| xiaowang  |    2 |    99 |
| xiaoli    |    3 |    98 |
| xiaozhang |    3 |    98 |
| xiaohong  |    4 |    97 |
+-----------+------+-------+

解题方法:

第一步,排序

mysql> select name, score from t_rank order by score desc;
+-----------+-------+
| name      | score |
+-----------+-------+
| xiaoming  |   100 |
| xiaowang  |    99 |
| xiaozhang |    98 |
| xiaoli    |    98 |
| xiaohong  |    97 |
+-----------+-------+
5 rows in set (0.04 sec)

利用count就可以统计出排名

假设最高分为100分,则可以利用count加去重得出每个分数的排名。

比如统计分数大于等于100的有多少,结果为1,则表示排第1

mysql> select count(distinct score) from t_rank where score >= 100;
+-----------------------+
| count(distinct score) |
+-----------------------+
|                     1 |
+-----------------------+
1 row in set (0.04 sec)

统计分数大于等于99的有多少,结果为2,则表示排第2

mysql> select count(distinct score) from t_rank where score >= 99;
+-----------------------+
| count(distinct score) |
+-----------------------+
|                     2 |
+-----------------------+
1 row in set (0.05 sec)

以此类推。。。

组合前2步,即可得到期望的结果

mysql> select score, (select count(distinct score) from t_rank a where a.score >= b.score) from t_rank b order by b.score desc;
+-------+---------------------------------------------------------------------+
| score | (select count(distinct score) from t_rank a where a.score>=b.score) |
+-------+---------------------------------------------------------------------+
|   100 |                                                                   1 |
|    99 |                                                                   2 |
|    98 |                                                                   3 |
|    98 |                                                                   3 |
|    97 |                                                                   4 |
+-------+---------------------------------------------------------------------+
5 rows in set (0.04 sec)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文解决MySQL突击面试,关键知识点总结 的相关文章

  • C++之单例类模板

    需求的提出 在架构设计时 xff0c 某些类在整个系统生命周期中最多只能有一个对象存在 xff08 Single Instance xff09 要控制类的对象数目 xff0c 必须对外隐藏构造函数 xff1b 思路 xff1a 1 将构造函
  • 【无标题】

    绘图控件GraphicsView 一 GraphicsView简介 1 QT有多种绘图相关的技术 xff0c 我们将在第2部分 2 4 QT绘图和图表 中比较详细系统的讲 2 本节简单讲一下GraphicsView的基本理论 xff0c 并
  • uboot源码分析之start.S解析

    1 start S引入 1 1 u boot lds中找到start S入口 1 在uboot中因为有汇编阶段参与 xff0c 因此不能直接找main c 整个程序的入口取决于链接脚本中ENTRY声明的地方 ENTRY start 因此 s
  • uboot启动第二阶段

    uboot启动第二阶段 start armboot函数简介 一个很长的函数 1 这个函数在uboot lib arm board c的第444行开始到908行结束 2 450行还不是全部 xff0c 因为里面还调用了别的函数 3 为什么这么
  • cmake设置编译类型为release命令

    cmake编译类型通常默认为debug xff0c 但是在编译软件时 xff0c 一般都需要使用release版本的 xff0c debug太慢了 设置为release版本可以在cmake文件里进行 xff0c 也可以在运行cmake命令时
  • 设计模式之单例模式(Singleton),以C++为例,实现日志输出。

    Hello大家好 xff0c 好久没更新了 xff0c 今天给大家补上最基础的设计模式 xff1a 单例模式 这个单例模式实在是我的心结啊 xff0c 2021年末左右面试京东算法岗 xff0c 面试官让我写一个单例 xff0c 没写出来
  • 源码分析MyBatis对数值(int、double)类型进行test判断的误区

    文章目录 问题描述问题分析验证解析表达式执行解析后表达式分别测试两个条件 查询Ognl官方文档验证问题解决 问题描述 在如下判断中 xff0c 如果type类型为int xff0c 那么对于type 61 39 39 部分判断会出现一些问题
  • Git报错:error: xxxx bytes of body are still expected.

    git一个很老的项目 xff0c 项目深度很深 xff0c 报错 xff1a error 7857 bytes of body are still expected fetch pack unexpected disconnect whil
  • 设计模式之代理模式(Proxy),以C++为例,实现远程代理、虚拟代理、保护代理等。

    兄弟姐妹们好 xff0c 又是好久没有更新了 xff0c 今天给大家简单介绍代理模式 xff0c 一个很简单的设计模式 xff0c 旨在不改变原对象的情况下通过代理对象来控制对原对象的访问 代理模式根据具体情况还可以分为远程代理 虚拟代理
  • C++ 互斥锁原理以及实际使用介绍

    兄弟姐妹们 xff0c 我又回来了 xff0c 今天带来实际开发中都需要使用的互斥锁的内容 xff0c 主要聊一聊如何使用互斥锁以及都有哪几种方式实现互斥锁 实现互斥 xff0c 可以有以下几种方式 xff1a 互斥量 xff08 Mute
  • 【C++】使用【windwos api】获取windwos计算机的基本信息

    今天来一篇获取windows计算机的基本信息的文章 xff0c 包含计算机名称 操作系统版本 处理器信息 内存信息 硬盘信息 显示器信息 网络信息 驱动程序信息 电源信息 其他硬件信息 目录 一 windwos系统包含的基本信息 二 获取信
  • C++ POCO库的基础介绍(Windwos和Linux)

    简单介绍C 43 43 POCO库能干什么 xff0c 后续有时间的话将根据其每个点详细解析 xff0c 关注我 本篇包含POCO库简单介绍 下载以及安装方式 简单代码示例 目录 一 POCO简单介绍 1 1 POCO库的基本模块 1 2
  • ROS踩坑记录

    ROS踩坑记录 问题 xff1a ubuntu 没有 dev ttyUSB0问题 xff1a 运行 launch 文件或 ROS 节点时出现 exit code 9 错误提示问题 xff1a windows使用vscode远程连接 xff0
  • STM32串口数据接收 --环形缓冲区

    STM32串口数据接收 环形缓冲区 环形缓冲区简介 在单片机中串口通信是我们使用最频繁的 xff0c 使用串口通信就会用到串口的数据接收与发送 xff0c 环形缓冲区方式接收数据可以更好的保证数据丢帧率第 在通信程序中 xff0c 经常使用
  • 如何设计安全可靠的开放接口---对请求参加密保护

    文章目录 如何设计安全可靠的开放接口 系列前言AES加解密代码实现 如何设计安全可靠的开放接口 系列 1 如何设计安全可靠的开放接口 之Token 2 如何设计安全可靠的开放接口 之AppId AppSecret 3 如何设计安全可靠的开放
  • rosdep init报错解决方法

    rosdep init报错解决方法 很多小伙伴在安装ROS的过程中都不可避免的要执行rosdep init和rosdep update这两行命令行 xff0c 这也是在安装ROS的过程中最让人头疼的两步 xff0c 一般都没法一次成功 xf
  • NVIDIA Jetson Nano/Xavier NX 扩容教程

    在售的 NVIDIA Jetson 内置 16 GB 的 eMMC xff0c 并已安装了 ubuntu 18 04 LTS 和 NVIDIA JetPack 4 6 xff0c 所以剩余的用户可用空间大约 2GB xff0c 这对将 NV
  • 深度学习框架YOLOv3的C++调用

    深度学习框架YOLOv3的C 43 43 调用 深度学习框架YOLOv3的C 43 43 调用 xff08 1 xff09 tensorflow版本的YOLOv3的C 43 43 调用 xff08 失败 xff09 xff08 2 xff0
  • 基于GPT-2实现图像文本生成

    原理 使用GPT 2模型处理文本 xff0c 做decoder 使用google的vit base patch16 224模型处理图像 xff0c 做encoder 最后通过VisionEncoderDecoderModel将这两个模型粘起
  • C语言中常见的两个比较字符串是否相等的函数strcmp和strncmp

    函数 xff1a strcmp和strncmp strcmp 使用格式 xff1a include lt string h gt int strcmp const char s1 const char s2 设这两个字符串为str1 xff

随机推荐

  • sprintf和printf 用法的区别

    printf 的作用是标准化输出 xff0c 默认的对象是标准输出缓冲区 xff0c 要有一定的条件才能把缓冲区里面的数据输出 sprintf 作用是格式化输出函数 xff0c 保存字符串到缓冲区中 xff0c 起到拼接字符串的作用 功能
  • 第六篇,STM32脉冲宽度调制(PWM)编程

    1 PWM概念 PWM叫脉冲宽度调制 Pulse Width Modulation xff0c 通过编程控制输出方波的频率和占空比 高低电平的比例 xff0c 广泛应用在测量 xff0c 通信 xff0c 功率控制等领域 呼吸灯 xff0c
  • 第十篇,STM32串口蓝牙编程

    1 串口蓝牙概念 串口蓝牙是一个蓝牙模块 xff0c 内部有蓝牙模块和程序 xff0c 可以进行蓝牙通信 xff0c 同时提供一个串口接口 xff0c 通过串口可以配置蓝牙模块进行数据传输 2 使用串口3连接蓝牙模块 3 手机上安装蓝牙调试
  • LeetCode岛屿问题通用解决模板

    文章目录 前言第一题 xff1a 求岛屿的周长模板整理遍历方向确定边界重复遍历问题处理 模板解第一题第二题 xff1a 求岛屿数量第三题 xff1a 岛屿的最大面积第四题 xff1a 统计子岛屿第五题 xff1a 统计封闭岛屿的数目第六题
  • 第十四篇,STM32的CAN总线通信

    1 CAN总线的概念 CAN指的是控制器局域网网络 Controller Area Network xff0c 由德国博世汽车电子厂商开发出来 CAN使用差分信号 xff0c 具有较强的抗干扰能力和传输稳定性 CAN属于多主通信 xff0c
  • OpenCV图像处理学习十九,像素重映射cv::remap

    一 像素重映射概念 重映射就是把输入图像中各个像素按照制定的规则顺序映射到另外一张图像的对应位置上去 xff0c 形成一张新的图像 二 像素映射API函数接口 cv remap xff08 InputArray src 输入图像 Outpu
  • OpenCV图像处理学习二十一,直方图比较方法

    一 直方图比较 直方图比较是对输入的两张图像进行计算得到直方图H1与H2 xff0c 归一化到相同的尺度空间 xff0c 然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度 xff08 每张图像都有唯一的直方图与之对应 xff0
  • 嵌入式FreeRTOS学习九,任务链表的构成,TICK时间中断和任务状态切换调度

    一 tskTaskControlBlock 函数结构体 在tskTaskControlBlock 任务控制块结构体中 xff0c 其中有任务状态链表和事件链表两个链表成员 xff0c 首先介绍任务状态链表这个结构 xff0c 这个链表通常用
  • SOAP传输协议

    一 HTTP传输协议 超文本传输协议 xff08 HyperText Transfer Protocol xff0c 缩写 xff1a HTTP xff09 xff0c 它是基于请求 响应的模式协议 xff0c 客户端发出请求 xff0c
  • ONVIF简介

    一 什么是ONVIF ONVIF规范描述了网络视频的模型 接口 数据类型以及数据交互的模式 并复用了一些现有的标准 xff0c 如WS系列标准等 ONVIF规范的目标是实现一个网络视频框架协议 xff0c 使不同厂商所生产的网络视频产品 x
  • gsoap工具生成onvif设备搜索(remotediscovery)代码框架

    什么是gsoap工具 xff1f gSOAP 提供了两个工具来方便开发人员使用 C C 43 43 语言快速开发Web 服务应用 xff0c 通过 gSOAP 提供的这两个工具 xff0c 开发人员可以快速生成服务端与客户端代码框架 xff
  • Latex之给字符上加横线、波浪等

    Latex 前几天想在 x x x 上加波浪号 xff0c 一时间忘记怎么打 xff0c 现在记录下来 xff0c 以后好查阅 加 号 xff1a hat x 加横线 xff1a overline x 加宽 xff1a widehat x
  • 数据结构笔记-2(线性表)

    线性表 2 1 线性表 1 定义 是零个或多个具有相同类型的数据元素的有序数列 xff1b xff08 长度等于零的线性表为空表 xff09 非空线性表通常记为 xff1a L xff1d a 1 xff0c a 2 xff0c xff0c
  • 数据结构-6(图)

    图 图的逻辑结构 图的定义 xff1a 图是由顶点的有穷非空集合和顶点之间边的集合组成 xff0c 通常表示为 xff1a G 61 V xff0c E 其中 xff1a G表示一个图 xff0c V是图G中顶点的集合 xff0c E是图G
  • 【leetcode常见面试题】螺旋矩阵解题思路

    文章目录 螺旋矩阵解题思路先找行进路线找每条路线的结束位置再找每条路线的结束位置模拟行走 螺旋矩阵 II总结 螺旋矩阵 解题思路 本题可以采用模拟的方式 xff0c 设4种行走方向 xff0c 如下图 xff1a 先找行进路线 4个方向的行
  • C++面向对象程序设计学习心得

    C 43 43 面向对象程序设计学习心得 经过几周c 43 43 面向对象程序设计的学习 xff0c 对面向对象程序设计有了一些了解 递归 简单地讲 xff0c 递归就是程序直接或间接调用本身的编程技巧 xff0c 通过把一个不能或不好解决
  • STL学习心得

    STL概述 STL组件 1 容器 xff08 Container xff09 xff0d 管理某类对象的集合 2 迭代器 xff08 Iterator xff09 xff0d 在对象集合上进行遍历 xff08 注意 xff1a 这些集合可能
  • 安装nodejs和vue出现问题

    安装nodejs国内镜像时报错 npm install g cnpm registry 61 https registry npm taobao org不知道这是啥错误 xff0c 怎么改啊 xff1f 安装vue 从官网点击下载 下载后点
  • Jetson nano/nx通过网线连接电脑实现远程控制

    Jetson nano nx通过网线连接电脑实现远程控制 摘要1 nano nx桌面共享设置2 安装dconf editor解除加密3 自启VNC server4 网络共享5 获取IP地址6 安装PuTTy7 安装VNC Viewwer8
  • 一文解决MySQL突击面试,关键知识点总结

    文章目录 MySQL重要知识点回顾一 索引1 为什么需要索引2 索引的结构3 避免索引失效3 1 联合索引不满足最左匹配原则3 2 隐式转换3 3 like查询3 4 索引列存在运算或者使用函数3 5 优化器 4 执行计划4 1 type4