△ 66次 MySQL 为什么使用 B+ 树来作索引,对比 B 树它的优点和缺点是什么?  中等

  1. 从需求分析上来讲,数据库使用多为 查询新增 操作,改删 本质上也是查询的进一步动作
  2. 二分搜索树实现的
DB 存在一个问题: 范围查询会很慢。我们需要不断地从根节点出发,然后往下遍历。所以我们稍微改造下,数据只 保存在叶子节点上,并且用双向链表进行连接
  3. 对于二分搜索树实现的
DB 而言,其查询效率是与树高有关的。假设我们有
2000 万条数据,我们大概需要一棵树高为
25 的
BST
才能装下所 有数据和索引。也就是说,我们至少需要
25 次硬盘
I/O 才能取出一条数据
  4. 将刚刚说的二分搜索树进行树高的压缩,让它变矮胖,就得到了多叉搜索树,aka B-树。
  5. B+ 树是二叉搜索树的一个扩充,是多路搜索树。它只在叶子节点存储具体的数据或者数据的指向指针,而非叶子节点存放索引数据。这样可以降低磁盘 IO,还能充分利用磁盘的预读功能,批量的加载索引数据。

△ 42次 数据库的事务隔离级别有哪些?各有哪些优缺点?  中等

首先要明白几个概念

  • 脏读:脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。

  • 可重复读:可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。

  • 不可重复读:对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。

  • 幻读:幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

  • 未提交读:(Read Uncommited)

    • 读取未提交内容,所有事务可看到其他未提交事务的结果,很少实际使用
    • 读取未提交的数据称为脏读(Dirty Read)。
    • 修改时加排它锁;读取时加共享锁,读完释放。 eg:select for update
  • 已提交读:(Read Committed) 如果想防止脏读,就需要等待其他事务提交后再进行读取操作。

  • 可重复读: (Repeatable Read)已提交读的隔离级别考虑到了数据回滚的无效性,却无法阻止事务的多次提交。比如事务 A 不断的对表进行修改提交,那么事务 B 就会在不同的时间点读取到不同的数据。为了让事务 B 在执行期间读取的数据都是一致的,就有了可重复读的隔离级别,即事务 B 在执行期间,其他事务不得进行修改操作。

  • 可串行化: (Serializable)上面的可重复读隔离级别保证了事务执行期间读取的一致性。然而这里并不包括插入、删除操作。即会出现读多读少数据的情况,这种现象叫做幻读。为了解决幻读,只得进行串行化执行事务,才能互不影响。而此时的事务并发性是最低的

△ 36次 简述 Redis 持久化中 RDB 以及 AOF 方案的优缺点  困难

1. RDB:Redis Database

指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作是 fork 一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,使用二进制压缩存储。
优点:

  • 整个 redis 只包含一个文件 dump.rdb,方便持久化
  • 容灾性好,易于备份
  • 性能最大化,使用子进程处理,保证主进程 IO 吞吐,保证了 redis 的高性能
  • 相对于数据集大时,比 AOF 的启动效率更高
    缺点:
  • 数据安全性低,因为会有间隔时间,所以如果间隔期间出现故障,无法保证期间的数据正常备份
  • 当数据集较大时,子进程长期占用 CPU,会导致服务中断时间延长至秒级

2. AOF:Append Only File

以日志的形式记录服务器处理的每个读写操作,以文本的方式详细记录,可以查看历史操作记录
优点:

  • 数据安全:每秒同步、每修改同步、不同步3中策略。每秒同步可以理解为类似1s间隔的 RDB ,可能也会出现丢失的情况;每修改同步可以认为是同步持久化,不会丢失
  • 通过 append 方式追加新的操作,不惧宕机,可以使用 redis-check-aof 工具来解决一致性的问题。
  • rewrite 模式可以定期对 AOF 文件重写,合并相关操作,以达到压缩存储的目的
    缺点
  • AOF 文件较大,恢复速度慢
  • 数据集大的情况下,启动比 RDB 慢
  • 运行效率 AOF < RDB

