-
复合索引未用左列字段;
-
like以%开头;
-
需要类型转换;
-
where中索引列有运算;
-
where中索引列使用了函数;
-
如果mysql觉得全表扫描更快时(数据少);
12. B+树
-
MySQL 中最常用的索引的数据结构是 B+ 树,有以下特点:
1)在 B+ 树中,所有数据记录节点都是按照键值的大小存放在同一层的叶子节点上,而非叶子结点只存储key的信息,这样可以大大减少每个节点的存储的key的数量,降低B+ 树的高度
2)B+ 树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针。
-
B+树和B树的区别:
1)B+ 树的层级更少:相较于 B 树 B+ 每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快
2)B+ 树查询速度更稳定:B+ 所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
3)B+ 树天然具备排序功能:B+ 树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
4)B+ 树全节点遍历更快:B+ 树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像 B 树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
13. 为什么用B+树而不是B树?
-
在B树中,你可以将键和值存放在内部节点和叶子节点,但在B+树中,内部节点都是键,没有值。
-
B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。
-
B树和B+树区别:
-
关键字数量不同:B+树分支结点M个关键字,叶子节点也有M个;B树分支结点则存在 k-1 个关键码
-
数据存储位置不同:B+树数据存储在叶子结点上,而且数据是按照顺序排列的;B树存储在每个结点上;
-
查询不同:B+树是从根节点到叶子节点的路径;B树是只需要找到数据就可以
-
分支节点存储信息不同:B+树存索引信息;B树存的是数据关键字
-
14. B树、B-树、B+树、B*树
-
B树:每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;
-
B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整棵树中出现,且只出现一次,非叶子结点可以命中;
-
B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
-
B*树: 在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;
15. 乐观锁和悲观锁
-
悲观锁:假定会发生并发冲突,屏蔽一切违反数据完整性的操作。每次取数据的时候都会上锁,想要拿数据就会block直到拿到锁。适用于写多读少的场景。如synchorized就是悲观锁的一种实现,适用于写多读少场景。
- 行锁、表锁、页锁
-
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。只在提交更新时判断别人有没有更新数据。适用于读多写少的场景。乐观锁的实现方式一般包括使用版本号和时间戳。如CAS,实现方式:
1)数据版本:需要乐观锁控制的table中增加一个字段version
2)时间戳:需要乐观锁控制的table中增加一个字段timestamp
16. 事务
-
定义:访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
-
事务的ACID特性:
1)原子性Atomicity:一个事务时不可分割的单位,事务中的操作要么都做,要么都不做。
2)一致性Consistency:事务操作前后数据库完整性一致。
3)隔离性Isolation:一个事务的执行不能被其他事务干扰
4)永久性Durability:事务完成后对数据库的改变是永久性的。
-
事务隔离级别
1)未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。
2)已提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)。
3)可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,即后一次查询看到了前一次查询没有看到的行。
4)串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。
17. 事务的实现原理
-
原子性:使用 undo log ,从而达到回滚
-
持久性:使用 redo log,从而达到故障后恢复
-
隔离性:使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行
-
一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。
18. 脏读、不可重复读、幻读
-
脏读:脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
-
不可重复读:不可重复读是指在同一个事务内,两次相同的查询返回了不同的结果。例如:事务T1会读取两次数据,在第一次读取某一条数据后,事务T2修改了该数据并提交了事务,T1此时再次读取该数据,两次读取便得到了不同的结果。不可重复读的重点是修改
-
幻读:幻觉读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。幻读的重点在于新增或者删除
19. 为什么实际开发中使用已提交读更多?
-
在可重复读隔离级别下,存在间隙锁,导致出现死锁的几率比已提交读大的多!
-
在可重复读隔离级别下,条件列未命中索引会锁表!而在已提交读隔离级别下,只锁行
-
在已提交读隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性!
20. 间隙锁(Gap)
-
定义:一个在索引记录之间的间隙上的锁。
-
作用:保证某个间隙内的数据在锁定情况下不会发生任何变化。比如mysql默认隔离级别下的可重复读(RR)。
-
对主键索引或者唯一索引会使用间隙锁吗?
-
如果where条件全部命中,则不会使用gap锁,只会加记录锁
-
如果where条件部分命中或全不命中,则会加gap锁
-
-
Gap锁会用在非唯一索引或不走索引的当前读中
21. 快照读和当前读
-
快照读:读取的是记录数据的可见版本(可能是过期的数据),不用加锁,第一次执行事务中select时生成快照。
-
当前读:读取的是记录数据的最新版本,并且当前读返回的记录都会加上锁,保证其他事务不会再并发的修改这条记录,当你执行update、insert、delete这几个操作的时候默认会执行当前读。
22. 对象属性与数据库字段不一致
1)Sql语句起别名
2)Mapper.xml中的resultMap自定义映射,
3)配置文件中开启驼峰命名规则(对象属性为驼峰命名)
23. 数据库优化方法
(1)选取最适用的字段属性
MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
(2)使用连接(JOIN)来代替子查询(Sub-Queries)
MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询
(3)使用联合(UNION)来代替手动创建的临时表
MySQL从4.0的版本开始支持union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要想同。
(4)事务
尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
24. 慢查询优化方式
https://blog.csdn.net/qq_35571554/article/details/82800463
-
数据库中设置SQL慢查询
-
方式一:修改配置文件 在 my.ini 增加几行: 主要是慢查询的定义时间(超过2秒就是慢查询),以及慢查询log日志记录( slow_query_log)
-
方法二:通过MySQL数据库开启慢查询:
-
-
分析慢查询日志
直接分析mysql慢查询日志 ,利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句,具体记录了:是哪条语句导致慢查询(sql_text),该慢查询语句的查询时间(query_time),锁表时间(Lock_time),以及扫描过的行数(rows_examined)等信息。
-
常见的慢查询优化
-
索引没起作用的情况
-
优化数据库结构
-
将字段很多的表分解成多个表
-
增加中间表
-
-
分解关联查询
-
优化LIMIT分页
-
筛选字段加索引
-
先查出主键ID
-
关延迟联
-
建立复合索引
-
-
分析具体的SQL语句
-
-
具体步骤
-
第一步:根据慢日志定位慢查询SQL
-
首先检查SQL中是否使用函数,隐式类型转换(字符串转数字),隐式函数或者传入的值超过索引长度
-
SQL中字段的字符集是否一致
-
如果使用count计数,尽量使用count(*)或者count(1),count(字段)会涉及到回表操作,count(id)会全表扫描,并且count(字段)和count(id)都需要判空操作,也可以按照具体的业务选择MyISAM引擎,直接取值
-
查看当前语句的状态,是否是在等MDL锁,数据页flush,其他线程占用了行锁
-
如果是热点数据,需要控制访问资源的并发事务量,可以将一行数据改成逻辑上的多行数据
-
-
第二步:使用explain等工具分析 SQL
-
查看SQL是否按照理想的状态检索最少的数据行,如果没有则查看是否走了指定索引
-
判断优化器是否选错索引,可以通过强行选择索引或者重新统计索引信息
-
-
第三步:修改SQL或者尽量让SQL走索引
-
在数据库空闲的时候,定期进行索引统计,防止优化器选错索引,造成索引失效
-
优化手段:聚簇索引,覆盖索引,索引下推优化,联合索引
-
字符串索引:前缀索引,倒序存储,hash索引
-
如果需要业务字段做索引,必须确保是唯一索引,符合K-V结构,不需要考虑其他索引叶子节点的大小
-
尽量使用自增主键索引,每次插入新的数据都是追加操作,可以防止数据页黑洞出现,保证索引的紧凑,不涉及挪动其他数据,也不会触动叶子节点的页分裂
-
主键索引的长度不可以过长,造成其他索引树的叶子节点较大
-
在建立联合索引的时候,如果可以通过顺序少建立一个索引,则调整顺序,同时需要考虑空间占用
-
数据写多读少的时候选择普通索引,利用change buffer可以提高效率,合理设置change buffer大小,防止频繁merge
-
如果sql语句出现锁操作,尽量让锁操作最后执行,防止影响其他SQL的执行
-
如果使用长连接,在进行较大查询之后,需要重置链接,防止占用较大内存,造成数据库异常重启
-
不要删除索引,删除索引可能会造成页分裂,导致数据页出现黑洞
-
使用order by查询的时候,如果单行数据过大,会造成回表操作,可以使用联合索引,让字段本身有序
-
-
25. 数据库水平切分与垂直切分
垂直拆分就是要把表按模块划分到不同数据库表中(当然原则还是不破坏第三范式),这种拆分在大型网站的演变过程中是很常见的。当一个网站还在很小的时候,只有小量的人来开发和维护,各模块和表都在一起,当网站不断丰富和壮大的时候,也会变成多个子系统来支撑,这时就有按模块和功能把表划分出来的需求。其实,相对于垂直切分更进一步的是服务化改造,说得简单就是要把原来强耦合的系统拆分成多个弱耦合的服务,通过服务间的调用来满足业务需求看,因此表拆出来后要通过服务的形式暴露出去,而不是直接调用不同模块的表,淘宝在架构不断演变过程,最重要的一环就是服务化改造,把用户、交易、店铺、宝贝这些核心的概念抽取成独立的服务,也非常有利于进行局部的优化和治理,保障核心模块的稳定性。垂直拆分:单表大数据量依然存在性能瓶颈
水平拆分,上面谈到垂直切分只是把表按模块划分到不同数据库,但没有解决单表大数据量的问题,而水平切分就是要把一个表按照某种规则把数据划分到不同表或数据库里。例如像计费系统,通过按时间来划分表就比较合适,因为系统都是处理某一时间段的数据。而像SaaS应用,通过按用户维度来划分数据比较合适,因为用户与用户之间的隔离的,一般不存在处理多个用户数据的情况,简单的按user_id范围来水平切分。
通俗理解:水平拆分行,行数据拆分到不同表中,垂直拆分列,表数据拆分到不同表中。
26. Statement 和 PreparedStatement 的区别
与Statement相比,PreparedStatement接口代表预编译的语句,它主要的优势在于:
-
可以减少SQL的编译错误并增加SQL的安全性(减少SQL注射攻击的可能性);
-
PreparedStatement中的SQL语句是可以带参数的,避免了用字符串连接拼接SQL语句的麻烦和不安全;
-
当批量处理SQL或频繁执行相同的查询时,PreparedStatement有明显的性能上的优势,由于数据库可以将编译优化后的SQL语句缓存起来,下次执行相同结构的语句时就会很快(不用再次编译和生成执行计划)。
27. MyBatis 中 #{} 和 ${} 的区别
-
${} 拼接符,#{} 占位符
-
${}:动态解析 -> 编译 -> 执行;#{}:动态解析 -> 预编译 -> 执行,#{} 能防止sql 注入
-
变量替换后,#{} 对应的变量自动加上单引号 ‘’;变量替换后,${} 对应的变量不会加上单引号 ‘’
-
表名作参数时,必须用 ${}。如:
select * from ${tableName}
- order by 时,必须用 ${}。如:
select * from t_user order by ${columnName}
28. select *缺点
总结
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
需要免费领取这份Kafka源码笔记的铁汁们,麻烦帮忙转发一下这篇文章+关注我,然后戳这里免费获取!
select * from ${tableName}
* order by 时,必须用 ${}。如:
select * from t_user order by ${columnName}
### 28\. select \*缺点
# 总结
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
**需要免费领取这份Kafka源码笔记的铁汁们,麻烦帮忙转发一下这篇文章+关注我,然后[戳这里免费获取!](https://gitee.com/vip204888/java-p7)**
![就这一次!拼多多内部架构师培训Kafka源码笔记(现已绝版)](https://img-blog.csdnimg.cn/img_convert/988f458b2ba06064badc785d1db7148c.png)