数据库
2026-10-16 14:20:27 0 举报数据库面试相关的思维导图、mysql、redis等
算法
模板推荐
作者其他创作
大纲/内容
redis
优缺点
单线程纯内存操作,每秒可以处理超过10W次读写操作
CPU不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程,所有使用了单线程。
<br> * 高性能服务器不一定是多线程的<br> * <br>CPU上下文切换一定不单线程效率高?<br><br><br>redis是将所有数据放在内存中的,所以单线程操作最高(多次读写在一个CPU上所以最快不用上下文切换)
场景
热点数据缓存
限时业务
分布式锁
redis的五种数据类型+三种特殊类型
五大基本数据类型
String
计数器
粉丝数
统计多单位的数量
不长变化的场景
int编码 可以用long类型表示整数
embstr编码 小于44字节 (redis3.2之前是39字节)
cpucacheline每次读是64byte(64位操作系统)
redisObject一般只占20byte剩下的空间缓存embstr字符串
raw编码 保存长度大于44字节的(redis3,2之前是39字节)
List
实际上是链表Node
消息队列
在两边插入或者改动效率高,中间元素相对效率低
发布订阅号的推送消息
ziplist+linkedlist(3.2之前)
ziplist+quickList(3.2之后)
压缩的原理是将数据按照一定的规则编码在一块连续的内存区域 节约内存
满足1.列表元素小于512个 2每个元素长度小于64字节 使用ziplist
不满足使用linkedlist
Hash
小型购物车<br>
hlen获取商品总数
ziplist
存储方式和list的ziplist一样 哈希对象的压缩列表底层
hashtable
hashtable编码的哈希表底层使用字典数据结构,哈希对象中每个键值对都使用一个字典键值对
Set
微信小程序开奖
微博公共关注的人放set里 粉丝也放set里然后有交集 并集
集合对象的编码可以是 intset 或者 hashtable。<br><br> intset 编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合中。<br><br> hashtable 编码的集合对象使用 字典作为底层实现,字典的每个键都是一个字符串对象,这里的每个字符串对象就是一个集合中的元素,而字典的值则全部设置为 null。这里可以类比Java集合中HashSet 集合的实现,HashSet 集合是由 HashMap 来实现的,集合中的元素就是 HashMap 的key,而 HashMap 的值都设为 null。
共同关注 推荐好友
Zset
存储班级成绩表 设置权重判断
排行榜
ziplist
元素数量小于128并且所有元素的长度小于64字节
第一个节点保存 member
第二个保存 score
skiplist
skiplist 编码的有序集合对象使用 zet 结构作为底层实现,一个 zset 结构同时包含一个字典和一个跳跃表
这两种数据结构会通过指针来共享相同元素的成员和分值,所以不会产生重复成员和分值,造成内存的浪费。
说明:其实有序集合单独使用字典或跳跃表其中一种数据结构都可以实现,但是这里使用两种数据结构组合起来,原因是假如我们单独使用 字典,虽然能以 O(1) 的时间复杂度查找成员的分值,但是因为字典是以无序的方式来保存集合元素,所以每次进行范围操作的时候都要进行排序;假如我们单独使用跳跃表来实现,虽然能执行范围操作,但是查找操作有 O(1)的复杂度变为了O(logN)。因此Redis使用了两种数据结构来共同实现有序集合。
个节点不会影响其它节点的层数。因此,插入操作只需要修改插入节点前后的指针,而不需要对很多节点都进行调整。这就降低了插入操作的复杂度。实际上,这是skiplist的一个很重要的特性,这让它在插入性能上明显优于平衡树的方案
跳表优点
跳表的线程安全也是通过cas锁实现的,跳表的构建相对简单,同时支持范围查找
相对于红黑树,增加了空间消耗增加
三种特殊的数据类型
geospatial
附近的人定位等功能
底层用Zset实现 可以用Zset的方法去做
hyoerloglog<br>
基于loglog算法用于基数统计
基数(两个集合中不重复的元素)<br>基数统计的算法 网页的UV(一个人访问一个人网站多次还算一个人)<br>
允许容错可以使用这个技术
bitmap
签到系统
统计感人人数
统计用户信息(活跃、不活跃、登录、未登录等二状态)
分布式锁
实现分布式锁要满足3点:多进程可见,互斥,可重入。
多进程可见
redis本身就是jvm之外的满足多个jvm的时候线程间互斥<br>
互斥
setnx
即同一时间只能有一个进程获取锁标记,我们可以通过redis的setnx实现,只有第一次执行的才会成功并返回1,其它情况返回0。<br>
释放锁<br> 释放锁其实只需要把锁的key删除即可,使用del xxx指令。不过,如果在我们执行del之前,服务突然宕机,那么锁就永远无法删除了。所以我们可以通过setex 命令设置过期时间即可。
1.一:获取锁时<br> setnx获取锁成功了,还没来得及setex服务就宕机了,由于这种非原子性的操作,死锁又发生了。其实redis提供了 nx 与 ex连用的命令。
二:释放锁时<br> 1. 3个进程:A和B和C,在执行任务,并争抢锁,此时A获取了锁,并设置自动过期时间为10s<br> 2. A开始执行业务,因为某种原因,业务阻塞,耗时超过了10秒,此时锁自动释放了<br> 3. B恰好此时开始尝试获取锁,因为锁已经自动释放,成功获取锁<br> 4. A此时业务执行完毕,执行释放锁逻辑(删除key),于是B的锁被释放了,而B其实还在执行业务<br> 5. 此时进程C尝试获取锁,也成功了,因为A把B的锁删除了。<br> 问题出现了:B和C同时获取了锁,违反了互斥性!如何解决这个问题呢?我们应该在删除锁之前,判断这个锁是否是自己设置的锁,如果不是(例如自己 的锁已经超时释放),那么就不要删除了。所以我们可以在set 锁时,存入当前线程的唯一标识!删除锁前,判断下里面的值是不是与自己标识释放一 致,如果不一致,说明不是自己的锁,就不要删除了。
可以使用线程ID来识别是否是自己的锁
可重入
缓存问题
缓存穿透
服务器和缓存都没命中,用户还是不断发起请求
1.业务层面校验
2.为不存在的数据设置缓存 和过期时间
3.布隆过滤器
允许一定的误差
缓存击穿
缓存没命中,全请求到服务器了
1.设置热点数据不过期
2.互斥锁
缓存雪崩
redis缓存数据大面积失效或者Redis宕机,导致大量请求一下子打数据库
1.数据预热
2,关闭某些功能降低服务器压力
3.设置过期时间+噪声,防止同时过期
持久化策略(redis断电即失)
RDB
触发方式
1.save配置文件中可以设置
2.flushall命令<br>
生成dump.rdb
3.退出redis会产生rdb
恢复机制
只需要将dump.rdb 直接放在启动目录下,启动就会自动恢复的数据。
redis服务器载入rdb文件的时候会一直处于阻塞状态,知道载入工作完成
优缺点
<div><span style="font-size: 21.3333px;"><span style="font-size: 21.3333px; font-weight: bold;">1.适合大数据的数据恢复!</span></span></div><div><span style="font-size: 21.3333px;"><span style="font-size: 21.3333px; font-weight: bold;">2.如果对数据完整性不高!</span></span></div><div><span style="font-size: 21.3333px;"><span style="font-size: 21.3333px; font-weight: bold;">缺点</span></span></div><div><span style="font-size: 21.3333px;"><span style="font-size: 21.3333px; font-weight: bold;">1.需要一定的时间间隔进程操作。如果redis意外 宕机了,这个最后一次修改的数据就没了</span></span></div><div><span style="font-size: 21.3333px;"><span style="font-size: 21.3333px; font-weight: bold;">2.fork进程的时候,会占用一定的内存空间</span></span></div>
存储的是键值对信息
AOF
触发方式
需要将 appendonly 修改为 yes。
重启机制<br>
如果文件aof大于64mb fork一个新的进程来将我们的文件进行重启。
也就是说 AOF 文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件
开启子进程①、子进程进行 AOF 重写期间,服务器进程(父进程)可以继续处理其他命令。<br> ②、子进程带有父进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。
<span style="color: rgb(0, 0, 0); font-family: 微软雅黑, PTSans, Arial, sans-serif; font-size: 15px;">为了解决这个数据状态不一致的问题,Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区是在创建子进程后开始使用,当Redis服务器执行一个写命令之后,就会将这个写命令也发送到 AOF 重写缓冲区。当子进程完成 AOF 重写之后,就会给父进程发送一个信号,父进程接收此信号后,就会调用函数将 AOF 重写缓冲区的内容都写到新的 AOF 文件中。</span>
存储的是命令
优缺点
①、AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而已。<br><br> ②、AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的片断,使用 redis-check-aof 工具也很容易修正 AOF 文件。<br><br> ③、AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢复数据。
缺点:<br><br> ①、对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大。<br><br> ②、虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。但在 Redis 的负载较高时,RDB 比 AOF 具好更好的性能保证。<br><br> ③、RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一些 BUG,这些 BUG 在 RDB 没有存在。
选择
1.RDB恢复速度快量小
2.AOF存储的比较完整
Redis4.0以后两者结合的方式
redis事务
redis单条命令是原子性的,但是事务不保证原子性
一个事务的所有命令会被序列化、一次性、顺序性、排他性
过程
开启事务 multi
然后命令入队
执行事务 exec
取消事务 discard
特点
不保证ACID原则
编译型错误mredis事务都不会执行
运行时错误,除了错误行抛出异常,其余命令正常执行
redis乐观锁和悲观锁
乐观锁
任何时候都不会出现问题,所以不会上锁,更新数据判断一下是否有人修改过这个数据的version
悲观锁
什么时候都会出问题,无论何时都加锁。
Jedis是使用java来操作redis 的中间件,是官方推荐的java连接开发工具!使用java操作redis中间件!
发布/订阅模式
消息通信模式,微信、微博、关注系统
第一个:消息发送者
第二个:消息订阅者
redis-server维护一个字典,值是一个链表遍历后发给订阅者<br>使用场景:1.实时消息系统 2.事实聊天!3.订阅,关注系统<br>复杂的场景使用消息中间件。
主从复制一致性问题
读写分离!80%是读操作。
数据的复制是单向 的 只能由主节点到从节点。Master写为主,Slave读为主<br>一主二从
主从复制主要作用:
1.数据冗余:实现数据热备份,持久化之外的一种数据冗余方式
2.故障恢复,主节点出现问题,从节点提供服务实现故障恢复。
3.负载均衡:写数据连接master 读redis数据连接slave 分担服务器负载,大大提高redis服务器并发量。
4.哨兵和集群的基础
一般来说主从复制是必要的,怕结构上单点故障,容量超过20G就得配置集群
环境配置
修改配置文件:端口、pid名字、log名字、dump.rdb 名字
1。一主二从:<br>默认三台都是主机,一般情况下只用配置从机。
slaveof host post<br>真实是在配置文件配置;
主机负责写 从机只负责读文件。
支持全量同步和增量同步
全量同步发生在slave初始化阶段需要将Master上的所有数据都复制一份
增量同步Slave初始化后开始正常工作时主服务器发送的写操作同步到从服务器
手动配置的,重启恢复主机获取不了以前的数据<br>重新设置为从机 可以获取
<span style="color: rgb(34, 34, 34); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 15.5px; background-color: rgba(255, 255, 255, 0.9);">slave节点做复制的时候,不会阻塞master节点的正常工作</span>
<span style="color: rgb(34, 34, 34); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 15.5px; background-color: rgba(255, 255, 255, 0.9);">如果采用主从架构,必须开启master节点的持久化,不建议用slave节点作master节点的数据热备,因为如果一旦关掉master的持久化,可能在master宕机重启时数据是空的,然后一经复制,slave节点也会随之丢失</span>
2.链路式连接
如果主机宕机以后、可以使用slaveof no one自己变成主机。<br>主机修复了只能重新配置。
redis主从同步的策略
主从刚建立全量同步
同步结束后增量同步
如过slave需要任何时候可以发起全量同步
redis策略 首先进行增量同步不成功则进行全量同步
如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机。
哨兵模式
自动选取老大
哨兵模式是一种特殊的模式,哨兵是一个独立的进程、作为进程独立运行,原理通过哨兵发送命令,等待redis服务器响应,从而监控运行多个redis实例
一个哨兵监控多个redis主从系统
多哨兵模式、主服务器宕机后,哨兵1(任意)检测到这个结果、系统不会马上failover过程,仅仅哨兵1认为主服务器不可用,这个现象成为主观下线。然后其余哨兵来检测这个服务器是否可用,当数量达到一定值,哨兵之间进行一次投票,投票由任意一哨兵发起进行failover故障转移操作。切换成功后发布订阅模式,让各个哨兵把自己监控的从服务器切换成主机这个过程称为客观下线。
主观下线
当超过down-after-milliseconds指定时间后,如果被PING的数据库或节点仍然未回复,则哨兵认为其主观下线(subjectively down),主观下线表示从当前的哨兵进程看来,该节点已经下线。
客观下线
在主观下线后,如果该节点是主数据库,则哨兵会进一步判断是否需要对其进行故障恢复,哨兵发送SENTINEL is-master-down-by-addr 命令询问其他哨兵节点以了解他们是否也认为该主数据库主观下线,如果达到指定数量时,哨兵会认为其客观下线(objectively down),并选举领头的哨兵节点对主从系统发起故障恢复。这个指定数量就是前面配置的 quorum参数。
选举领头哨兵
当前哨兵虽然发现了主数据客观下线,需要故障恢复,但故障恢复需要由领头哨兵来完成。这样来保证同一时间只有一个哨兵来执行故障恢复,选举领头哨兵的过程使用了Raft算法,具体过程如下:<br><br>1.发现主数据库客观下线的哨兵节点(A)向每个哨兵节点发送命令,要求对象选择自己成为领头哨兵<br>2.如果目标哨兵节点没有选过其他人,则会同样将A设置为领头哨兵<br>3.如果A发现有超过半数且超过quorum参数值的哨兵节点同样选择自己成为领头哨兵,则A成功成为领头哨兵<br>4.当有多个哨兵节点同时参选领头哨兵,则会出现没有任何节点当选的可能,此时每个参选节点将等待一个随机事件重新发起参选请求进行下一轮选举,直到选举成功。
故障恢复
选出领头哨兵后,领头哨兵将会开始对主数据库进行故障恢复。
1.首先领头哨兵将从停止服务的主数据库的从数据库中挑选一个充当新的主数据库
1<br><br>所有在线的从数据库中,选择优先级最高的从数据库。优先级通过replica-priority参数设置<br><br>2<br><br>优先级相同,则复制的命令偏移量越大(复制越完整)越优先<br><br>3<br><br>如果以上都一样,则选择运行ID较小的从数据库
2.领头哨兵将向从数据库发送SLAVEOF NO ONE命令使其升格为主数据库,而后领头哨兵向其他从数据库发送 SLAVEOF命令来使其成为新主数据库的从数据库,最后一步则是更新内部的记录,将已经停止服务的旧的主数据库更新为新的主数据库的从数据库,使得当其恢复服务时自动以从数据库的身份继续服务
优缺点
优点
1.哨兵集群基于主从模式
2.主从模式可以切换,故障可以转移,系统整体可用性较高
3.主从模式的升级,手动-->自动更加健壮
缺点
1.redis难以在线扩容,集群数量到了上限,在线扩容就十分麻烦
2.全部配置很麻烦
redis常用的缓存读写策略
1.旁路缓存模式
写
先更新DB 然后直接删除缓存
问题1:在写数据的过程中,可以先删除 cache ,后更新 DB 么?
肯定是不行的,会造成数据库和缓存数据不一致问题
请求1先把cache中的A数据删除 -> 请求2从DB中读取数据->请求1再把DB中的A数据更新。
问题2:在写数据的过程中,先更新DB,后删除cache就没有问题了么?
<span style="color: rgb(77, 77, 77); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 16px; font-variant-ligatures: common-ligatures;">理论上来说还是可能会出现数据不一致性的问题,不过概率非常小,因为</span><span style="box-sizing: border-box; outline: 0px; font-weight: 700; overflow-wrap: break-word; color: rgb(77, 77, 77); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 16px; font-variant-ligatures: common-ligatures;">缓存的写入速度是比数据库的写入速度快很多</span><span style="color: rgb(77, 77, 77); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 16px; font-variant-ligatures: common-ligatures;">!</span>
请求1从DB读数据A->请求2写更新数据 A 到数据库并把删除cache中的A数据->请求1将数据A写入cache。
读
从cache中读取数据,读取到就直接返回
cache读取不到就去DB中读返回
再把数据放到cache中
缺陷
缺陷1:首次请求数据一定不在 cache 的问题<br><br>解决办法:可以将热点数据可以提前放入cache 中。
<span style="box-sizing: border-box; outline: 0px; font-weight: 700; overflow-wrap: break-word; color: rgb(77, 77, 77); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 16px; font-variant-ligatures: common-ligatures;">缺陷2:写操作比较频繁的话导致cache中的数据会被频繁被删除,这样会影响缓存命中率 。</span>
解决办法:<br><br>数据库和缓存数据强一致场景 :更新DB的时候同样更新cache,不过我们需要加一个锁/分布式锁来保证更新cache的时候不存在线程<br><br>安全问题。<br><br>可以短暂地允许数据库和缓存数据不一致的场景 :更新DB的时候同样更新cache,但是给缓存加一个比较短的过期时间,这样的话就<br><br>可以保证即使数据不一致的话影响也比较小。
2.读写穿透模式
中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 DB,从而减轻了应用程序的职责
写
先查 cache,cache 中不存在,直接更新 DB。<br>cache 中存在,则先更新 cache,然后 cache 服务自己更新 DB(同步更新 cache 和 DB)。
读
从 cache 中读取数据,读取到就直接返回 。<br>读取不到的话,先从 DB 加载,写入到 cache 后返回响应。
3.异步缓存写入
Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。
<p style="box-sizing: border-box; outline: 0px; margin-bottom: 16px; padding: 0px; font-size: 16px; color: rgb(77, 77, 77); line-height: 26px; overflow: auto hidden; overflow-wrap: break-word; font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-variant-ligatures: common-ligatures;">很明显,这种方式对数据一致性带来了更大的挑战,比如cache数据可能还没异步更新DB的话,cache服务可能就就挂掉了。</p><p style="box-sizing: border-box; outline: 0px; margin-bottom: 16px; padding: 0px; font-size: 16px; color: rgb(77, 77, 77); line-height: 26px; overflow: auto hidden; overflow-wrap: break-word; font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-variant-ligatures: common-ligatures;">这种策略在我们平时开发过程中也非常非常少见,但是不代表它的应用场景少,比如消息队列中消息的异步写入磁盘、MySQL 的 InnoDB Buffer Pool 机制都用到了这种策略。</p><p style="box-sizing: border-box; outline: 0px; margin-bottom: 16px; padding: 0px; font-size: 16px; color: rgb(77, 77, 77); line-height: 26px; overflow: auto hidden; overflow-wrap: break-word; font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-variant-ligatures: common-ligatures;">Write Behind Pattern 下 DB 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。</p>
redis和mysql 的区别
1.类型
mysql是关系型数据库,主要存放持久化数据 读取速度慢
redis是nosql数据库也是缓存数据库,数据在缓存中,读取较快,可以大大提高运行效率,断电即失
过期删除策略
1.过期字典
每当设置一个键的过期时间时,Redis就会从过期字典中检测是否存在,存在则获取过期时间,然后用过期时间和当前系统时间进行比对,比系统时间打则没有过期,反之过期。
expire key time 表示key的过期时间是time秒
删除方式
1.定时删除
设置过期时间,创建定时器,定时器过该时间后到来立即执行删除操作
对内存优化,用完就删了
对cpu不友好,过期键多的时候,删除会占cpu时间对服务器响应好吞吐量有影响‘’
2.惰性删除
设置过期时间不管他,等用的时候看是否过期了,过期就执行删除
对cpu友好
对内存不友好,可能造成内存泄露
3.定期删除
没隔一段时间对一些key检查,删除里面过期的key
优点:
可以限制删除操作执行的时长和频率来减少删除操作对cpu的影响
定期删除也能有效的释放过期键占用的内存
缺点:
难以确定删除操作的时长和频率
太频繁 和定时删除一样了
不少和惰性删除一样了
另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误。
Redis的过期删除策略就是:惰性删除和定期删除两种策略配合使用。
内存淘汰策略
通过过期删除策略,对于某些永远使用不到的键,并且多次定期删除也没选定到并删除,那么这些键同样会一直驻留在内存中,又或者在Redis中存入了大量的键,这些操作可能会导致Redis内存不够用,这时候就需要Redis的内存淘汰策略了
在配置文件redis.conf 中,可以通过参数 maxmemory <bytes> 来设定最大内存:
当现有内存大于 maxmemory 时,便会触发redis主动淘汰内存方式,通过设置 maxmemory-policy ,有如下几种淘汰方式:
内存淘汰方式
allkeys-lru 利用LRU算法移除任何key (删除的key包括设置过期时间和不设置过期时间的)。通常使用该方式
大key问题
大key指的是这个key存储的value过大
导致读写性能下降,删除是O(n)甚至阻塞服务。单个耗时过大的命令容易引起缓存雪崩或者Redis集群发送故障切换
解决方法:
1.可以拆分多个key然后mget获取值,多个key可以分散到不同的节点,这样就可以把流量分摊到不同的节点
2.可以把大value拆到一个hash中。用hget去获取值
3.本地缓存
<br>现在可以固定一个桶数量,比如 1w,<br>每次存取的时候,先在本地计算field的hash值,<br>对 1w取模,确定 field落在哪个 key 上:<br>
热key问题
突然有几十万的请求去访问redis上的某个特定key。那么,这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机。<br>那接下来这个key的请求,就会直接怼到你的数据库上,导致你的服务不可用。
(1)利用二级缓存<br>比如利用ehcache,或者一个HashMap都可以。在你发现热key以后,把热key加载到系统的JVM中。<br>针对这种热key请求,会直接从jvm中取,而不会走到redis层。<br>假设此时有十万个针对同一个key的请求过来,如果没有本地缓存,这十万个请求就直接怼到同一台redis上了。<br>现在假设,你的应用层有50台机器,OK,你也有jvm缓存了。这十万个请求平均分散开来,每个机器有2000个请求,会从JVM中取到value值,然后返回数据。避免了十万个请求怼到同一台redis上的情形。
(2)备份热key<br>这个方案也很简单。不要让key走到同一台redis上不就行了。我们把这个key,在多个redis上都存一份不就好了。接下来,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。<br>假设redis的集群数量为N
是做热点发现和本地缓存。
redis压测
mysql
数据库的三大范式
1.数据库每个列都是原子级别的不可再分
2.建立在1的基础上,非主键属性依赖于主键
完全依赖主键
3.在前两个范式的基础上,非主键属性只依赖于主键
不能有传递关系:比如学号、名字、校区、校区编号、校区电话这是后两个依赖于校区了存在传递关系
如何设计数据库:
1.需求分析,并画出E-R模型
2.根据模型设计数据库
3.写代码测试和部署
设计原则
关联表不超过3张表
更多考虑商业化需求和目标(成本以及用户体验)性能更加重要
在规范性能满足的情况下适当的考虑一下规范性
故意给某些字段增加冗余字段(将多表查询改为单表查询)<br>
故意增加计算列将大数据量降低为小数据量查询:索引
超键
能唯一表示一条数据的属性或属性集, 比如学号
一个属性可以作为超建,多个属性组合在一起也可以作为一个超键
候选键
最小超键,没有冗余元素的超键
主键
对数据对象的唯一和完整的标识,一个数据表只能有一个主键,且不能为null
Mysql整体架构
Server层(存储过程、触发器、视图和内置函数都在这一层)
连接器
负责和客户端建立间接,获取用户权限以及维持和管理连接
show processlist查看连接状态
默认连接8小时,用户连接后即使管理员改变连接用户权限也不会影响已经连接的用户
长连接
优点:在连接时间客户端一直使用同一连接,避免多次创建连接的资源消耗
缺点:使用的内存被连接对象管理,长时间没释放会导致系统内存溢出被系统kill
应该定期断开大连接或者执行大查询后断开连接
基于TCP协议
查询缓存
当查询时先从查询缓存中查询,是否执行过没有再走分析器
只适合静态表,表更新缓存会被清空,Mysql8.0被废除
因为命中率太低所以被废除
分析器
词法分析 识别select 表名等等
语法分析 是否符合mysql语法
优化器
确定索引的使用,join表的连接顺寻等
执行器
在具体执行语句前 先进行权限的检查,如果设置了慢查询会在对应日志中看到rows_examined来表示扫描的行数
一些场景下(索引) 执行器调用一次引擎扫描多行 所以引擎扫描的行数和rows_examined并不完全相同
存储引擎层(负责数据的存储和提取)
InnoDB
内存管理
使用LRU结构来管理页的 页大小默认16kb
将LRU分为两部分 热点数据放在new(3/8)列表,其余放在old列表通过midpoint分割
改进LRU的原因
Innodb引擎的四大特性
插入缓存
提升性能 change buffering是insert buffer的加强,插入缓存只对insert有效
change buffering对insert、delete、update3都有效
使用条件
非聚簇索引
非唯一索引
定义:只对非聚簇索引(非唯一)的插入、删除、修改有效,对于每一次的插入不是写到索引页中,而是先判断插入的非聚簇索引页是否再缓冲池中,如果在则直接插入,如果不在则先放到insert buffer中,再按一定的频率进行合并操作,再写回disk。可以把多个插入合并到一个操作,目的减少随机IO带来的性能损耗
insert buffer
只对insert有效
change buffer
对insert、delete、update、purge(后台执行的物理删除)都有效
参数
inserts
all
nono
deletes
changes
缓存delete+insert 实现update
purge
缓存后台执行的物理删除
二次写(double write)
如果操作系统或者数据库进程在数据页写磁盘的过程崩溃可以从二次写缓冲中找到数据页的备份来执行crash恢复
自适应哈希
自动监控并且为频繁访问的二级缓存设置哈希值
只适合等值查询
预读
线性预读
将下一个extent提前读取到buffer pool中
InnoDB以64个page为一个extent
随机预读
将下一个extent剩余的page提前读取到buffer pool中
5.5废弃 可以启用
InnoDB的索引组织方式
存储引擎以页16kb为基本单位存储
InnoDB和MyISAM的对比
Innodb支持行级锁、外键、事务、奔溃后的安全恢复,MVCC、不支持全文索引
MyISAM强调的是性能,每次查询都具有原子性,执行速度快与InnoDB
MyISAM适合SELECT密集型的表,而InnoDB适合INSERT和UPDATE密集型的表
MyISAM
Memory
日志模块
Server对应binlog
row
仅记录那条记录被修改,纪录单元为每一行的改动日志量很大
mixed
普通操作使用statament 无法使用时使用row
statement
每一条修改数据的sql都会记录,不需要记录每一行的变化减少了日质量
Innodb 对应redo log(重做日志)
WAL(Write-Ahead-Logging技术)
由于数据直接写入磁盘带来的IO开销,所以先写入内存然后等系统空闲再写入,防止内存中的数据意外消失不能持久化,加入了redo loog日志来记录数据的变化。
除了效率还使得mysql具有crash-safe的能力 发生异常不会丢失数据
一组四个文件,每个文件1GB,更新时对四个文件循环写入
保证持久性
奔溃恢复
子主题
undo log(回滚日志)
1.保证原子性
2.场景:事物回滚
事务id(唯一、递增)
生成时间
事务对表中的记录做变动的时候
生成策略
服务器在内存维护一个全局变量,且事务id分配之后自动+1
每当变量是256的倍数时,会将变量值刷新到系统表空间页为5的页面中
日志格式
insert对应的undo日志
delete
update
分库分表
水平分库
水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。(垂直分库已经扛不住了)
解决了单库大数据,高并发的性能瓶颈。<br>提高了系统的稳定性及可用性。
水平分表
水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。
库内分表
优化单一表数据量过大而产生的性能问题<br><br>避免IO争抢并减少锁表的几率<br><br>库内的水平分表,解决了单一表数据量过大的问题,分出来的小表中只包含一部分数据,从而使得单个表的数据量变小,提高检索性能。
垂直分库
垂直分库是指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念是专库专用。
解决业务层面的耦合,业务清晰<br><br>能对不同业务的数据进行分级管理、维护、监控、扩展等<br><br>高并发场景下,垂直分库一定程度的提升IO、数据库连接数、降低单机硬件资源的瓶颈<br><br>垂直分库通过将表按业务分类,然后分布在不同数据库,并且可以将这些数据库部署在不同服务器上,从而达到多个服务器共同分摊压力的效果,但是依然没有解决单表数据量过大的问题。
垂直分表
将一个表安照字段分成多表,每个表存储其中一部分字段
1.为了避免IO争抢并减少锁表的几率,查看详情的用户与商品信息浏览互不影响<br><br>2.充分发挥热门数据的操作效率,商品信息的操作的高效率不会被商品描述的低效率所拖累。
原则
把不常用的字段单独放在一张表;<br>把text,blob等大字段拆分出来放在附表中;<br>经常组合查询的列放在一张表中;
在数据量及访问压力不是特别大的情况,首先考虑缓存、读写分离、索引技术等方案。若数据量极大,且持续增长,再考虑水平分库水平分表方案。
分库分表方案:按照hash取模和range的方案
1.hash取模方案
按key水平分表
假设4000W的订单量可以分成四个表按主键取模确定(提前预测这几年的总的订单数)
优点:数据可以均匀的放到四个表中,不会有热点问题
缺点:将来的数据迁移和扩容很难
2.range的方案
不需要数据迁移,但有热点问题
id范围放到不同的表
优点:利于扩容
缺点:热点问题
热点的含义:热点的意思就是对订单进行操作集中到1个表中,其他表的操作很少。<br><br>订单有个特点就是时间属性,一般用户操作订单数据,都会集中到这段时间产生的订单。如果这段时间产生的订单 都在同一张订单表中,那就会形成热点,那张表的压力会比较大。
主从复制
主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库。
作用
1、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,物理服务器增加,负荷增加。<br><br>2、读写分离,使数据库能支撑更大的并发。主从只负责各自的写和读,极大程度的缓解X锁和S锁争用。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。<br><br>3、做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
S锁 共享锁,互相不阻塞又称为读锁
事务T对数据对象A加锁共享锁,事务T可以读A但是不能修改A,其他事务只能加读锁不能加写锁,知道A释放读锁其他事务才能修改A
X锁 排他锁 写锁
事务A对对象加锁后其他事务不能对A加任何锁知道A释放自己的锁
原理
1.数据库有bin-log二进制文件记录了所有sql
2.把主数据库的bin-log文件的sqk语句复制过去
3.在从数据的relay-log重做日志文件中再执行一次这些sql即可
需要三个线程来做
1。binlog输出线程
每次有从库连接上主库时,主库会创建一个线程然后发送binlog内容到从库
2.在从库 复制开始时创建两个线程
1.从库I/O线程
从库I/O线程读取主库的binlog输出线程发送的更新并拷贝这些更新到本地文件,包括了relay log文件
2从库sql线程
读取io写到relay log的更新事件并执行
索引
b+树结构
索引为什么用b+树而不用b树或者红黑树实现
b树或者红黑树都太高了
b树查找不稳定,红黑树增加删除费时间
B树有可能在非叶子节点完成搜索,越靠近根节点的记录查询时间越短,等价于做一次二分查找
B+树 顺序检索比较明显,随机检索时任意查询的效率相同都需要到根节点
没有利用局部性原理
b+树磁盘读写代价更低
非叶子节点只存储索引,一次读入内存中可以查询的关键字也多,降低IO次数
B+树可以遍历一遍叶子节点完成整个树的遍历,b树不行
B+树索引和Hash索引的区别
无法范围查找,无法排序,不支持最左匹配原则,有大量重复键的查询效率很低
哈希索引只适合等值查询,不适合范围查询
哈希索引没法利用索引排序
哈希索引不支持多列联合索引的最左匹配规则
如果重复大量重复键,效率很低—>存在哈希碰撞的问题
为什么MongoDB用B树索引
MongoDB做单一查询比较多,数据遍历操作较少,所有用B树索引
红黑树和AVL树比较
红黑树性能高,其利用了“缓存”,红黑树相当于2-3树,2节点等价于平二叉树的节点,3节点本质上是非平衡缓存,当数据随机性强时,2节点向3节点转化可以吸收一些非平衡性,减少旋转次数快速完成平衡
计算多少数据的树高/三层可以放多少数据
innodb最小单位是页16k 一个页的key假设bigint占8字节,指向下个页的指针为6byte,16/(8+6)=1170个对象(关键字-->页指针)
假设数据记录大小为1kb-->叶子结点(页)可以存16条数据
高度为2的B+树:1170*16=19720,约2W条数据记录
高度为3的B+树:1170*1170*16=21902400,约2千万条数据记录
如果是B树,2000万数据的高度为log16 2000万远远大于3层
索引失效场景
1.没有将查询的数据用引号括起时不会使用索引
‘’
2.未使用建立索引的列来查询
3.通配符在前时索引失效%
4.在查询条件中使用or
需要将or的每个列都加上索引才会使索引生效
1.对OR语句求并集,如查询SELECT * FROM TB1 WHERE c1="xxx" OR c2=""xxx"时,如果c1和c2列上分别有索引,可以按照c1和c2条件进行查询,再将查询结果合并(union)操作,得到最终结果<br><br>2.对AND语句求交集,如查询SELECT * FROM TB1 WHERE c1="xxx" AND c2=""xxx"时,如果c1和c2列上分别有索引,可以按照c1和c2条件进行查询,再将查询结果取交集(intersect)操作,得到最终结果<br><br>3.对AND和OR组合语句求结果
5.索引列进行函数运算
6.联合索引ABC问题,不符合最左匹配原则
index(a,b,c)可以支持a|a,b|a,b,c不支持b,c,b |c
如果没有主键,还会不会有聚簇索引?
有
mysql会找到一个值都不相同的列为它加上unique index
如果没有唯一值的特征列,innodb会自动生成一个不可见的row_ID的列,名为gen_clust_index的聚簇索引该列是一个6字节的自增数值,随着插入而自增
为什么主键设置为自增?
自增才能范围查找
递增下,新增节点之间往双链表后放,不自增会因插入新节点造成大量页分裂
聚簇索引和非聚簇索引
聚簇索引
InnoDB聚簇索引的叶子结点存储行记录,因此,InnoDB必须要有且只有一个聚簇索引
1.如果有PK则PK是聚簇索引
2.如果没有PK则第一个not null unique是聚簇索引
3.否则会创建一个隐藏的row-id作为聚簇索引
索引pk查询很快,直接定位行记录
普通索引
普通索引的叶子节点存储主键值
MyISAM的所有叶子结点存储记录指针(行记录头指针)
t(id pK,name key,sex,flag)
id是聚簇索引
name是普通索引
普通索引没法直接定位行记录,那么普通索引的查询过程是怎么样的呢?
回表查询
1.通过普通索引定位到id(主键聚簇索引)
2.在通过聚簇索引定位到行记录
索引覆盖?
只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。
如何实现索引覆盖?
常见方法是:将被查询的字段,建立到联合索引去
哪些场景可以利用索引覆盖来优化sql?
1.全表count查询优化
2.列查询回表优化
升级为联合索引
3.分页查询
select id,name,sex ... order by name limit 500,100;
将单列索引(name)升级为联合索引(name, sex),也可以避免回表。
举例:如t表
select id,name from user where name='shenjian';
命中name索引 name索引叶子节点存储主键id 通过索引树即可获取结果不需要回表查询
<em style="box-sizing: border-box; color: rgb(64, 64, 64); font-family: -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px;">select id,name</em><em style="box-sizing: border-box; color: rgb(64, 64, 64); font-family: -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px;">,sex</em><span style="color: rgb(64, 64, 64); font-family: -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px;">* from user where name='shenjian';*</span>
没命中sex,需要再次通过id扫描聚簇索引才行,不符合索引覆盖效率低
<div><p><em>create table user (</em></p><br><p><em>id int primary key,</em></p><br><p><em>name varchar(20),</em></p><br><p><em>sex varchar(5),</em></p><br><p><em>index(name, sex)</em></p><br><p><em>)engine=innodb;</em></p></div><br>
name和sex升级为联合索引即可保证索引覆盖
联合索引?(创建多个单列索引好还是建立一个联合索引好?)
多个单列索引在多条件查询时优化器会选择最优索引策略,可能只用一个索引,也可能将多个索引全用上! 但多个单列索引底层会建立多个B+索引树,比较占用空间,也会浪费一定搜索效率,故如果只有多条件联合查询时最好建联合索引!
联合索引本质:<br><br>当创建**(a,b,c)联合索引时,相当于创建了(a)单列索引**,(a,b)联合索引以及**(a,b,c)联合索引**<br>想要索引生效的话,只能使用 a和a,b和a,b,c三种组合;当然,我们上面测试过,a,c组合也可以,但实际上只用到了a的索引,c并没有用到!
用到联合索引的
a
a and b
b and a
a and c
实际上只用了a
a and b and c
没用的联合索引的
b
c
a or b
b无索引全表扫描
b and c
abc分别创建单列索引
a and b and c
只用到a mysql优化器会选择一个最有效的或几个
b and c
只用到b
a or b
ab都用到了
索引建立的原则
1、需要加索引的字段,要在where条件中<br>2、数据量少的字段不需要加索引;因为建索引有一定开销,如果数据量小则没必要建索引(速度反而慢)<br>3、避免在where子句中使用or来连接条件,因为如果俩个字段中有一个没有索引的话,引擎会放弃索引而产生全表扫描<br>4、联合索引比对每个列分别建索引更有优势,因为索引建立得越多就越占磁盘空间,在更新数据的时候速度会更慢。另外建立多列索引时,顺序也是需要注意的,应该将严格的索引放在前面,这样筛选的力度会更大,效率更高。
where查询是从左到右的顺序,所有筛选力度大的放前面?
错误
mysql优化器会根据索引自己选择,和顺序无关
建立索引的注意点?
给具有唯一值的列建唯一性索引
给经常查询 排序 分组的列建索引
对于过长的字段尽量使用前缀索引
尽量使用最左前缀匹配原则
mysql会一直向右匹配遇到范围查询><between like就停止匹配
a = 1 and b = 2 and c>3 and d = 4如果建立 a,b,c,d顺序的索引 d是用不到索引的
d和c的顺序换一个就可以了,其余位置可以任意换只要c在最后
删除不用的索引
mysql调优
慢查询
慢查询优化步骤
0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
1.where条件单表查,锁定最小返回记录表。
这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高,区分用count(distinct col)/count(*)表示字段不重复的比例
2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
<span style="color: rgb(51, 51, 51); font-family: "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans-serif; font-size: 15px; background-color: rgb(253, 253, 253);">3.order by limit 形式的sql语句让排序的表优先查</span>
<span style="color: rgb(51, 51, 51); font-family: "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans-serif; font-size: 15px; background-color: rgb(253, 253, 253);">4.了解业务方使用场景</span>
<span style="color: rgb(51, 51, 51); font-family: "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans-serif; font-size: 15px; background-color: rgb(253, 253, 253);">5.加索引时参照建索引的几大原则</span>
<span style="color: rgb(51, 51, 51); font-family: "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans-serif; font-size: 15px; background-color: rgb(253, 253, 253);">6.观察结果,不符合预期继续从0分析</span>
慢查询的相关参数
show variables like '%slow_query_log%'开启慢查询(默认是关闭)
开启set global slow_query_log=1 只对当前数据库生效,重启失效永久有效就得修改my.cnf文件
查看日志 cat 。。\slow.log
查询当前表有多少条慢sql
截取日志:<br><br><br><br>sed -n '/# Time: 161102 1:03:39/,/end/p' slow-queries.log > show.log<br><br><br><br>然后用mysqldumpshow命令取出耗时最长的前10条慢SQL进行分析。
主要功能是, 统计不同慢sql的<br><br>出现次数(Count), <br><br>执行最长时间(Time), <br><br>累计总耗费时间(Time), <br><br>等待锁的时间(Lock), <br><br>发送给客户端的行总数(Rows), <br><br>扫描的行总数(Rows),
过程
1.开启慢查询
1.修改配置文件
2.通过MySQL数据库开启慢查询
超过设定时间的sql记录了
2.执行慢查询sql
3.查看慢查询数量
show global status like '%slow%';
4.分析慢查询日志
自带了mysqldumpslow工具用来分析slow query日志
慢查询日志量很大,对时间排序top几,优化前面几条最慢的慢查询
sql语句+时间+锁时间
使用慢查询的分析工具
mysqldumpslow.pl(mysql/bin/)
需要装插件执行perl pl类似java -jar
拿到sql语句
sql优化方式面试题
1.硬件优化:机械硬盘-->固态硬盘
2.mysql服务器优化
windows-->linux 更加稳定
3.sql本身优化
子查询+关联子查询==》关联查询
4.反范式优化
添加冗余字段 空间换时间
5.索引优化
5.优化
1.where条件增加索引<br><br>2.联合索引,最左匹配原则
字符类型加‘ ’才使用索引
加limit优化
SELECT * FROM member WHERE `username`='www.kubiji.cn';
SELECT * FROM member WHERE `username`='www.kubiji.cn' LIMIT 1;
经作者以100万条数据做测试得出结果:<br><br>第一条查询语句耗时 0.56 s<br><br>第二条使用了 LIMIT 1 后的查询语句耗时 0.28 s<br><br>由上可见在数据量很大的情况下,适当的使用LIMIT 1对查询操作的优化效果还是相当明显的。<br><br>注意:如果以上表字段中username被设置为了索引的话,这个时候使用LIMIT 1在查询速度上没有明显的效果。
mysql大表优化查询效率
对索引进行优化
1.不要加过多索引,给where和order by加
2.避免在where子句中用NULL,会造成全表扫描
3.字符字段最好不做主键、只加前缀索引
4.使用覆盖索引(例如通过添加联合索引实现覆盖索引)
join优化
驱动表
那什么是驱动表呢?<br>指定了联接条件时,满足查询条件的记录行数少的表为驱动表<br>未指定联接条件时,行数少的表为驱动表(Important!)<br>如果你搞不清楚该让谁做驱动表、谁 join 谁,就别指定谁 left/right join 谁了,请交给 MySQL优化器 运行时决定吧
复杂的sql怎么识别驱动表呢?<br>按经验谈,使用EXPLAIN, 第一行出现的表就是驱动表。
关联查询
MySQL 表关联的算法是 Nest Loop Join,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。<br><br>//例: user表10000条数据,class表20条数据<br>select * from user u left join class c u.userid=c.userid<br>上面sql的后果就是需要用user表循环10000次才能查询出来,而如果用class表驱动user表则只需要循环20次就能查询出来。<br>
小表驱动大表优化NestdedLoop循环的次数
<span style="box-sizing: border-box; outline: 0px; font-weight: 700; overflow-wrap: break-word; color: rgb(77, 77, 77); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun; font-size: 16px;">对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序!</span>
in和exists的区别
适合B表比A表小的情况,会从缓存中取数据
当B表的数据比A表的数据大时适合使用EXISTS()查询,因为它不用遍历B操作,只执行一次查询就OK了
order by
1.利用有序索引获取有序数据
explain 显示Using index
Using index:所需的数据在index中即可全部获得,无需再到表中取数据,如果是主键索引,数据直接从表中得到
主键索引,B+树的叶子结点相互之向有序的并且数据也在上面,所以order by主键索引时直接取出有序数据即可,效率很高
2.文件排序
explain 显示Using filesort
using filesort:当我们的query中包含order by操作,而且无法利用索引完成排序操作时,Mysql Query Optimizer不得不选择相应的排序算法来实现。
双路排序
先按排序字段和定外到行数据的行指针信息排序,然后拿到每一行数据再组装返回结果
还需要两次IO,一次是取排序字段(id,col2)到sort buffer中,第二次是通过上面取出的主键id再来取其他所需要返回列(col1,col2,col3)
使用了快排和归并
单路排序
一次把行数据全读进来 空间换时间
优先队列排序
堆排实现
需要回表查询的情况会导致using filelist,这将会直接到主键索引处扫表再进行文件排序
获取的数据在内存中进行排序,排序区是线程读享的 同一时刻mysql可能存在多个 sort buffer内存区域
<div>group by</div>
前提是排序基于order by 的,只是在排序之后有个分组操作
DINSTINCT去重又是基于group by的,所以说group by与dinstinct的优化都是基于order by的优化,也就是尽量优化索引,减少排序时间。
事务
事务特性
ACID
原子性atomicity
要么全成功要么全失败
事务的原子性是通过undolog来实现的。<br>
一致性consistency
事务结果必须是数据库从一致性状态到另一个一致性状态
子主题
隔离性isolation
一个事物的执行不受其他事务干扰
MVCC保证隔离性
四种隔离级别和底层实现
持久性durability
事务一旦提交,它对数据库的改变就是永久性的
redolog
隔离级别
已读未提交
读取到未提取的事务
可能会导致 脏读幻读不可重复度
已读已提交
读取已经提交的事务
可能会导致幻读和不可重复读
可重复读mysql默认
事务A读取数据时,事务B更改数据并提交,A未提交事务A读取的还是之前一样的数据
可能会导致幻读
串行化
最高的隔离级别可以解决幻读的问题,每个读的行都加共享锁-->会导致大量的超时现象和锁竞争
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。<br>不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。<br>幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。<br>
底层实现
MVCC
简单讲,如果没有MVCC,当想要读取的数据被其他事务用排它锁锁住时,只能互斥等待;而这时MVCC可以通过提供历史版本从而实现读取被锁的数据的历史版本,从而避免了互斥等待。
InnoDB采用的MVCC实现方式是:在需要时,通过undo日志构造出历史版本。
锁
READ UNCOMMITTED隔离级别下, 读不会加任何锁。而写会加排他锁,并到事务结束之后释放。
READ COMMITTED写数据时,使用排它锁, 读取数据不加锁而是使用了MVCC机制。<br><br>因此,在读已提交的级别下,都会通过MVCC获取当前数据的最新快照,不加任何锁,也无视任何锁(因为历史数据是构造出来的,身上不可能有锁)。<br><br>但是,该级别下还是遗留了不可重复读和幻读问题:
REPEATABLE READ
READ COMMITTED级别不同的是MVCC版本的生成时机,即:一次事务中只在第一次select时生成版本,后续的查询都是在这个版本上进行,从而实现了可重复读。
<span style="color: rgb(77, 77, 77); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif, SimHei, SimSun; font-size: 16px;">SERIALISABLE</span>
会自动将所有普通select转化为select ... lock in share mode执行,即针对同一数据的所有读写都变成互斥的了,可靠性大大提高,并发性大大降低。
并发场景
读-读不存在线程安全问题
读-写有线程安全问题,可能会造成事务隔离性问题
并发带来的问题
脏读
读取别的事务未提交的数据
事务一读<br><br>事务二写(未提交)<br><br>事务二读(脏数据)<br><br>事务二回滚
幻读
两次读到的行数不一致
gap锁解决幻读问题
事务一读<br><br>事务二写(新增已提交)<br><br>事务二读(数据不一致,幻读)
不可重复读
读一次,有其他事务更新了数据又读了一次,数据不一致
事务一读<br><br>事务二写(更新已提交)<br><br>事务二读(数据不一致,不可重复读)
读肯定没加锁,不然写肯定被阻塞,所有写加了排他锁
写-写 有线程安全问题,可能存在丢失更新问题
丢失更新
后一个事务的更新覆盖了前一个事务更新的情况
第一类:回滚覆盖
某个事务回滚操作,参与回滚的旧数据将其他事务的提交结果覆盖 隔离级别解决这个问题不需要我们关心
第二类:更新覆盖
多个事务同时更新一行数据,导致一个事务更新被另外一个事务覆盖
乐观锁和悲观锁
阻塞,肯定加了排他锁
MVCC
什么是MVCC?
多版本并发控制协议,只存在innodb下,解决读写冲突,通过版本号控制实现对不同的事务的隔离,好处是读不加锁,读写不冲突,非阻塞并发读,对应高并发事务,MVCC比单纯的加锁更高效
MVCC只在读已提交和可重复读两个隔离级别下工作;MVCC可以使用乐观锁和悲观锁来实现
版本链
在Mysql的InnoDB引擎中就是指在已提交读(READ COMMITTD)和可重复读(REPEATABLE READ)这两种隔离级别下的事务对于SELECT操作会访问版本链中的记录的过程。
三个隐藏列
row_id
trx_id
存储每次对某个聚簇索引记录进行修改的时候的事务id
roll_pointer
旧版本串联成单链表,版本头结点是当前记录最新值
两种隔离级别不同的就是可重复读ReadView还是第一次select时候生成的
ReadView
READ COMMITTED —— 每次读取数据前都生成一个ReadView
REPEATABLE READ —— 在第一次读取数据时生成一个ReadView
如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。<br>如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。<br>如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。<br>如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
问题?
判断版本链哪些版本对当前实务可见
版本链访问记录判断逻辑
实现
Read Commited
每一次进行普通的select操作前都会生成一个ReadView
Repeated Read
只在第一次进行普通select操作前生成一个ReadView 之后的select可重复使用,可重复读
undo
insert undo
事务提交释放
update undo
需要支持MVCC,不能立即删除
delete mark
相当于只是对记录打上了一个删除标志位,这主要就是为MVCC服务的。不然如果更新主键的记录直接删了,就没办法出现在版本链中,就没办法通过MVCC来找到对应的记录。
explain
用sql语句前面查看sql语句的执行计划,用explain这个命令来查看一个些sql语句的执行计划
id:选择标识符
优先级越高值越大
<span style="color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">select_type:表示查询的类型。</span>
(1) SIMPLE(简单SELECT,不使用UNION或子查询等)<br><br>(2) PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)<br><br>(3) UNION(UNION中的第二个或后面的SELECT语句)<br><br>(4) DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)<br><br>(5) UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select)<br><br>(6) SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询)<br><br>(7) DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询)<br><br>(8) DERIVED(派生表的SELECT, FROM子句的子查询)<br><br>(9) UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)
table:输出结果集的表
<span style="color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">partitions:匹配的分区</span>
type:表示表的连接类型
常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)<br><br>ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行<br><br>index: Full Index Scan,index与ALL区别为index类型只遍历索引树<br><br>range:只检索给定范围的行,使用一个索引来选择行<br><br>ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值<br><br>eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件<br><br>const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system<br><br>NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
possible_keys:表示查询时,可能使用的索引
key:表示实际使用的索引
<span style="color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">key_len:索引字段的长度</span>
ref:列与索引的比较
<span style="color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">rows:扫描出的行数(估算的行数)</span>
估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
优化sql语句都是在优化row
<span style="color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">filtered:按表条件过滤的行百分比</span>
Extra:执行情况的描述和说明
using where
使用了where
using index
使用了索引覆盖
using temporay
表示查询过程中产生的临时表用于保存中间结果
在对不是索引的字段使用group by的时候会出现临时表,group by的实质是先排序后分组
using filesourt
通常出现在order by,试图对一个不是索引的字段排序时,mysql会为其字段进行排序称为"文件排序"
InnoDB锁
1.加锁机制
乐观锁
不会上锁 CAS+版本号或者时间戳
悲观锁
每次开启事务拿数据都加锁,这样别人想拿这个数据就block直到它拿到锁
事务提交回滚再加锁
2.按粒度分
表锁
对当前表加 MYISAM支持表锁
粒度大,开销小,不会发生死锁,并发度低,发送锁冲突概率高
页锁
处于行锁也表锁直接
会发生死锁并发度一般
行锁
对当前操作行加锁
力度小,开销大,会发生死锁,并发度高,锁冲突概率低
3.按兼容性分
共享锁(读锁)
读数据时任何事务不得修改数据
用法:lock in share mode
排他锁(写锁)
修改数据时,任何事物不能读取和写锁
用法:for update
4.锁模式
Recored lock
单个行记录上的锁
Gap Lock(间隙锁)
间隙锁,锁一个区间
防止幻读
Next-Key Lock
记录锁+间隙锁 锁指定行记录+区间
当前读和快照读
当前读
加锁读,读取记录的最新版本,保证其他并发事务不能修改当前记录
快照读
即不加锁读,读取记录的快照版本而非最新版本,通过MVCC实现;
等值查询的锁算法介绍
主键(聚簇索引)
对聚簇索引记录+recoed Lock
唯一索引
对辅助索引+Record Lock
对聚簇索引记录+Record Lock
普通索引
对相关辅助索引 + NEXT-KEY Lock
对聚簇索引——Record Lock
不使用索引
对聚簇索引全表+Next-key lock
主键生成策略
分布式ID生成系统的需求
全局唯一,不能重复
递增(可选)
信息安全,非连续ID,避免恶意用户发现id规则 猜出下一个id或者猜出业务总量
高可用,不能故障
高QPS性能不能太差导致线程阻塞
时延要低
1.mysql主键自增
auto_increment
优点:
代码简单方便、性能可以接受
<div>数字ID天然排序,对分页或排序结果有帮助</div>
缺点
不同数据库语法和实现不同,数据库迁移或多数据库版本支持需要处理
依赖单机的读写性能 高并发下性能不太好
在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生产。有单点故障的风险
难以扩展
多个系统合并相当复杂
分表分库无法依靠aut_increment属性来标识一条记录
2.UUID
优点
简单代码好些
生产ID性能非常好
全球唯一,预见数据迁移数据合并数据库变更等情况,性能很不错
缺点
没有排序 无法保证趋势递增
字符串存储查询效率低
传输数据量大
存储空间大,存储量问题
不可读
3.redis固定步长生成
4.Twitter的雪花算法(不太了解)
5.Zookeeper生成分布式唯一ID
方案一:持久顺序节点实现
方案二:节点版本号
如何保证缓存一致性
1.先更新数据库再更新缓存
两个线程线程A先改了 然后B改了 B改后先更新了缓存 A才更新了缓存造成缓存数据库不一致
2.先更新缓存再更新数据库
和上面一样 会不一致
3.先删除缓存再更新数据库
1)请求A更新值,先把缓存中的值删除,但还没有来得及更新数据库<br>2)此时请求B过来查询此值,发现缓存中不存在,就到数据库中查询<br>3)请求B在数据库中获取到值,在把值设置到缓存中。<br>4)请求A这时才更新数据库里面的值为99
缓存不一致
4.先更新数据库再删除缓存(最优)
1)请求A发起查询请求,直接到数据库查询到100,但还没有来得及去设置缓存<br>2)请求B更新值,先更新数据库,在删除缓存<br>3)请求A这时才设置缓存为100<br>这种情况发生的不一致,是因为缓存突然失效了。而且还要保证请求B更新操作 比 请求A的查询操作还要快;才会导致不一致。这种情况概率会很少。一般要求不高的项目可以采用此方式(推荐)。<br>
SQL语句练习
1.执行顺序
一、sql执行顺序 <br>from <br>join <br>on <br>where <br>group by(开始使用select中的别名,后面的语句中都可以使用)<br> avg,sum.... <br>having <br>select <br>distinct <br>order by<br>limit
1、 on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。<br>2、where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。<br><br>
对于JOIN参与的表的关联操作,如果需要不满足连接条件的行也在我们的查询范围内的话,我们就必需把连接条件放在ON后面,而不能放在WHERE后面,如果我们把连接条件放在了WHERE后面,那幺所有的LEFT,RIGHT,等这些操作将不起任何作用,对于这种情况,它的效果就完全等同于INNER连接。对于那些不影响选择行的条件,放在ON或者WHERE后面就可以。<br>记住:所有的连接条件都必需要放在ON后面,不然前面的所有LEFT,和RIGHT关联将作为摆设,而不起任何作用。
今天看到:"SELECT * FROM strName WHERE 1 = 0";<br>不理解为什么有1=0?<br><br>查询得出答案:<br><br>该select语句主要用于读取表的结构而不考虑表中的数据,这样节省了内存,因为可以不用保存结果集。<br><br>另外,这个用在什么地方呢?主要用于创建一个新表,而新表的结构与查询的表的结构是一样的。如下SQL语句:
in 小 exists后大
SQL1 查找最晚入职员工的所有信息
limit y 分句表示: 读取 y 条数据<br><br>limit x, y 分句表示: 跳过 x 条数据,读取 y 条数据<br><br>limit y offset x 分句表示: 跳过 x 条数据,读取 y 条数据
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL2</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 查找入职员工时间排名倒数第三的员工所有信息</span>
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL3</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 查找当前薪水详情以及部门编号dept_no</span>
1.inner join,在两张表进行连接查询时,只保留两张表中完全匹配的结果集。
2.left join,在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录。<br>
3.right join,在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录。
<h3 id="4full-join在两张表进行连接查询时返回左表和右表中所有没有匹配的行" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; list-style-type: none; list-style-image: none; font-size: 16px; line-height: 1.5; font-family: Tahoma, Arial, Helvetica, sans-serif;">4.full join,在两张表进行连接查询时,返回左表和右表中所有没有匹配的行。(所有数据)</h3>
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL4</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 查找所有已经分配部门的员工的last_name和first_name以</span>
交集
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL5</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 查找所有员工的last_name和first_name以及对应部门编号</span>
SQL7 查找薪水记录超过15次的员工号emp_no以及其对应的记录
GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组
在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与聚合函数一起使用。<br><br>HAVING 子句可以让我们筛选分组后的各组数据。
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL8</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 找出所有员工当前薪水salary情况</span>
每个唯一显示去重distinct
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL10</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 获取所有非manager的员工emp_no</span>
not in +嵌套select去除一个表中包含另外有一个表的数据
SQL11 获取所有员工当前的manager
<span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;">12.获取每个部门中当前员工薪水最高的相关信息</span>
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL15</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 查找employees表emp_no与last_name的员工信息</span>
<span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;">16.统计出当前各个title类型对应的员工当前薪水对应的平均工资</span>
<span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;">17.获取当前薪水第二多的员工的emp_no以及其对应的薪水sal</span>
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL19</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 查找所有员工的last_name和first_name以及对应的dept_nam</span>
<span data-v-79f29a4f="" class="mr-1" style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap; margin-right: 5px !important;">SQL22</span><span style="color: rgb(51, 51, 51); font-family: system, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "PingFang SC", "Segoe UI", "Microsoft YaHei", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif; font-size: 18px; font-weight: 700; white-space: nowrap;"> 统计各个部门的工资记录数</span>
SQL24 获取所有非manager员工当前的薪水情况
SQL25 获取员工其当前的薪水比其manager当前薪水还高的相关信息
可以先分表然后连接查询
SQL26 汇总各个部门当前员工的title类型的分配数目
分组的时候加上所有需要的分组不然只会按第一个进行分组
防止索引或数据的扫描操作时会访问表中的很多页,这些页可能只是这次查询有用并不是活跃数据,用传统LRU会导致真正的热点数据被移除从而下次需要重新访问磁盘读取,会导致性能低下
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页