4.x版本后,把重写方式变成 RDB 直接放到 AOF 文件的头部,比以前版本更快

3. 主从同步

  • 全量复制:
    • 主节点通过 bgsave 命令 fork 子进程进行 rdb 持久化,过程非常消耗CPU,内存,硬盘io
    • 主节点将rdb文件通过网络发给从节点,消耗带宽
    • 从节点清空数据,使用rdb文件载入,整个过程阻塞,无法响应命令
  • 部分复制:
    • 复制偏移量,双方都要维护
    • 复制积压缓冲区:定长FIFO的队列作为缓冲区

△ 36次 Redis 如何实现分布式锁?  困难

一般可以采用 setnx 原子操作来进行加锁,常规过期时间为 1s,设置的值为随机数。对于加锁的 key,一般要和当前的操作和请求有幂等关系,防止解锁的时候把其他请求的锁解掉;另一方面如果 1s 过期时间还未执行完毕,可以考虑使用看门狗给这个锁续期。
对于 setnx 的解锁,可以使用 lua 脚本 ‘if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end’ 保证解的锁就是请求刚刚申请的那个锁

Redlock

这个是针对于 Redis 中多 master 实例的情况来获取锁的。具体的步骤如下:

  • 获取当前毫秒单位时间
  • 以相同的 key 和随机值在 n 个节点上请求锁,这里获取锁的尝试时间一定要远小于锁的过期时间,防止某个 master 宕机,而我们还在不断地尝试,产生较长的阻塞时间
  • 只有在绝对大多数节点上获取了锁,且获取时间小于锁的过期时间,就认为锁获取成功了
  • 如果获取成功,key 的真正有效时间等于过期时间时间减去获取锁所使用的时间(即还会在预设的过期时间过期掉)
  • 如果获取失败,或者尝试时间超过了超时时间,都认为是获取失败,要将已经设置了 key 的 master 节点删除该 key

△ 34次 Redis 序列化有哪些方式?  中等

  1. 字符串序列化:将数据以字符串的形式进行序列化和反序列化,常用的字符串序列化方式有JSON、XML、YAML等。
  2. 二进制序列化:将数据以二进制的形式进行序列化和反序列化,常用的二进制序列化方式有MessagePack、Protocol Buffers、Thrift等。
  3. 压缩序列化:将序列化后的数据进行压缩,减小存储空间,常用的压缩序列化方式有Gzip、Snappy等。
  4. 自定义序列化:根据业务需求自定义序列化方式,可以根据数据的特点选择最合适的序列化方式,例如使用Hessian、Kryo等。

△ 34次 简述 Redis 的哨兵机制  中等

主从复制

当从数据库启动时,会向主数据库发送sync命令,主数据库接收到sync后开始在后台保存快照rdb,在保存快照期间收到的命令缓存起来,当快照完成时,主数据库会将快照和缓存的命令一块发送给从库。复制初始化结束。 之后,主每收到1个命令就同步发送给从。 当出现断开重连后,2.8之后的版本会将断线期间的命令传给重数据库。增量复制

主从复制是乐观复制,当客户端发送写执行给主,主执行完立即将结果返回客户端,并异步的把命令发送给从,从而不影响性能。也可以设置至少同步给多少个从主才可写。 无硬盘复制:如果硬盘效率低将会影响复制性能,2.8之后可以设置无硬盘复制,repl-diskless-sync yes

哨兵模式

哨兵的作用:
1、监控redis主、从数据库是否正常运行
2、主出现故障自动将从数据库转换为主数据库。
哨兵的核心知识
1、哨兵至少需要 3 个实例,来保证自己的健壮性。
2、哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
3、对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
4、配置哨兵监控一个系统时,只需要配置其监控主数据库即可,哨兵会自动发现所有复制该主数据库的从数据库。

△ 30次 简述 Redis 中如何防止缓存雪崩和缓存击穿  中等

数据库是架构的瓶颈,要尽量保证有效请求到达数据库,可以酌情放大链路上前置位置的复杂度和成本
c client;redis ; db/mysql

