1.redis 相关面试题
1.1 Redis为什么执行这么快?
(1)纯内存操作:Redis将所有数据存储在内存中,这意味着对数据的读写操作直接在内存中运行,而内存的访问速度远远高于磁盘。这种设计使得Redis能够已接近硬件极限的速度处理数据读写
(2)单线程模型:Redis使用单线程模型来处理客户端请求。这可能听起来效率不高,但是实际上,这种设计避免了多线程频繁切换和过度竞争带来的性能开销。Redis每个请求的执行时间都是很短的,因此单线程下,也能处理大量的并发请求
(3)I/O多路复用:Redis使用了I/O多路复用技术,可以在单线程的环境下同时监听多个客户端连接,只有当有网络事件(如用户发送一个请求)发生的时候才会进行实际的I/O操作。这样有效的利用了CPU资源,减少了无谓的等待
(4)高效数据结构:Redis提供了多种高效的数据结构,如哈希表、有序集合等。这些数据结构的实现都经过了优化,使得Redis在处理这些数据结构的操作是非常高效的
1.2 Redis是单线程执行还是多线程执行?它有线程安全问题吗?为什么吗?
Redis版本在6.0之前都是使用的单线程运行的。所有的客户端的请求处理、命令执行以及数据读写操作都是在一个主线程中完成得。这种设计目的就是为了防止多线程环境下的锁竞争和上下文切换所带来的性能开销,这样保证在高并发场景下的性能
Redis版本在6.0中,开始引入了多线程的支持,但是这仅限于网络I/O层面,即在网络请求阶段使用工作线程进行处理,对于指令的执行过程,仍然是在主线程来处理,所以不会存在多个线程通知执行操作指令的情况
关于线程安全问题,从Redis服务层面俩看,Redis Server本身就是一个线程安全按的K-V数据库,也就是说在Redis Server上面执行的指令,不需要任何同步机制,不会存在线程安全问题
1.3 在实际工作中,使用Redis实现了哪些业务场景?
Redis在实际工作中广泛应用于多种业务场景,以下是一些常见的例子:
缓存:Redis作为Key-Value形态的内存数据库,最先会被想到的应用场景就是作为数据缓存。Redis提供了键过期功能,也提供了键淘汰策略,所以Redis用在缓存的场合非常多
排行榜:很多网站都有排行榜应用,如京东的月度销量榜单、商品按时间上新排行榜等。Redis提供的有序集合(zset)数据类
分布式会话:在集群模式下,一般会搭建以Redis等内存等内存数据库为中心的session服务,session不再由容器管理,而是由session服务及内存数据库管理
分布式锁:在高并发的情景,可以利用Redis的setnx功能来编写分布式锁
1.4 Redis常用数据类型有哪些?
字符串(String):最简单的数据类型,可以包含任意数据,如文本、二进制数据等。常见的使用场景是存储Session信息、存储缓存信息、存储整数信息,可以使用incr实现整数+1,使用decr实现整数-1
列表(List):有序的字符串元素集合,支持双端进行插入和删除操作,可以用作队列或栈
哈希(Hash):用于存储对象,类似于关联数组。每个哈希可以包含字段和与之相关联的值。常见使用场景是存储Session信息、存储商品的购物车,购物车非常适用于哈希字典表示,使用人员唯一编号作为字典的key,value值可以存储商品的id和数量等信息、存储详情页等信息
集合(Set):一个无序并唯一的键值集合。它常见的使用场景是是仙女关注功能,比如关注我的人和我关注的人,使用集合存储,可以保证人员不重复
有序集合(Sorted Set):使用zset表示,相当于Set集合类型多了一个排序属性score(分值)。。它常见的使用场景是可以用来存储排名信息,关注列表功能,这样就可以根据关注实现排序展示
1.5 有序集合底层是如何实现的?
在Redis7之前,有序集合使用的是ziplist(压缩列表)+skiplist(跳跃表),当数据列表元素小于128个,并且所有元素成员的长度都小于64字节时,使用压缩列表存储,否则使用调表存储
在Redis之后,有序集合使用listPack(紧凑列表)+skiplist(跳跃表)
1.6 什么是跳表?为什么使用跳表?
skiplist是一种以空间换时间的数据结构。由于链表无法进行二分查找,因此借鉴数据库索引的思想,提取出链表中的关键姐点(索引),现在关键节点上查找,在进入下层链表查找提取多层关键节点,就形成了跳表。但是由于索引要占据一定的空间,所以索引添加的越多,占用的空间越多。
对于一个单链表来讲,即便链表中存储的数据是有序的,如果我们要想在其中查找某个数据,也只能从头到尾遍历链表。这样查找效率就会很低,时间复杂度会很高O(N)
从这个例子里,我们看出,加来一层索引之后,查找一个结点需要遍历的结点个数减少了,也就是说查找效率提高了。时间复杂度从原来的O(n)到O(logn),是一空间换时间的解决方法
1.7 使用Redis如何实现分布式锁?
使用Redis实现分布式锁可以通过setnx(set if not exists)命令实现,但当我们使用setnx创建键值成功时,则表中加锁成功,否则代码加锁失败,实现示例如下:
127.0.0.1:6379> setnx lock true
(integer) 1#创建锁成功
#逻辑业务处理..
当我们重复加锁时,只有第一次会加锁成功
127.8..1:6379> setnx lock true # 第一次加锁
(integer) 1
127.8.8.1:6379> setnx lock true # 第二次加锁
(integer) 0
从上述命令可以看出,我们可以看执行结果返回是不是1,就可以看出是否加锁成功
释放分布式锁
127.0.0.1:6379> de1 lock
(integer) 1 #释放锁
然而,如果使用 setnx lock true 实现分布式锁会存在死锁问题,以为 setnx 如未设置过期时间,锁忘记删了或加锁线程宕机都会导致死锁,也就是分布式锁一直被占用的情况
解决死锁问题
死锁问题可以通过设置超时时间来解决,如果超过了超时时间,分布锁会自动释放,这样就不会存在死锁问题了也就是 setnx和 expire 配合使用,在 Redis 2.6.12 版本之后,新增了一个强大的功能,我们可以使用一个原子操作也就是一条命令来执行 setnx 和expire 操作了,实现示例如下:
其中ex为设置超时时间, nx 为元素非空判断,用来判断是否能正常使用锁的。
因此,我们在 Redis 中实现分布式锁最直接的方案就是使用 set key value ex timeout nx 的方式来实现。
1.8 Redis数据持久化方案?
Redis是一个内存数据库,一旦断电或服务器进程退出,内存数据库中的数据将全部丢失,所以需要Redis持久化
Redis持久化就是把数据保存在磁盘上,利用永久性存储介质将数据保存,在特定的时间将保存的数据进行恢复的工作机制
Redis提供两种持久化机制:
RDB(Redis DataBase):存储数据结果,关注点在数据(快照)
AOF(Append Only File):存储操作过程,关注点在数据的操作过程(命令)
RDB与AOF触发方式、优缺点:
RDB的触发方式:
手动触发:通过命令手动生成快照 (save,bgsave)
自动触发:通过配置参数的设置触发自动生成快照
缺点:
快照时间有间隔,不能实时备份,丢失数据可能会比较多
开启子进程备份数据,在数据集比较庞大时,fork()可能会非常耗时,造成服务器在一定时间内停止处理客户端。
优点:
1.恢复数据比较快
2.备份的文件就是原始内存数据的大小,不会额外增加数据占用,
AOF的触发方式
1.手动触发
通过bgrewriteaof命令:重新AOF持久化生成aof文件(触发重写)
2.自动触发
默认情况,redis是没有开启AOF(默认使用RDB持久化),需要通过配置文件开启
AOF的优缺点
优点:
数据安全性高,不易丢数据
AOF文件有序保存了所有写操作,可读性强
缺点:
AOF方式生成文件体积变大
数据恢复速度比RDB慢
1.9 Redis面试题-缓存穿透,缓存击穿,缓存雪崩
1 穿透: 两边都不存在(皇帝的新装) (黑名单) (布隆过滤器)
2 击穿:一个热点的key失效了,这时大量的并发请求直接到达数据库. (提前预热)
3 雪崩:大量key同时失效 (避免大量的key同一时间失效,错峰)
1.91 Redis 过期键删除策略
1)惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
2)定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
3)内存淘汰策略 Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
1.92 Redis主从同步机制
步骤如下:(全量)
1.从服务器向主服务器发送同步命令 sync;
2.主数据库接收到同步命令后,会执行 bgsave 命令,在后台生成一个 rdb 文件,并使用一个缓冲区记录从现在开始执行的所有写命令;
3.当主服务器执行完 bgsave 命令后,主服务器会将 bgsave 命令生成的 rdb 文件发送给从服务器;
4.从服务器接收到这个 rdb 文件,然后加载到内存 ;之后主服务器会把刚刚在缓存区的命令同步过来,从服务器就会执行这些命名。(两边就一致了)
5.以上处理完之后,之后主数据库每执行一个写命令,都会将被执行的写命令发送给从数据库。
1.93 Redis 和 Mysql 数据库数据如何保持一致性
1、在发现缓存没有数据后,在执行查询数据库前,对该Key进行加锁,查询数据库并放入缓存后再解锁,这样可以避免缓存击穿问题,当某个redis数据不存在时,大量线程并发查询数据库。
2、在需要执行双删前,对该Key进行加锁,之后执行删除缓存,更新数据库,放入新数据到缓存,在解锁。保证缓存和数据一致性。
3、加锁的Key都需要设置过期时间,避免因为宕机造成死锁。
1.94 Redis集群
Redis提供了多种集群模式以适应不同场景下的高可用性和水平扩展需求。以下是Redis集群模式:
主从复制(Master-Slave)模式:
在此模式下,有一个主节点负责处理写入请求,而从节点则复制主节点的数据并提供读取服务。
优点:实现简单,能实现数据冗余,通过读写分离提高系统性能。
缺点:需要手动进行故障转移,无法自动处理主节点故障;不支持自动的数据分区(sharding),难以做到水平扩展。
哨兵(Sentinel)模式:
Sentinel是Redis提供的一个高可用性解决方案,它能监控主从节点状态,并在主节点出现故障时自动完成故障转移。
优点:解决了主从模式下手动故障转移的问题,提供了自动化监控和故障恢复机制。
缺点:虽然比主从模式增加了自动化,但仍不支持自动的数据分区,且随着节点数量增加,管理和配置的复杂性也会增大。
Redis Cluster模式:
Redis Cluster是官方正式支持的分布式解决方案,它采用了数据分片(sharding)技术,将数据分散在多个节点上。
优点:真正实现了分布式存储,每个节点都可以处理读写请求,具备良好的水平扩展能力;内置了数据自动分割、故障检测与转移功能。
缺点:相比其他模式更复杂,需要更多的网络资源和配置管理;客户端需要支持集群特性;跨slot的数据操作可能涉及多个节点,有一定复杂度。
1.95 redis的数据是如何保存的以及保存的数据结构
存储结构
字典(dict)实现
redis 数据库通过 dict 实现映射关系。key 的固定类型是 string,value 的类型有多种。
redis 中 KV 组织是通过字典来实现的;hash 结构当节点超过512 个或者单个字符串长度大于 64 时,hash 结构采用字典实现
dict 由哈希表 dictht + 哈希节点 dictEntry 组成。哈希表有两个,通常 ht[0] 使用,ht[1] 不使用;rehash 时,ht[0] 存储 rehash 之前的数据,ht[1] 存储新数据和 ht[0] 迁移来的数据。
1)字符串经过 hash 函数运算得到 64 位整数;
2)相同字符串多次通过 hash 函数得到相同的64位整数;
3)整数对 取余可以转化为位运算;sizemask是size-1,属于对字典的优化。因为散列表的存储是通过hash(key)%size=index确定索引,sizemask是对取余长度的优化,将hash(key)%size变成hash(key) &sizemask,把除法优化为二进制的运算,从而提高执行速度,这种优化的前提是 数组的长度必须是2的n次幂(2 n 2^n2n)。
哈希冲突
哈希冲突指的是不同的键在哈希表中计算得到相同的哈希值,但它们的实际存放位置并不相同。在哈希表中,每个键通过哈希函数映射到一个桶(bucket)或槽(slot),存储在对应的位置上。
由于哈希表的大小是有限的,而键的数量可能是无限的,所以哈希冲突是不可避免的。
我们通过负载因子 LoadFactor = used / size
来衡量哈希冲突的程度, used 是数组存储元素的个数,size 是数组的长度;
负载因子越小,冲突越小;负载因子越大,冲突越大;redis 的负载因子是 1 .
扩容
- 如果负载因子 > 1 ,则会发生扩容;扩容的规则是翻倍;
- 如果正在 fork (在 rdb、aof 复写以及 rdb-aof 混用情况下)时,会阻止扩容;
- 但是此时若负载因子 > 5 ,索引效率大大降低, 则马上扩容;这里涉及到写时复制原理;
在写时复制中,当需要修改一个数据副本时,不会立即进行实际的复制操作,而是在修改发生时创建该数据的新副本。这样可以避免对原始数据进行修改,从而保持数据的一致性和完整性。
写时复制核心思想:只有在不得不复制数据内容时才去复制数据内容;
缩容
如果负载因子 < 0.1 ,则会发生缩容;缩容的规则是恰好包含used 的 2 n 2^n2n;
恰好的理解:假如此时数组存储元素个数为 9,恰好包含该元素的就是 ,也就是 16;
为什么缩容的负载因子不是小于1?
因为缩容的负载因子是小于1的话会造成频繁的扩缩容,扩缩容都有分配内存的操作,内存操作变得频繁就会造成IO密集。
渐进式rehash
扩容和缩容都会导致rehash,因为映射算法发生了改变。
当 hashtable 中的元素过多的时候,因为redis是一个数据库,里面存储的数据非常多,不能一次性 rehash 到ht[1];这样会长期占用 redis,其他命令得不到响应;所以需要使用渐进式 rehash。
rehash步骤:
将 ht[0] 中的元素重新经过 hash 函数生成 64 位整数,再对ht[1] 长度进行取余,从而映射到 ht[1]。
渐进式规则:
1) 分治的思想,将 rehash 分到之后的每步增删改查的操作当中。
2)在定时器中,最大执行一毫秒 rehash ;每次步长 100 个数组槽位。
3)处理渐进式 rehash 的过程中,不会发生扩容和缩容。
2. mysql相关面试题
2.1 什么是数据库事务?
数据库事务是一个作为单个逻辑工作单元执行的一系列操作。事务具有ACID属性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这意味着事务内的操作要么全部成功,要么全部失败,保持数据完整性,并且独立于其他事务运行。
2.2 MySQL中InnoDB与MyISAM的区别是什么?
InnoDB支持事务处理,行级锁定和外键,适用于需要高并发和事务处理的场景。MyISAM不支持事务和行级锁定,但读取速度快,适用于查询密集型的场景。
2.3 如何优化MySQL查询?
优化MySQL查询的方法包括:使用合适的索引、避免在WHERE子句中使用函数、选择合适的数据类型、使用LIMIT语句减少数据量、避免全表扫描、合理设计表结构等。
2.4 为什么选择 B+ 树
- 哈希索引虽然能提供O(1)复杂度查询,但对范围查询和排序却无法很好的支持,最终会导致全表扫描。
- B 树能够在非叶子节点存储数据,但会导致在查询连续数据可能带来更多的随机 IO。
- 而 B+ 树的所有叶节点可以通过指针来相互连接,减少顺序遍历带来的随机 IO。
2.5 什么是覆盖索引和索引下推?
覆盖索引:
在某个查询里面,索引 k 已经“覆盖了”我们的查询需求,称为覆盖索引。
覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
索引下推:
MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
2.6 哪些操作会导致索引失效?
对索引使用左或者左右模糊匹配,也就是 like %xx 或者 like %xx% 这两种方式都会造成索引失效。原因在于查询的结果可能是多个,不知道从哪个索引值开始比较,于是就只能通过全表扫描的方式来查询。
对索引进行函数/对索引进行表达式计算,因为索引保持的是索引字段的原始值,而不是经过函数计算的值,自然就没办法走索引。
对索引进行隐式转换相当于使用了新函数。
WHERE 子句中的 OR语句,只要有条件列不是索引列,就会进行全表扫描。
2.7 MySQL 的 redo log 和 binlog 区别?
2.8 为什么 redo log 具有 crash-safe 的能力,是 binlog 无法替代的?
第一点:redo log 可确保 innoDB 判断哪些数据已经刷盘,哪些数据还没有
redo log 和 binlog 有一个很大的区别就是,一个是循环写,一个是追加写。也就是说 redo log 只会记录未刷盘的日志,已经刷入磁盘的数据都会从 redo log 这个有限大小的日志文件里删除。binlog 是追加日志,保存的是全量的日志。
当数据库 crash 后,想要恢复未刷盘但已经写入 redo log 和 binlog 的数据到内存时,binlog 是无法恢复的。虽然 binlog 拥有全量的日志,但没有一个标志让 innoDB 判断哪些数据已经刷盘,哪些数据还没有。
但 redo log 不一样,只要刷入磁盘的数据,都会从 redo log 中抹掉,因为是循环写!数据库重启后,直接把 redo log 中的数据都恢复至内存就可以了。
第二点:如果 redo log 写入失败,说明此次操作失败,事务也不可能提交
redo log 每次更新操作完成后,就一定会写入日志,如果写入失败,说明此次操作失败,事务也不可能提交。
redo log 内部结构是基于页的,记录了这个页的字段值变化,只要crash后读取redo log进行重放,就可以恢复数据。
这就是为什么 redo log 具有 crash-safe 的能力,而 binlog 不具备。
2.9 当数据库 crash 后,如何恢复未刷盘的数据到内存中?
根据 redo log 和 binlog 的两阶段提交,未持久化的数据分为几种情况:
change buffer 写入,redo log 虽然做了 fsync 但未 commit,binlog 未 fsync 到磁盘,这部分数据丢失。
change buffer 写入,redo log fsync 未 commit,binlog 已经 fsync 到磁盘,先从 binlog 恢复 redo log,再从 redo log 恢复 change buffer。
change buffer 写入,redo log 和 binlog 都已经 fsync,直接从 redo log 里恢复。
2.91 什么是两阶段提交?
MySQL 将 redo log 的写入拆成了两个步骤:prepare 和 commit,中间再穿插写入binlog,这就是"两阶段提交"。
而两阶段提交就是让这两个状态保持逻辑上的一致。redolog 用于恢复主机故障时的未更新的物理数据,binlog 用于备份操作。两者本身就是两个独立的个体,要想保持一致,就必须使用分布式事务的解决方案来处理。
为什么需要两阶段提交呢?
如果不用两阶段提交的话,可能会出现这样情况
先写 redo log,crash 后 bin log 备份恢复时少了一次更新,与当前数据不一致。
先写 bin log,crash 后,由于 redo log 没写入,事务无效,所以后续 bin log 备份恢复时,数据不一致。
两阶段提交就是为了保证 redo log 和 binlog 数据的安全一致性。只有在这两个日志文件逻辑上高度一致了才能放心的使用。
在恢复数据时,redolog 状态为 commit 则说明 binlog 也成功,直接恢复数据;如果 redolog 是 prepare,则需要查询对应的 binlog事务是否成功,决定是回滚还是执行。
2.92 CHAR和VARCHAR的区别?
- CHAR和VARCHAR类型在存储和检索方面有所不同
- CHAR列长度固定为创建表时声明的长度,长度值范围是1到255
- 当CHAR值被存储时,它们被用空格填充到特定长度,检索CHAR值时需删除尾随空格
2.93 聚簇索引与非聚簇索引区别
存储方式不同:#
聚簇索引是将数据按照索引顺序存储在磁盘上,因此聚簇索引的数据存储和索引存储是混合在一起的;而非聚簇索引则是将索引和数据分开存储的。唯一性不同:#
聚簇索引必须是唯一的,因为它们是按照索引顺序存储数据的,如果有两条数据具有相同的索引值,则它们将无法区分;而非聚簇索引可以是唯一的,也可以不是唯一的。查询效率不同:#
对于聚簇索引来说,查询效率往往比非聚簇索引更高,因为聚簇索引将数据存储在一起,查询时可以更快地定位到所需的数据行;而对于非聚簇索引来说,查询时需要先查找索引,再根据索引找到对应的数据行,因此查询效率相对较低。插入数据效率不同:#
对于聚簇索引来说,由于数据按照索引顺序存储,因此在插入新数据时,可能需要移动已有的数据,因此插入数据的效率较低;而对于非聚簇索引来说,插入数据时只需要更新索引,因此效率相对较高。需要注意的是,一个表只能有一个聚簇索引,因为数据只能按照一种顺序存储;而可以有多个非聚簇索引,以满足不同的查询需求。在设计数据库时,需要根据具体的应用场景和查询需求选择不同的索引类型。
2.94 什么是 MVCC?
数据库隔离级别读已提交、可重复读 都是基于MVCC实现的,相对于加锁简单粗暴的方式,它用更好的方式去处理读写冲突,能有效提高数据库并发性能。
MVCC实现的关键知识点
事务版本号: 事务每次开启前,都会从数据库获得一个自增长的事务ID,可以从事务ID判断事务的执行先后顺序。这就是事务版本号。
隐式字段: 对于InnoDB存储引擎,每一行记录都有两个隐藏列trx_id、roll_pointer,如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列row_id。
undo log : undo log,回滚日志,用于记录数据被修改前的信息。在表记录修改之前,会先把数据拷贝到undo log里,如果事务回滚,即可以通过undo log来还原数据。
可以这样认为,当delete一条记录时,undo log 中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录。
undo log有什么用途呢?
- 事务回滚时,保证原子性和一致性。
- 用于MVCC快照读。
版本链: 多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(roll_pointer),连成一个链表,这个链表就称为版本链。如下:
其实,通过版本链,我们就可以看出事务版本号、表格隐藏的列和undo log它们之间的关系。我们再来小分析一下。
- 假设现在有一张core_user表,表里面有一条数据,id为1,名字为孙权:
现在开启一个事务A: 对core_user表执行update core_user set name ="曹操" where id=1,会进行如下流程操作
首先获得一个事务ID=100
把core_user表修改前的数据,拷贝到undo log
修改core_user表中,id=1的数据,名字改为曹操
把修改后的数据事务Id=101改成当前事务版本号,并把roll_pointer指向undo log数据地址。
快照读和当前读
快照读: 读取的是记录数据的可见版本(有旧的版本)。不加锁,普通的select语句都是快照读,如:
select * from core_user where id > 2;
当前读:读取的是记录数据的最新版本,显式加锁的都是当前读
select * from core_user where id > 2 for update;
select * from account where id>2 lock in share mode;
Read View
Read View是什么呢? 它就是事务执行SQL语句时,产生的读视图。实际上在innodb中,每个SQL语句执行前都会得到一个Read View。
Read View有什么用呢? 它主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据~
Read View是如何保证可见性判断的呢?我们先看看Read view 的几个重要属性
m_ids:当前系统中那些活跃(未提交)的读写事务ID, 它数据结构为一个List。
min_limit_id:表示在生成ReadView时,当前系统中活跃的读写事务中最小的事务id,即m_ids中的最小值。
max_limit_id:表示生成ReadView时,系统中应该分配给下一个事务的id值。
creator_trx_id: 创建当前read view的事务ID
Read view 匹配条件规则如下:
如果数据事务ID trx_id < min_limit_id,表明生成该版本的事务在生成Read View前,已经提交(因为事务ID是递增的),所以该版本可以被当前事务访问。
如果trx_id>= max_limit_id,表明生成该版本的事务在生成ReadView后才生成,所以该版本不可以被当前事务访问。
如果 min_limit_id =<trx_id< max_limit_id,需腰分3种情况讨论
(1).如果m_ids包含trx_id,则代表Read View生成时刻,这个事务还未提交,但是如果数据的trx_id等于creator_trx_id的话,表明数据是自己生成的,因此是可见的。
(2)如果m_ids包含trx_id,并且trx_id不等于creator_trx_id,则Read View生成时,事务未提交,并且不是自己生产的,所以当前事务也是看不见的;
(3).如果m_ids不包含trx_id,则说明你这个事务在Read View生成之前就已经提交了,修改的结果,当前事务是能看见的。
MVCC实现原理分析
查询一条记录,基于MVCC,是怎样的流程
- 获取事务自己的版本号,即事务ID
- 获取Read View
- 查询得到的数据,然后Read View中的事务版本号进行比较。
- 如果不符合Read View的可见性规则, 即就需要Undo log中历史快照;
- 最后返回符合规则的数据
InnoDB 实现MVCC,是通过 Read View+ Undo Log
实现的,Undo Log 保存了历史快照,Read View可见性规则帮助判断当前版本的数据是否可见。
2.95 mysql8.0版本后增加了什么功能?
1.MySQL 8.0对redo log进行了重构,去掉了之前的锁机制,采用了区间的方式来保证数据的一致性。这种改进使得redo log的写入更加高效,提高了整体的运行效率。同时,MySQL 8.0还引入了Link_buf数据结构,使得整个模块变成了Lock_free的模式,进一步提升了性能。这种无锁化的重构使得不同的线程在写入redo_log_buffer时得以并行写入,从而提高了数据库的并发性能。
2.MySQL 8.0新增了隐藏索引和降序索引等特性。隐藏索引可以用来测试去掉索引对查询性能的影响,帮助管理员找到最佳的索引策略。而降序索引则可以提高查询的效率,特别是对于需要按照降序排列的查询场景。这些索引优化使得MySQL在查询性能方面有了显著提升。
3.随着非关系型数据库和数据存储的兴起,MySQL 8.0也对NoSQL支持进行了优化。它不再依赖模式(schema),以更灵活的方式实现NoSQL功能,从而满足用户在数据处理方面的多样化需求。这使得MySQL能够更好地适应不同的应用场景,提供了更广泛的数据存储和查询选项。
4.MySQL 8.0新增了SET PERSIST命令,允许用户将配置持久化到数据目录下的mysqld-auto.cnf文件中。这样,即使数据库重启,之前的配置也会得到保留,从而简化了配置管理的工作。这使得管理员可以更加方便地管理和调整数据库的配置,提高了数据库的可维护性。
操作 SET PERSIST 命令,例如:
SET PERSIST max_connections = 500;
MySQL 会将该命令的配置保存到数据目录下的 mysqld-auto.cnf 文件中,下次启动时会读取该文件,用其中的配置来覆盖缺省的配置文件。
5. MySQL 8.0 加入了窗口函数支持,如ROW_NUMBER()、RANK()、DENSE_RANK()等。窗口函数允许用户在查询中对数据集进行窗口分析,执行更复杂的计算任务。这使得MySQL在数据处理和分析方面的能力得到了显著提升。通过窗口函数,用户可以轻松地计算移动平均值、累计总和等,实现更高级的数据分析需求。
窗口函数有点像是 SUM()、COUNT() 那样的集合函数,但它并不会将多行查询结果合并为一行,而是将结果放回多行当中。也就是说,窗口函数是不需要 GROUP BY 的。
2.96 undo log 的作用
undo log是mysql中比较重要的事务日志之一,顾名思义,undo log是一种用于撤销回退的日志,在事务没提交之前,MySQL会先记录更新前的数据到 undo log日志文件里面,当事务回滚时或者数据库崩溃时,可以利用 undo log来进行回退。
1、提供回滚操作
2、提供多版本控制(MVCC)【undo log实现多版本并发控制(MVCC)】
undo log的存储由InnoDB存储引擎实现,数据保存在InnoDB的数据文件中。在InnoDB存储引擎中,undo log是采用分段(segment)的方式进行存储的。rollback segment称为回滚段,每个回滚段中有1024个undo log segment。在MySQL5.5之前,只支持1个rollback segment,也就是只能记录1024个undo操作。在MySQL5.5之后,可以支持128个rollback segment,分别从resg slot0 - resg slot127,每一个resg slot,也就是每一个回滚段,内部由1024个undo segment 组成,即总共可以记录128 * 1024个undo操作
2.97 mysql中的一级缓存和二级缓存有什么区别?
一级缓存:
也称本地缓存,sqlSession级别的缓存。一级缓存是一直开启的;与数据库同一次会话期间查询到的数据会放在本地缓存中。
如果需要获取相同的数据,直接从缓存中拿,不会再查数据库。
一级缓存失效的四种情况:
1.sqlSession不同。
2.sqlSession相同,查询条件不同。因为缓存条件不同,缓存中还没有数据。
3.sqlSession相同,在两次相同查询条件中间执行过增删改操作。(因为中间的增删改可能对缓存中数据进行修改,所以不能用)
4.sqlSession相同,手动清空了一级缓存。
二级缓存:全局缓存;基于namespace级别的缓存。一个namespace对应一个二级缓存。
工作机制:1.一个会话,查询一条数据,这个数据会被放在当前会话的一级缓存中。
2,如果会话被关闭了,一级缓存中的数据会被保存带二级缓存。新的会话查询信息就会参照二级缓存。
3.sqlSession ====> Employee====>employee
sqlSession ====>DepartmentMapper=====>Department
不同的namespace查出的数据会放在自己对应的缓存中。
效果:查出的数据首先放在一级缓存中,只有一级缓存被关闭或者提交以后,一级缓存数据才会转移到二级缓存
3. mongoDB相关面试题
3.1.简述什么是MongoDB?
MongoDB是一个基于分布式文件存储的数据库,由C++语言编写,旨在为Web应用提供可扩展的高性能数据存储解决方案。它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。MongoDB支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
3.2. MySQL与MongoDB之间最基本的差别是什么?
ySQL和MongoDB之间有很多区别,以下是一些最基本的区别:
数据库类型:MySQL是关系型数据库,而MongoDB是非关系型数据库。
数据存储方式:MySQL支持多种引擎,不同引擎有不同的存储方式,而MongoDB以类JSON的文档的格式存储。
查询语言:MySQL使用传统SQL语句进行查询,而MongoDB有自己的查询方式(类似JavaScript的函数)。
索引:MySQL可以对表中的列建立索引,而MongoDB可以对任何属性建立索引。
扩展性:MySQL虽然也可以扩展,但需要更多的工作,而MongoDB是一个基于分布式文件存储的数据库,可以方便地扩展到大量的数据和高并发。
延迟:由于MongoDB对写入操作有较低的延迟,因此非常适合实时应用,而MySQL延迟相对较高。
事务:MySQL有完整的事务支持,而MongoDB不支持事务操作。
数据模式:MySQL需要预先定义字段,而MongoDB是动态模式,同一个集合里的文档不需要有相同的字段和结构。
3.3 MongoDB成为最好NoSQL数据库的原因是什么?
MongoDB成为最好NoSQL数据库的原因主要有以下几点:
面向文档的存储方式:MongoDB采用面向文档的存储方式,这意味着它可以直接存储数据对象,而不需要像关系型数据库那样将数据拆分成多个字段。这种存储方式使得MongoDB在处理复杂数据结构时更加灵活和高效。
高性能:MongoDB具有出色的性能,尤其是在处理大量数据和高并发访问时。它采用二进制协议,可以快速地读写数据,并且支持索引和查询优化,进一步提高查询效率。
高可用性:MongoDB具有高可用性,可以在多个节点之间进行数据复制和备份,确保数据的可靠性和容错性。此外,MongoDB还支持自动分片和水平扩展,可以轻松地扩展数据库的容量和性能。
易扩展性:MongoDB具有易于扩展的特性,可以方便地增加节点来处理更多的数据和请求。这对于需要处理大规模数据和高并发访问的应用程序来说非常有用。
丰富的查询语言:MongoDB采用类似JavaScript的查询语言,可以轻松地执行复杂的查询操作。这种查询语言功能强大且易于使用,可以满足各种数据检索需求。
综上所述,MongoDB的高性能、高可用性、易扩展性和丰富的查询语言等特点使得它成为最好的NoSQL数据库之一。
3.4 简述MongoDB内部构造?
MongoDB的内部构造主要由以下几个部分组成:
数据库:MongoDB是一个基于文档的数据库,可以创建多个数据库。每个数据库都有自己的文件和索引,用于存储和检索数据。
集合:集合是MongoDB中存储文档的容器。一个集合可以包含多个文档,每个文档都是一个键值对的形式,键是字符串,值可以是各种数据类型。
文档:文档是MongoDB中的基本数据单位,它是一个键值对的集合。键是字符串,值可以是各种数据类型,包括其他文档、数组、日期、布尔值等。
索引:索引是MongoDB中用于加速查询的数据结构。它可以根据一个或多个字段的值创建,使得查询操作能够快速找到满足条件的文档。
复制:MongoDB支持数据复制,可以将数据从一个节点复制到另一个节点。这有助于提高数据的可用性和可靠性。
分片:MongoDB支持数据分片,可以将一个集合中的数据分散到多个节点上,以便提高数据的处理能力和存储容量。
查询语言:MongoDB使用一种类似JavaScript的查询语言,用于检索和操作数据。这种语言非常灵活,可以用于执行各种复杂的查询操作。
总的来说,MongoDB的内部构造是基于文档的存储模型,通过索引、复制和分片等技术来提高数据的处理能力和存储容量。同时,MongoDB还提供了丰富的查询语言和API接口,方便开发者进行应用程序的开发和集成。
3.5 MongoDB支持事务吗?
MongoDB支持事务。 MongoDB最初并不支持事务,但在其4.0版本中引入了多文档事务支持,使得在单个集合中执行多个操作成为可能。
MongoDB的事务具有以下特点:
- 原子性:事务包含的所有操作要么全部成功,要么全部失败,不存在成功一半的情况。
- 一致性:MongoDB支持在复制集和分片集群中使用事务,提高了系统的可用性和可靠性。
- 隔离级别:MongoDB提供了不同的隔离级别,以满足不同的应用需求。
然而,MongoDB的事务也有一些限制:
- 单文档原子性:虽然MongoDB支持多文档事务,但对单个文档的更新仍然是原子的,这有助于避免分布式事务的需求。
- 复杂查询不支持:MongoDB的事务不支持复杂的查询操作,主要支持简单的CRUD操作。
- 性能考虑:使用事务可能会对性能产生一定影响,特别是在高并发环境下。
综上所述,MongoDB通过引入多文档事务支持,提供了更强大的数据一致性保证,但开发者在使用时仍需注意其限制和性能影响。