个人整理 - Java 后端面试题 - 数据库篇

本贴最后更新于 1500 天前,其中的信息可能已经时移世异
  • 标 ★ 号为重要知识点

★ 有一个组合索引(A,B,C),可以有哪几种查询方式?

有A,AB,ABC三种查询方式。如果是AC的话,数据库会先利用索引查找到A索引的所有节点,接下来查找C节点时,
则没有使用索引。但并非使用了AC的查询就不走索引。但是如果是BC的话,则不走索引,因为在数据库的B树当中
是以A节点开始索引的。

★ 索引失效的原因有哪些?如何优化避免索引失效?

可以从索引的结构进行分析

  1. 索引树不存 null 值,不走索引
  2. 值变化太少的列,可能不走这个值的索引,或者直接走全表
  3. 索引树按照索引字符串是从首字母开始查找,所以前模糊查找不生效(类似多列索引,查询字段用后面的字段造成不走索引)
  4. 不确定的符号比较(无法在树中定位元素),比如 <>、not in、not exist 之类的无法缩小查找范围的搜索。
  5. mysql 自己估计全表扫描效率更高就不走索引了。
  6. 类型不匹配也会导致索引失效
  7. 使用函数

所以判断为何索引失效主要要从索引的结构去分析。大部分不走索引的情况都是因为条件是发散的,而不是收敛的。

数据库水平切分,垂直切分?

水平切分指的是拆分一张表中的行。
垂直切分指的是拆分一张表中的列。

数据库索引介绍一下。介绍一下什么时候用 Innodb 什么时候用 MyISAM?

  • 如果是读多写少的项目,可以考虑使用 MyISAM,MYISAM 索引和数据是分开的,而且其索引是压缩的,可以更好地利用内存。所以它的查询性能明显优于 INNODB。压缩后的索引也能节约一些磁盘空间。MYISAM 拥有全文索引的功能,这可以极大地优化 LIKE 查询的效率。
  • 如果你的应用程序一定要使用事务,毫无疑问你要选择 INNODB 引擎
  • 如果是用 MyISAM 的话,merge 引擎可以大大加快应用部门的开发速度,他们只要对这个 merge 表做一些 select
    count(*)操作,非常适合大项目总量约几亿的 rows 某一类型(如日志,调查统计)的业务表。

★ 数据库两种引擎

  1. InnoDB 支持事务与外键和行级锁,MyISAM 不支持(最主要的差别)
  2. MyISAM 读性能要优于 InnoDB,除了针对索引的 update 操作,MyISAM 的写性能可能低于 InnoDB,其他操作 MyISAM 的写性能也是优于 InnoDB 的,而且可以通过分库分表来提高 MyISAM 写操作的速度
  3. MyISAM 的索引和数据是分开的,而且索引是压缩的,而 InnoDB 的索引和数据是紧密捆绑的,没有使用压缩,所以 InnoDB 的体积比 MyISAM 庞大
  4. MyISAM 引擎索引结构的叶子节点的数据域,存放的并不是实际的数据记录,而是数据记录的地址。索引文件与数据文件分离,这样的索引称为“非聚簇索引”。其检索算法:先按照 B+Tree 的检索算法检索,找到指定关键字,则取出对应数据域的值,作为地址取出数据记录。InnoDB 引擎索引结构的叶子节点的数据域,存放的就是实际的数据记录。这样的索引被称为“聚簇索引”,一个表只能有一个聚簇索引。
  5. InnoDB 中不保存表的具体行数,也就是说,执行 select count() from table 时,InnoDB 要扫描一遍整个表来计算有多少行,但是 MyISAM 只要简单的读出保存好的行数即可。注意的是,当 count()语句包含 where 条件时,两种表的操作是一样的。
  6. DELETE FROM table 时,InnoDB 不会重新建立表,而是一行一行的删除。
  7. innoDB 表的行锁也不是绝对的,假如在执行一个 SQL 语句时 MySQL 不能确定要扫描的范围,InnoDB 表同样会锁全表,例如 updatetable set num=1 where name like “%aaa%” 在 where 条件没有主键时,InnoDB 照样会锁全表
  8. MySQL 的 innodb 存储引擎支持行级锁,innodb 的行锁是通过给索引项加锁实现的,这就意味着只有通过索引条件检索数据时,innodb 才使用行锁,否则使用表锁。