穿透:缓存和db中都找不到该数据 = db中无数据,redis无数据,大量并发,请求打到db层
原因:接口请求数据不是有效参数,因此根本查不到结果;有效参数但是确实没有数据
解法:接口层增加鉴权,业务层增加有效请求判断;返回空 && 增加短效的空缓存;布隆过滤器(难道要加到每个接口上么?需要思考嗷);业务加互斥锁;对于空值可以增加一个空缓存

击穿:热点key过期 or key从来没有被访问过 = db中有数据,大量并发,redis无缓存,请求打到db层
解法:热点数据永不过期;互斥锁(挡住大量重复的并发请求)

雪崩:N个key的都过期了(没有被缓存到) = db中有,大量并发,redis无缓存,请求打到db层
解法:缓存数据设置随机过期时间;增加缓存失效标记位;缓存预热;互斥锁

流程: 请求redis,查不到 –> 大家抢锁 O(1) –> 抢上的查db,并更新缓存 O(1) –> 没抢上的重复请求Redis ,拿到数据
延伸问题:多个等待的请求,是blocking的轻量级进程,不参与cpu及内核调度,注意处理线程池即可

△ 30次

产生死锁的必要条件有哪些?如何解决死锁?  中等 参考1 参考2 参考3

△ 28次 简述 Redis 中常见类型的底层数据结构

string

  • id/id_info 缓存
  • 计数器
  • setnx 分布式锁
    • setnx 的时候会传入一个随机值
    • 解锁:‘if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end’
    • lua脚本
  • 分布式ID
  • sds(动态简单字符串)
    • 底层结构为:
      • len buf中已经占有的长度(表示此字符串的实际长度)
      • free buf中未使用的缓冲区长度
      • buf[] 实际保存字符串数据的地方
    • 读取时间复杂度为 O(1)
    • 惰性删除,留待下次使用
    • 一次分配最大为 128kb

      list

  • 当队列用,最近100个购买用户信息
  • 当栈用
  • 元素可重复
  • 非实时分页列表,比如小时榜,日榜,周榜
  • 存储微博、微信公众号等消息流数据
  • 等于链表,插入删除 O(1),查找 O(n)
  • 底层是一个叫做 quicklist 的节点,相当于由 ziplist 节点组成的双向链表

    hash

  • 相比string获取单个字段节省 序列化与反序列化操作
  • 相当于 HashMap ,由数组加链表组合而成,当一维的 hash 数组碰撞时,用链表串起来
  • 底层实现方式有两种,数据量少的时候是 ziplist;量大时转为 Hashtable
  • hashtable 由 size、used、哈希节点数组组成;
    • hash节点就是 kv 键值对,并且用链地址法处理冲突

      set

  • 取交、并、差集
  • 点赞、收藏、关注等…
  • kv 模式
  • 内部元素不重复

    sorted set(zset)

  • 排行榜功能,实时分页列表,实时榜单等
  • 延迟队列,score 存储过期时间,从小到大排序,最靠前的就是最先到期的数据。
  • set 基础上增加了分数
  • 结构上是由一个 dict 和一个跳表组成,dict 保证 key 唯一性
  • 插入删除都是 O(log n),查找 O(logn)
  • 一大优点是可以对分数进行范围查找 zrange
  • 少量数据按照 ziplist 存储,member和score顺序存放并按score的顺序排列;元素数量 < 128,元素大小 < 64kb
  • 大量数据按照跳表存储

△ 28次 简述什么是最左匹配原则  简单

mysql 建立联合索引后,是按最左匹配原则来筛选记录的,即检索数据是从联合索引的第一个字段来筛选的。如果 where 里的条件只有第二个字段,那么将无法应用到索引。

△ 26次 简述 Redis 的过期机制和内存淘汰策略  困难

过期机制

  1. 惰性过期:访问一个 key 的时候才判断这个 key 是否已经过期,该策略可以最大化的节省 CPU 资源,但是对内存不友好,会存在大量的过期 key 没有被再次访问,从而不会被清除,占用内存,造成泄漏
  2. 定期过期:每隔一定的时间,扫描数据库中一定量的 expire 字典中的 key,并清除其中已经过期的 key。该策略是前两者的一个折中方案,通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使内存和 CPU 达到最优平衡。特点:随机抽取检查,缺点是实际操作中很难界定时间间隔和限定耗时。
  3. 二种策略同时生效

    内存淘汰策略

    常见的六种淘汰策略

  • noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。 大多数写命令都会导致占用更多的内存(有极少数会例外, 如 DEL )
  • allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key
  • volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key
  • allkeys-random: 所有key通用; 随机删除一部分 key
  • volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key
  • volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key

    使用场景

  • 如果分为热数据与冷数据, 推荐使用 allkeys-lru 策略;也就是, 其中一部分key经常被读写. 如果不确定具体的业务特征, 那么 allkeys-lru 是一个很好的选择
  • 如果需要循环读写所有的key, 或者各个key的访问频率差不多, 可以使用 allkeys-random 策略, 即读写所有元素的概率差不多
  • 假如要让 Redis 根据 TTL 来筛选需要删除的key, 请使用 volatile-ttl 策略
    volatile-lru 和 volatile-random 策略主要应用场景是: 既有缓存,又有持久key的实例中。一般来说, 像这类场景, 应该使用两个单独的 Redis 实例
    值得一提的是, 设置 expire 会消耗额外的内存, 所以使用 allkeys-lru 策略, 可以更高效地利用内存, 因为这样就可以不再设置过期时间了

    淘汰的内部实现

    淘汰过程可以这样理解:
  • 应用执行一个命令, 导致 Redis 中的数据增加,占用更多内存
  • Redis 检查内存使用量, 如果超出 maxmemory (redis.conf中配置)限制,根据策略清除部分 key
  • 继续执行下一条命令, 以此类推
    在这个过程中, 内存使用量会不断地达到 limit 值, 然后超过, 然后删除部分 key, 使用量又下降到 limit 值之下
    如果某个命令导致大量内存占用(比如通过新key保存一个很大的set), 在一段时间内, 可能内存的使用量会明显超过 maxmemory 限制

△ 24次

数据库有哪些常见索引?数据库设计的范式是什么?  中等 参考1

△ 24次 简述乐观锁以及悲观锁的区别以及使用场景  简单

悲观锁(Pessimistic Lock):
总是担心数据被修改,每次使用数据都要加锁,使用结束以后解锁,期间所有其他线程等待。
乐观锁(Optimistic Lock):
不担心数据被修改,使用数据不加锁,但是更新数据时会判断是否有其他人修改过,如果已经被修改,就不继续修改。

使用场景:

悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。
乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

△ 24次 MySQL 中 join 与 left join 的区别是什么?  简单

join = inner join 返回两个表中都有的数据
left join 以左表为基准,返回左表中的所有数据结果,右表中不存在的值一般为 null

△ 24次

简述 MySQL MVCC 的实现原理  困难 参考1

△ 22次

简述脏读和幻读的发生场景,InnoDB 是如何解决幻读的?  中等 参考1 参考2

△ 20次

Redis 有几种数据结构?Zset 是如何实现的?  中等 参考1 参考2

△ 20次 简述 Redis 中跳表的应用以及优缺点  中等

时间复杂度

我们知道单链表查询的时间复杂度为O(n),而插入、删除操作需要先找到对应的位置,所以插入、删除的时间复杂度也是O(n)。
那么,跳表的时间复杂度是多少呢?
如果按照标准的跳表来看的话,每一级索引减少k/2个元素(k为其下面一级索引的个数),那么整个跳表的高度就是(log n)。
学习过平衡二叉树的同学都知道,它的时间复杂度与树的高度成正比,即O(log n)。
所以,这里跳表的时间复杂度也是O(log n)。(这里不一步步推倒了,只要记住,查询时每次减少一半的元素的时间复杂度都是O(log n),比如二叉树的查找、二分法查找、归并排序、快速排序)

空间复杂度