★ 什么是行锁、表锁、读锁、写锁,说说它们各自的特性?

行锁和表锁是从锁的粒度上来讲。行锁并发高,表锁并发低。
因为行锁有多个,所以会出现互相等待锁的情况,导致死锁。
读锁是 S 锁,共享锁。写锁是 X 锁,排他锁。
悲观锁是认为数据极有可能被修改,所以每次都加锁。
乐观锁原理应该还是 CAS,认为修改的操作不多,不加锁。再并发较小的情况下建议乐观锁。

Mysql 怎么分表后如果想按条件分页查询怎么办

水平分表分页:一定要限定分表条件,否则性能很慢。
垂直分表分页:尽量反模式将查询参数放入主表,非查询参数放入其他表。

★ 分表之后想让一个 id 多个表是自增的,效率实现

  • flicker 实现:选择 ID 数据库的 ID 表的自增 Id 作为多个真实业务表的自增 id,ID 数据库可以多台,并设置自增步长。
  • twitter 实现:雪花算法。
  • 缓存实现:redis 或者 memcached

MySQL 如何启动慢查询日志?

mysql> set global slow_query_log=ON;
mysql> set global slow_launch_time=5;

union 和 union all 的区别?

union 有排重 union all 没有排重

★ 什么情况下行锁变表锁?

首先 innodb 的行锁是利用索引实现的,也就是锁信息在索引上。
更新操作要全表扫描的情况下,会锁表。
在某些情况下,例如索引失效(没有加引号的情况下),索引失效导致扫全表,进而锁全表。

★ 什么情况下会出现间隙锁?

更新操作涉及一个索引范围内的更新,新增的一条数据之前不存在这个索引,但也在这个索引范围内的话,会导致幻读,所以会出现间隙锁。间隙锁在 InnoDB 的唯一作用就是防止其它事务的插入操作,以此来达到防止幻读的发生,所以间隙锁不分什么共享锁与排它锁。 另外,在上面的例子中,我们选择的是一个普通(非唯一)索引字段来测试的,这不是随便选的,因为如果 InnoDB 扫描的是一个主键、或是一个唯一索引的话,那 InnoDB 只会采用行锁方式来加锁,而不会使用 Next-Key Lock 的方式,也就是说不会对索引之间的间隙加锁。

要禁止间隙锁的话,可以把隔离级别降为读已提交,或者开启参数 innodb_locks_unsafe_for_binlog。

★ 谈谈你对 MySQL 的 in 和 exists 用法的理解?

  • exist 在筛选出每行数据的时候都去判断是否存在。
  • In 先计算出子查询中的数据,再根据这些数据去外层查询中筛选条件。

★ 什么是 B-Tree?

B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;
重复,直到所对应的儿子指针为空,或已经是叶子结点;

B-树的特性:

  1. 关键字集合分布在整颗树中;

  2. 任何一个关键字出现且只出现在一个结点中;

  3. 搜索有可能在非叶子结点结束;

  4. 其搜索性能等价于在关键字全集内做一次二分查找;

  5. 自动层次控制;

B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;
重复,直到所对应的儿子指针为空,或已经是叶子结点;

★ 什么是 B+Tree?

B+ 树是 B-树的变体,也是一种多路搜索树:

  1. 其定义基本与 B-树同,除了:

  2. 非叶子结点的子树指针与关键字个数相同;

  3. 非叶子结点的子树指针 P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);

  4. 为所有叶子结点增加一个链指针;

  5. 所有关键字都在叶子结点出现;