我们还是以标准的跳表来分析,每两个元素向上提取一个元素,那么,最后额外需要的空间就是:
n/2 + (n/2)^2 + (n/2)^3 + … + 8 + 4 + 2 = n - 2
所以,跳表的空间复杂度是O(n)。

随机层数

在新增节点时会计算当前节点应该放置的层数,表面上是按照随机数计算出来的。
跳表通常需要有一个最大层数maxLevel,以及一个概率p,即新增加一层的概率。通常数据量够大,就会呈现出一定的比率。
当p是0.25,那么只有产生一层的概率就是1-p(0.75),产生第二层的概率就是(1-p) * p。三层(1-p)pp…
因此,一个节点的平均层数(也即包含的平均指针数目)

总结

(1)跳表是可以实现二分查找的有序链表;
(2)每个元素插入时随机生成它的level;
(3)最低层包含所有的元素;
(4)如果一个元素出现在level(x),那么它肯定出现在x以下的level中;
(5)每个索引节点包含两个指针,一个向下,一个向右;
(6)跳表查询、插入、删除的时间复杂度为O(log n),与平衡二叉树接近;

△ 18次 聚簇索引和非聚簇索引有什么区别?  简单

聚簇索引(Clustered Index)和非聚簇索引(Non-Clustered Index),也称为二级索引或非聚集索引,是数据库索引的两种主要类型,它们在结构和性能方面有显著的区别:

  1. 存储结构

    • 聚簇索引:在聚簇索引中,索引的构建与表数据是一起存储的。这意味着索引的叶节点直接包含行数据或指向行数据的指针。由于数据按索引顺序存储,聚簇索引可以快速检索数据。
    • 非聚簇索引:在非聚簇索引中,索引结构是独立的,并且索引的叶节点包含指向表数据的指针(如行ID)。这意味着索引和数据分开存储,非聚簇索引用于快速定位到数据行,但可能需要额外的I/O操作来检索实际的数据。
  2. 物理顺序

    • 聚簇索引的物理顺序与索引顺序相同,即数据行是按照索引的顺序来存储的。
    • 非聚簇索引的物理顺序与索引顺序可以不同,数据行的存储顺序与索引顺序是独立的。
  3. 一个表中的索引数量

    • 聚簇索引:一个表只能有一个聚簇索引,因为表数据本身只能按一种顺序存储。
    • 非聚簇索引:一个表可以有多个非聚簇索引,每个索引都可以提供访问数据的不同路径。
  4. 性能

    • 聚簇索引由于数据和索引在一起,对于范围查询和顺序访问非常高效。
    • 非聚簇索引在执行查找时可能需要两次查找:首先在索引中查找行的指针,然后在表数据中检索实际数据。
  5. 更新性能

    • 更新聚簇索引可能更昂贵,因为可能需要移动数据行来维护索引的物理顺序。
    • 非聚簇索引的更新通常更快,因为只需更新索引页,不需要移动数据行。
  6. 主键约束

    • 通常,表的主键会自动创建一个聚簇索引,除非明确指定使用非聚簇索引。
    • 非聚簇索引可以通过唯一约束或索引命令显式创建。
  7. 使用场景

    • 聚簇索引适合于经常通过索引值访问数据的场景,如主键查询。
    • 非聚簇索引适合于需要多种不同方式访问数据的场景,可以提高查询的灵活性。

理解聚簇索引和非聚簇索引的区别对于数据库设计和性能优化非常重要,合理的索引设计可以显著提高数据库的查询效率和整体性能。

△ 17次 MySQL 中 varchar 和 char 的区别是什么?  简单

CHAR

  • 固定长度:CHAR 类型用于存储固定长度的字符串。不管实际存储的数据长度是多少,CHAR 类型的列都会在存储时填充空格(空格补齐)到指定长度。
  • 存储效率:由于长度固定,CHAR 类型在存储时效率较高,因为 MySQL 可以直接定位到特定行的位置。
  • 适用场景:适合存储长度固定的数据,例如国家代码、邮政编码等。

    VARCHAR

  • 可变长度:VARCHAR 类型用于存储可变长度的字符串。它只会存储实际使用的字符数,并在存储时附加一个额外字节(或两个字节,如果字符串长度超过 255)来记录字符串的长度。
  • 存储效率:由于长度可变,VARCHAR 类型在插入和检索数据时可能需要更多的处理时间,但在存储空间的使用上更为灵活。
  • 适用场景:适合存储长度不固定的数据,例如名字、地址、描述等。

△ 16次* 简述数据库中的 ACID 分别是什么?  简单

  • 原子性(Atomicity):事务是一个不可分割的单位,因此在一个事务里的所有操作要么全部生效,要么全部不生效。
  • 一致性(Consistency):也可以理解为是预期状态的正确性,即从一个正确的状态到另一个正确的状态,这里的状态往往是由业务来定义的。比如转账中的一个扣钱一个加钱,是我们规定的一个数据流转,那么执行前的账户余额和转账后的账户余额就得满足加减特性,这就是所谓的业务正确。题外话:银行家舍入 —— 四舍六入五考虑,五后非零则进一,五后皆零看奇偶,奇进偶舍不连续。
  • 隔离性(Isolation):事务并发执行时,各个事务之间相互影响的程度。
  • 持久化(Durability):通过日志等手段,只要我们的事务提交成功了,那么就意味着这次的数据操作是成功的。即使下次重启了程序,也不会丢失此处的操作结果。

△ 16次

简述 MySQL 三种日志的使用场景  中等 参考1 参考2

△ 16次

模糊查询是如何实现的?  简单

△ 14次

并发事务会引发哪些问题?如何解决?  中等

△ 12次

什么是数据库事务,MySQL 为什么会使用 InnoDB 作为默认选项

布隆过滤器

可以简单的理解为有一批哈希函数,对将要存储的内容进行哈希计算,每个哈希函数的结果对应一个 bit 位,如果命中了结果,就将对应 bit 位置为1。那么当这个数据查询请求再次来到时,比较哈希函数结果位是否都为1,就可以知道这个数据是不是在我们的集合内了。
值得一提的是,当存储的内容很大时,可能大部分 bit 位都被标记为1了,存在误判的可能。
即布隆过滤器中查到的元素可能存在在集合中,但是查不到的元素一定不在

常见应用:

  • 网页爬虫对 URL 去重,避免爬取相同的 URL 地址;
  • 反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱;

遇见慢查询如何查看执行计划
2024-02-05 京东
可以开启慢查询日志,使用 EXPLAIN 语句来查看执行计划。
通过分析执行计划,可以了解数据库如何执行查询语句的。在执行计划中,表的访问方式、使用的索引、行数估计等信息,都可能通过调整来提高查询性能。

MySQL 的索引机制
2024-02-28 阿里
B-tree索引:
B-tree(Balanced Tree)是一种平衡树结构,用于实现对数据的快速查找。MySQL的B-tree索引是基于这种数据结构实现的,包括以下几种类型:
普通索引(Normal Index): 普通的B-tree索引,没有任何限制。
唯一索引(Unique Index): 确保索引列的所有值都是唯一的,不允许重复。
主键索引(Primary Key Index): 主键是一种特殊的唯一索引,用于唯一标识每一条记录。
组合索引(Composite Index): 使用多个列组合成一个索引,可以提高查询的效率。
哈希索引:
哈希索引是基于哈希表实现的索引,适用于等值查询。相比B-tree索引,哈希索引在等值查询时速度更快,但在范围查询和排序等操作上性能较差。
哈希索引的主要特点:
适用于等值查询,例如WHERE column = value。
不支持范围查询和排序操作。
不支持部分索引查询。
对于哈希冲突(多个不同的键值映射到同一个哈希桶)的处理通常通过链表等方式实现。

MySQL 如何避免死锁
2024-02-12 bilibili
死锁是无法完全避免的,但可以通过一些手段降低死锁发生概率。

缩短锁持有的时间
减少间隙锁
减少加锁范围
设置 MySQL 参数
设置锁等待超时参数:innodb_lock_wait_timeout
开启主动死锁检测:innodb_deadlock_detect