B+ 的搜索与 B-树也基本相同,区别是 B+ 树只有达到叶子结点才命中(B-树可以在非叶子结点命中),
其性能也等价于在关键字全集做一次二分查找;

B+ 的特性:

  1. 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;

  2. 不可能在非叶子结点命中;

  3. 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

  4. 更适合文件索引系统;

★B-树和 B+ 树的区别?为什么需要使用 B+ 树作为数据库的聚簇索引?

区别:

  1. B+ 树内节点是不存储数据的。
  2. 内节点的指针树不一样。B-树内节点关键字隔开指针而 B+ 树内节点关键字树就是指针树。指针范围是闭区间。
  3. B+ 叶子结点有链表可以有序。

原因:

  1. 树高效率低。
  2. B+ 树读取一个节点,便知道多个分路。
  3. B+ 树的内节点采用稀疏索引使得内存能加载更多索引。

一条执行慢的 SQL 如何进行优化,如何通过 Explain+SQL 分析性能?

  1. 调整 sql 尽量走索引。
  2. 尽量减少扫描次数。
  3. 尽量减少关联表次数。

尽量调整 SQL 让他走索引,尽量减少扫描次数,或者关联表次数,实在不行只能加索引

★ 数据库的隔离级别

  • 读未提交(Read Uncommitted):只处理更新丢失。如果一个事务已经开始写数据,则不允许其他事务同时进行写操作,
    但允许其他事务读此行数据。可通过“排他写锁”实现。
  • 读提交(Read Committed):处理更新丢失、脏读。读取数据的事务允许其他事务继续访问改行数据,但是未提交的写事
    务将会禁止其他事务访问改行。可通过“瞬间共享读锁”和“排他写锁”实现。
  • 可重复读取(Repeatable Read - Mysql 的默认级别 ):处理更新丢失、脏读和不可重复读取。读取数据的事务将会禁止写事务,但允许读事务,
    写事务则禁止任何其他事务。可通过“共享读锁”和“排他写锁”实现。
  • 序列化(Serializable):提供严格的事务隔离。要求序列化执行,事务只能一个接一个地执行,不能并发执行。
    仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

DB 的特性和隔离级别:

4 大特性:原子性,一致性,分离性,持久性

隔离级别:

  • 读未提交:写事务允许读
  • 读提交:写事务禁止读
  • 可重复读:写事务禁止读事务,读禁止写
  • 序列化:全部禁止

详细说明:读提交 1 个事务开始写则全部禁止其他事务访问该行。读未提交 1 个事务开始写则不允许其他事务同时写,但可以读。
可重复读 读事务会禁止写事务,写事物则禁止其他任何事务。序列化性能最低,全部禁止,串行执行。 MYSQL 默认的是可重复读。

★ 数据库的乐观锁和悲观锁?

  • 乐观锁适用于写少读多的情景,因为这种乐观锁相当于 JAVA 的 CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回。

  • 悲观锁适用于写多读少的情景,这种情况也相当于 JAVA 的 synchronized,reentrantLock 等,大量数据过来的时候,只有一条数
    据可以被写入,其他的数据需要等待。执行完成后下一条数据可以继续。

  • 乐观锁采用版本号的方式,即当前版本号如果对应上了就可以写入数据,如果判断当前版本号不一致,那么就不会更新成功,

  • 悲观锁实现的机制一般是在执行更新语句的时候采用 for update 方式。

数据库的三范式?

  • 第一范式:每一列不能再拆分原子数据项(不能表嵌套表,但我们开发中的,1,2,3,格式字段就属于反范式)
  • 第二范式:在第一范式的基础上属性完全依赖于主键,不能部分依赖。(比如学生课程分数表{学号,课程号,分数,学生姓名},这里
    分数是完全依赖于学号和课程号的,但是学生姓名仅依赖于学号,所以学生姓名必须移出去。但是开发中有时候也会反范式)
  • 第三范式:在第二范式的基础之上,非主键列必须直接依赖于主键不能存在传递依赖。(比如学生信息表{学号,学生姓名,学院号,
    学院名称},学院号依赖学号,学院名称又依赖学院号,存在传递依赖。这里和第二范式不一样,第二范式要求完全依赖,而第三范式要
    求不能传递依赖。

数据库的 ACID?

  • 原子性(atomicity): 一个事物必须被视为一个不可分割的最小工作单元,整个事物中的操作要么全部提交成功,
    要么全部失败回滚,对于一个事物来说,不可能只执行其中的一部分操作,这就是的原子性。
  • 一致性(consistency): 数据库总是从一个一致性的状态转到另一个事务的一致性状态。 (假设用户 A 和用户 B 两者的钱
    加起来一共是 5000,那么不管 A 和 B 之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是 5000)
  • 隔离性(isolation):一个事物所做的修改在最终提交前,对其他事物是不可见的。在前面的例子中,如果执行到第 A 账
    户扣 100,此时有另一个账户汇款,则其看见的 A 账户得余额并没有被减去 100。
  • 持久性(durability): 一旦事物提交,则其所做的修改就会永远保存在数据库中。

★mysql 的主从复制原理?

  1. Slave 上面的 IO 线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
  2. Master 接收到来自 Slave 的 IO 线程的请求后,通过负责复制的 IO 线程根据请求信息读取指定日志指定位置之后的日志信息,
    返回给 Slave 端的 IO 线程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息在 Master 端的 Binary Log 文件的
    名称以及在 Binary Log 中的位置;
  3. Slave 的 IO 线程接收到信息后,将接收到的日志内容依次写入到 Slave 端的 RelayLog 文件(mysql-relay-bin.xxxxxx)
    的最末端,并将读取到的 Master 端的 bin-log 的文件名和位置记录到 master-info 文件中,以便在下一次读取的时候能够清楚
    的告诉 Master“我需要从某个 bin-log 的哪个位置开始往后的日志内容,请发给我”。
  4. Slave 的 SQL 线程检测到 Relay Log 中新增加了内容后,会马上解析该 Log 文件中的内容成为在 Master 端真实执行时候的
    那些可执行的 Query 语句,并在自身执行这些 Query。这样,实际上就是在 Master 端和 Slave 端执行了同样的 Query,所以两
    端的数据是完全一样的。

leftjoin 和 rightjoin 的区别?

  • left join 按左表为基础表与右表连接,找不到对应连接的右表,设置为 null
  • right join 按右表为基础表与左表连接,找不到对应连接的左表,设置为 null
  • inner join 输出两表都存在对应连接的行数。

数据库的优化。

  • 软优化
    • 查询语句优化
    • 优化子查询
    • 使用索引
    • 分解表
    • 增加中间表
    • 增加冗余字段
    • 分析表、检查表、优化表
  • 硬优化
    • cpu、内存、硬盘
    • 参数设置
    • 分库分表、读写分离

★ 什么是索引?什么条件适合建立索引?什么条件不适合建立索引?

  • 索引就是构建出能快速定位出元素的数据结构,例如哈希或者树。
  • 适合做索引:主键、查询条件、关联字段、排序、分组
  • 不适合做索引:频繁更新、不会出现在 where 中,不进行关联,字段变化太少的字段

数据库连接池的工作原理?

由于程序每次创建和关闭数据库连接都比较耗费资源,所以引入数据库连接池。在程序启动时初始化多个数据库连接,
需要访问数据库时就在连接池中获取连接对象,使用完毕后释放连接对象。如果访问超过连接池数量则会等待。

jdbc 如何控制事务的?

自动提交事务:默认 setAutocommit(true)
手动提交事务:setAutocommit(false) commit 提交 rollback 回滚

Statement 和 PreparedStatement 有什么区别?哪个性能更好?

与 Statement 相比:

  • PreparedStatement 接口代表预编译的语句,它主要的优势在于可以减少 SQL 的编译错误并增加 SQL 的安全性
    (减少 SQL 注射攻击的可能性);
  • PreparedStatement 中的 SQL 语句是可以带参数的,避免了用字符串连接拼接 SQL 语句的麻烦和不安全;
  • 当批量处理 SQL 或频繁执行相同的查询时,PreparedStatement 有明显的性能上的优势,由于数据库可以将编译
    优化后的 SQL 语句缓存起来,下次执行相同结构的语句时就会很快(不用再次编译和生成执行计划)

count(*)、count(列名)和 count(1)的区别?

COUNT(常量) 和 COUNT()表示的是直接查询符合条件的数据库表的行数。而 COUNT(列名)表示的是查询符合条件的列的值不为 NULL 的行数。
在官方文档中:
InnoDB handles SELECT COUNT(
) and SELECT COUNT(1) operations in the same way. There is no performance difference.
表明 count(*)和 count(1)无区别

mysql 数据库锁表怎么解决?

查询锁表信息

当前运行的所有事务
select * from information_schema.innodb_trx
当前出现的锁
select * from information_schema.innodb_locks
锁等待的对应关系
select * from information_schema.innodb_lock_waits  
通过 select * from information_schema.innodb_trx 查询 trx_mysql_thread_id
然后执行 kill 线程ID
KILL   8807;//后面的数字即时进程的ID

查询 mysql 中的线程:

show processlist
kill id

★ 聚集索引和非聚集索引的区别?

聚集索引:
索引中键值的逻辑顺序决定了表中相应行的物理顺序(索引中的数据物理存放地址和索引的顺序是一致的),可以这么理解:
只要是索引是连续的,那么数据在存储介质上的存储位置也是连续的。比方说:想要到字典上查找一个字,我们可以根据字典
前面的拼音找到该字,注意拼音的排列时有顺序的。聚集索引就像我们根据拼音的顺序查字典一样,可以大大的提高效率。
在经常搜索一定范围的值时,通过索引找到第一条数据,根据物理地址连续存储的特点,然后检索相邻的数据,直到到达条件截至项。

非聚集索引:
索引的逻辑顺序与磁盘上的物理存储顺序不同。非聚集索引的键值在逻辑上也是连续的,但是表中的数据在存储介质上的物理顺序是
不一致的,即记录的逻辑顺序和实际存储的物理顺序没有任何联系。索引的记录节点有一个数据指针指向真正的数据存储位置。

总结如下:

  • 如果一个主键被定义了,那么这个主键就是作为聚集索引

  • 如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引

  • 如果没有主键也没有合适的唯一索引,那么 innodb 内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个 6 个字节的列,
    该列的值会随着数据的插入自增。InnoDB 引擎会为每张表都加一个聚集索引,而聚集索引指向的的数据又是以物理磁盘顺序来存储的,
    自增的主键会把数据自动向后插入,避免了插入过程中的聚集索引排序问题。如果对聚集索引进行排序,这会带来磁盘 IO 性能损耗是非常大的。

  • 聚集索引:类似新华字典正文内容本身就是一种按照一定规则排列的目录,其实是数据的存储方式。

  • 非聚集索引:新华字典开头中的目录,需要根据页码来找到指定的字。这种才是传统意义上的索引。

★ 数据库的锁?

  • 从锁的粒度:行锁、表锁

  • 从事务的角度:共享锁(只能读)、排他锁(不能读不能写)、更新锁

  • 从程序的角度:悲观锁和乐观锁

      更新锁不是很能理解
    

★MyIsam 和 InnoDB 锁的不同

MyISAM 采用表级锁,对 Myisam 表读不会阻塞读,会阻塞同表写,对 Myism 写则会阻塞读和写,即一个线程获得 1 个表的写锁后,
只有持有锁的线程可以对表更新操作,其他线程的读和写都会等待。

InnoDB,采用行级锁,支持事务,例如只对 a 列加索引,如果 update ...where a=1 and b=2 其实也会锁整个表,
select 使用共享锁,update insert delete 采用排它锁,commit 会把锁取消,当然 select by id for update 也可以制定排它锁。

★Mysql 怎么分表,以及分表后如果想按条件分页查询怎么办(如果不是按分表字段来查询的话,几乎效率低下,无解)

分表的话,可以使用 mysql 自带的 partition 功能,如果没有按照分表字段进行条件分页的话,没有很好的解决办法,需要借助大数据工具。
分表之后想让一个 id 多个表是自增的,效率实现
设置不同表的自增步长,或者自增 id 由另外一张表来专门生成。或者由 redis 来生成自增 id

★ 什么是幻读。innodb 如何解决幻读的?

幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
幻读仅专指“新插入的行”。

当前读:在有索引的情况下,innodb 会使用间隙锁来防止幻读。
普通读:普通读没有索引作为依据的情况下,innodb 会使用 MVCC 防止幻读。

高并发下,如何做到安全的修改同一行数据。

  • 数据库本来的锁
  • redis 的锁
  • zookeeper 公平锁

★SQL 优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义。

explain sql

主要字段有

  • type:all,index,range,ref,eq_ref,const,system
  • key:使用到的索引键
  • ref:显示索引的哪一列用于查找
  • rows:扫描的行数
  • extra:具体情况

具体各个字段如下:
https://blog.csdn.net/qinaye/article/details/100077070

MYsql 的索引原理,索引的类型有哪些,如何创建合理的索引,索引如何优化。

https://blog.csdn.net/qq_38149009/article/details/81779853

★select for update 是什么含义,会锁表还是锁行或是其他?

InnoDb 行锁是通过给索引上的索引项加锁来实现的,这一点 mysql 与 oracle 不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB 这种行锁实现的特点意味着: 只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁。

在实际应用中,要特别注意 InnoDB 行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。在不通过索引条件查询的时候,InnoDB 确实使用的是表锁,而不是行锁。
由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,因此虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。

当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引还是普通索引,InnoDB 都会使用行锁来对数据加锁。即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同的执行计划的代价来决定的。如果 MySQL 认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查 SQL 的执行计划,以确认是否真正使用了索引

★ 为什么要用 Btree 实现,它是怎么分裂的,什么时候分裂,为什么是平衡的,mysql 索引为什么使用 B+ 树。

https://blog.csdn.net/qq_36520235/article/details/94317993

★ 某个表有近千万数据,CRUD 比较慢,如何优化。

https://blog.csdn.net/qq_37221991/article/details/87693639

★ 数据库自增主键可能的问题。(优缺点自增主键和 UUID 的)

使用自增长做主键的优点:

  1. 很小的数据存储空间

  2. 性能最好

  3. 容易记忆
    使用自增长做主键的缺点:

  4. 如果存在大量的数据,可能会超出自增长的取值范围

  5. 很难(并不是不能)处理分布式存储的数据表,尤其是需要合并表的情况下

  6. 安全性低,因为是有规律的,容易被非法获取数据

使用 UUID 做主键的优点:

  1. 它是独一无二的,出现重复的机会少
  2. 适合大量数据中的插入和更新操作,尤其是在高并发和分布式环境下
  3. 跨服务器数据合并非常方便
  4. 安全性较高

使用 UUID 做主键的缺点:

  1. 存储空间大(16 byte),因此它将会占用更多的磁盘空间
  2. 会降低性能
  3. 很难记忆

MVCC 的含义,如何实现的。

MVCC 即多版本并发控制。MySQL 的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制可以认为 MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。大多数的 MVCC 都实现了非阻塞的读操作,写操作也只锁定必要的行。

MVCC 的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据是一致的。根据事务开始的时间不同,每个事物对同一张表,同一时刻看到的数据可能是不一样的。

InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏的列来实现。这两个列,一个保存了行的创建时间,一个保存了行的过期时间(删除时间)。并且存储的并不是真实的时间值,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。

MVCC 只在 REPEATABLE READ 和 READ COMMITTED 两个隔离级别下工作。其他两个隔离级别都和 MVCC 不兼容,因为 READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行,而 SERIALIZABLE 会对所有读取到的行都加锁。

★ 你做过的项目里遇到分库分表了吗,怎么做的,有用到中间件么,比如 sharding jdbc 等,他们的原理知道么。

分表主要从时间范围进行水平分表,还有将大字段拆出去进行垂直分表。分库的话,通过哈希或者其他策略将数据分别存到不同的数据库当中。相关数据库中间件的介绍:https://blog.csdn.net/u011596455/article/details/84821559
原理无非就是多一层代理层进行 sql 解析执行并汇总。

MySQL InnoDB 存储的文件结构

https://blog.csdn.net/john_lw/article/details/80306122

★MySQL 的几种优化

  1. SQL 语句优化
  2. 索引优化
    • 选择合适的列建立索引
    • 索引优化 SQL 的方法
    • 索引维护的方法
  3. 数据库结构优化
    • 选择合适的数据类型
    • 范式优化
    • 反范式优化
    • 数据库表的垂直拆分
    • 数据库表的水平拆分
  4. 系统配置优化
    • 数据库系统配置优化
    • MySQL 配置文件优化
    • 第三方配置工具使用
  5. 服务器硬件优化

★ 数据库锁表的相关处理

数据库锁表:在数据库里,同一个数据可能有多个人来读取或更改,为了防止更改的时候别人也同时更改,这里一般要锁住
表不让别人改。当然还有其它各种复杂情况。

数据库锁从类型上讲,有共享锁,意向锁,排他锁。从锁的粒度角度来说,可以分为为行、页键、键范围、索引、表或数据库
获取锁。(锁粒度是被封锁目标的大小,封锁粒度小则并发性高,但开销大,封锁粒度大则并发性低但开销小)

可能的原因有:

  • 字段不加索引:在执行事务的时候,如果表中没有索引,会执行全表扫描,如果这时候有其他的事务过来,就会发生锁表!
  • 事务处理时间长:事务处理时间较长,当越来越多事务堆积的时候,会发生锁表!
  • 关联操作太多:涉及到很多张表的修改等,在并发量大的时候,会造成大量表数据被锁!

出现锁表的解决方法有:

  • 通过相关的 sql 语句可以查出是否被锁定,和被锁定的数据!
  • 为加锁进行时间限定,防止无限死锁!
  • 加索引,避免全表扫描!
  • 尽量顺序操作数据!
  • 根据引擎选择合理的锁粒度!
  • 事务中的处理时间尽量短!

INNODB 的行级锁有哪 2 种,解释其含义

  • 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
  • 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

★ 数据库会死锁吗,举一个死锁的例子,mysql 怎么解决死锁

出现原因:

1、一个用户 A 访问表 A(锁住了表 A),然后又访问表 B;另一个用户 B 访问表 B(锁住了表 B),然后企图访问表 A;这时用户 A 由于用户 B 已经锁住表 B,它必须等待用户 B 释放表 B 才能继续,同样用户 B 要等用户 A 释放表 A 才能继续,这就死锁就产生了。

2、用户 A 查询一条纪录,然后修改该条纪录;这时用户 B 修改该条纪录,这时用户 A 的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户 B 里的独占锁由于 A 有共享锁存在所以必须等 A 释放掉共享锁,而 A 由于 B 的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁由于比较隐蔽,但在稍大点的项目中经常发生。

 该条有争议

3、如果在事务中执行了一条不满足条件的 update 语句,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,
最终发生阻塞或死锁。

一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。

解决方法:

这种死锁比较常见,是由于程序的 BUG 产生的,除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作 A 和 B 两张表时,总是按先 A 后 B 的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。

转自我的 github

技术讨论群 QQ:1398880
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    324 引用 • 1395 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...