数据库基础面试题汇总(mysql调优/底层B+ tree机制/sql执行计划详解/索引优化详解/sql语句优化)

深入理解数据库 专栏收录该内容
5 篇文章 0 订阅

数据库基础面试题汇总(关系型数据库mysql/mysql调优/底层B+ tree机制/sql执行计划详解/索引优化详解/sql语句优化)

学习完本文章的好处:即使只是一个开发工程师,只是 MySQL 的用户,在了解了一个个系统模块的原理后,再来使用它,感觉是完全不一样的。当在代码里写下一行数据库命令的时候,我就能想到它在数据库端将怎么执行,它的性能是怎么样的,怎样写能让我的应用程序访问数据库的性能最高。进一步,哪些数据处理让数据库系统来做性能会更好,哪些数据处理在缓存里做性能会更好,我心里也会更清楚。在建表和建索引的时候,我也会更有意识地为将来的查询优化做综合考虑,比如确定是否使用递增主键、主键的列怎样选择。学习资料:《高性能mysql》《mysql必知必回》《redis入门指南》《redis实战》《mysql实战45讲》(强烈推荐) 慕课网教学视频

文章目录

推荐书籍

1、为什么学习 MySql?

1、mysql基础介绍,什么是mysql

mysql是最流行的关系型数据库管理系统之一,在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展,其默认端口号是3306。

  • linux下查看安装目录 ps -ef | grep mysql
  • 查看MySQL提供的所有存储引擎 show engines;

2、mysql的配置文件详解

1、日进制日志log-bin //主从复制
2、错误日志log-error //默认关闭,用于记录严重的警告和错误信息,每次启动和关闭的详情
3、查询日志log //默认关闭,记录查询的sql语句,开启会降低mysql性能
4、数据文件 //路径/var/lib/mysql frm文件:存放表结构 mym文件:存放表数据 myi文件:存放表索引
5、如何配置 // /etc/my.cnf文件


3、什么样的数据库设计时符合要求的(从范式的角度)大学学习数据库时,第一章的内容

理论上达到第三范式是符合要求的,但是一般在生产环境下为了数据查询方便,数据会有一定的冗余,也就是说一般达到第二范式即可。
范式的种类:

  • 第一范式:字段不可再分; 例如标准中心后台类目表中的tags字段就不满足该范式要求,给维护带来极大的困难
  • 第二范式:满足第一范式的前提下,不能出现部分依赖。如果某个字段依赖复合主键的一部分字段,则称之为对主键的部分依赖 例如:两个字段才能确定一个主键,就不满足第二范式;
  • 第三范式:满足第二范式的前提下,不能出现传递依赖,某个字段依赖于主键,而有其他字段依赖于该字段。例如:C依赖于B,B依赖于主键,则C对主键存在传递依赖关系(实际开发中,达不到第三范式的要求也是可以的)

4、sql语句执行过程(一条sql语句在mysql中如何执行的) 20200930 牛逼

1、mysql组件
连接器: 身份认证和权限相关(登录 MySQL 的时候)。
查询缓存: 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。
分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
优化器: 按照 MySQL 认为最优的方案去执行。
执行器: 执行语句,然后从存储引擎返回数据。

2、简单来说 MySQL 主要分为 Server 层和存储引擎层:
Server 层:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如 存储过程、触发器、视图,函数 等(我司一般不使用),还有一个通用的日志模块 binlog 日志模块。
存储引擎:主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始就被当做默认存储引擎了。

3、查询语句
我们的 sql 可以分为两种,一种是查询,一种是更新(增加,更新,删除)。我们先分析下查询语句,语句如下:

select * from tb_student  A where A.age='18' and A.name=' 张三 ';

结合上面的说明,我们分析下这个语句的执行流程:

  • 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。

  • 通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 parana_items,需要查询所有的列,查询条件是这个表的 id=‘1’。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。

  • 接下来就是优化器进行确定执行方案,上面的 sql 语句,可以有两种执行方案:

    a.先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。
    b.先找出学生中年龄 18 岁的学生,然后再查询姓名为“张三”的学生。
    那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。

  • 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。

4、更新语句
以上就是一条查询 sql 的执行流程,那么接下来我们看看一条更新语句如何执行的呢?sql 语句如下:

update tb_student A set A.age='19' where A.name=' 张三 ';

我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的,不然要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块式 binlog(归档日志,可以理解为git中的master分支) ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志,可以理解为git中的release分支),我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:

  • 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
  • 然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
  • 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。
  • 更新完成。

5、为什么要用两个日志模块,用一个日志模块不行吗?

这是因为最开始 MySQL 并没与 InnoDB 引擎( InnoDB 引擎是其他公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。

并不是说只用一个日志模块不可以,只是 InnoDB 引擎就是通过 redo log 来支持事务的。那么,又会有同学问,我用两个日志模块,但是不要这么复杂行不行,为什么 redo log 要引入 prepare 预提交状态?这里我们用反证法来说明下为什么要这么做?

  • 先写 redo log 直接提交,然后写 binlog,假设写完 redo log 后,机器挂了,binlog 日志没有被写入,那么机器重启后,这台机器会通过 redo log 恢复数据,但是这个时候 binlog 并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
  • 先写 binlog,然后写 redo log,假设写完了 binlog,机器异常重启了,由于没有 redo log,本机是无法恢复这一条记录的,但是 binlog 又有记录,那么和上面同样的道理,就会产生数据不一致的情况。

如果采用 redo log 两阶段提交的方式就不一样了,写完 binglog 后,然后再提交 redo log 就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的情况呢?假设 redo log 处于预提交状态,binlog 也已经写完了,这个时候发生了异常重启会怎么样呢? 这个就要依赖于 MySQL 的处理机制了,MySQL 的处理过程如下:

  • 判断 redo log 是否完整,如果判断是完整的,就立即提交。
  • 如果 redo log 只是预提交但不是 commit 状态,这个时候就会去判断 binlog 是否完整,如果完整就提交 redo log, 不完整就回滚事务。

这样就解决了数据一致性的问题。

6、总结

  • MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
  • 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
  • 查询语句的执行流程如下:权限校验(如果命中缓存)—》查询缓存—》分析器—》优化器—》权限校验—》执行器—》引擎
  • 更新语句执行流程如下:分析器----》权限校验----》执行器—》引擎—redo log(prepare 状态)—》binlog—》redo log(commit状态)

参考资料:林晓斌《MySQL 实战45讲》


5、 mysql视图/游标是什么?

1、mysql视图:是一种虚拟存在的表(对视图的修改不影响基本表),通常是有一个表或者多个表的行或列的子集,具有和物理表相同的功能,可以对视图进行增,删,改,查等操作
作用:重用sql语句,简化复杂的sql操作,不必知道他的查询细节;保护数据,提高安全性
应用场景:1、多个地方用到同样的查询结果,该查询结果使用的sql语句较为复杂 如:查询邮箱中包含a字符的员工名、部门名和工种信息 /查询各部门的平均工资级别 /查询平均工资最低的部门信息
2、游标:是一个存储在MySQL服务器上的数据库查询,它不是一条 SELECT语句,而是被该语句检索出来的结果集。
在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改.
使用游标的原因:使用简单的 SELECT语句,例如,没有办法得到第一行、下一行或前 10行,也不存在每次一行地处理所有行的简单方法(相对于成批地处理它们)。有时,需要在检索出来的行中前进或后退一行或多行。这就是使用游标的原因

我们公司目前没有用到视图和游标。


6、MySQL怎么创建存储过程/有哪些优缺点?

1、什么是存储过程?存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合
业务逻辑可以封装存储过程中,这样不仅容易维护,而且执行效率也高。

2、好处:减少数据在数据库和应用服务器之间的传输,提高了数据处理的效率

3、MySQL存储过程

-- 创建MySQL存储过程
drop procedure if exists pr_add;  (备注:如果存在pr_add的存储过程,则先删掉
计算两个数之和(备注:实现计算两个整数之和的功能)
create procedure pr_add   (a int,b int)   
begin    declare c int;   
    if a is null then   set a = 0;     
end if;
    if b is null then   set b = 0;    
end if;    
    set c = a + b;  
select c as sum;       //存储过程默认自动提交

-- 调用 MySQL 存储过程
call pr_add(10, 20); 

4、存储过程和存储函数的特点和区别

  • 一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强。
  • 对于存储过程来说可以返回参数,而函数只能返回值或者表对象;
  • 函数必须有返回值,而过程没有;
  • 函数可以嵌入到SQL语句中执行.而过程不行;
  • 函数可以单独执行.而过程必须通过execute执行.
  • 其实我们可以将比较复杂的查询写成函数.然后到存储过程中去调用这些函数

5、存储过程与SQL的对比?

  • 存储过程只在创建时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行效率;
  • 存储过程调用时只需用提供存储过程名和必要的参数信息,从而可降低网络的流量。
  • 通过存储过程能够使没有权限的用户在控制之下间接地存取数据库,从而确保数据的安全
  • 当SQL语句有变动时,可以只修改数据库中的存储过程而不必修改代码,从而极大的提高了程序的可移植性。

6、你觉得存储过程和SQL语句该使用哪个?

  • 1、在一些高效率或者规范性要求比较高的项目,建议采用存储过程;
  • 2、对于一般项目建议采用参数化命令方式,是存储过程与SQL语句一种折中的方式;
  • 3、对于一些算法要求比较高,涉及多条数据逻辑,建议采用存储过程。

7、MySQL触发器怎么写 触发器的作用有哪些?

触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行。

CREATE TRIGGER trigger_name   //触发器名称
    trigger_time    //触发时机
    trigger_event ON tbl_name  //触发事件   表名
FOR EACH ROW
    trigger_stmt   //触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句
可以建立6种触发器 时机,即:
    BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE
create trigger tri_stuInsert after insert
    on student for each row
begin
    declare c int;
    set c = (select stuCount from class where classID = new.classID);
update class set stuCount = c + 1 where classID = new.classID;
  • 作用?
    1、触发器可通过数据库中的相关表实现级联更改;通过级联引用完整性约束可以更有效地执行这些更改;
    2、触发器可以强制比用 CHECK 约束定义的约束更为复杂的约束;
    3、触发器还可以强制执行业务规则;
    4、触发器也可以评估数据修改前后的表状态,并根据其差异采取对策。

8、 mysql中文乱码的解决方案?

解决乱码的核心思想是统一编码。utf8编码


9、如何提高MySQL的安全性

  • 1、如果 MySQL 客户端和服务器端的连接需要跨越并通过不可信任的网络,那么需要使用 ssh 隧道来加密该连接的通信;
  • 2、MySQL需要提防的攻击有,防偷听、篡改、回放、拒绝服务等,不涉及可用性和容错方面,访问控制列表的安全措施来完成;
  • 3、设置除了root用户外的其他任何用户不允许访问mysql主数据库中的user表;
  • 4、使用 grant 和 revoke 语句来进行用户访问控制的工作;
  • 5、不要使用明文密码,而是使用md5()和sha1()等单向的哈希函数来设置密码;
  • 6、服务端要对SQL进行预编译,避免SQL注入攻击,例如 where id=234,别人却输入where id=234 or 1=1。
  • 7、学会使用 tcpdump 和 strings 工具来查看传输数据的安全性,例如 tcpdump -l -i eth0 -w -src or dst port 3306 strings

10、SQL注入

SQL注入:通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句

  • 1、SQL注入攻击实例
    比如,在一个登录界面,要求输入用户名和密码,可以这样输入实现免帐号登录:用户名: ‘or 1 = 1-- 密码:然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用。
    又如:“ or “”=” 那么,拼接出的SQL字符串就变成了下面的条件,OR的存在导致输入什么名字都是符合条件的。
Select * from use_info where username = “input_usr_name” and password = “” or “” = “”    

举例:当执行的sql为 select * from user where username = “admin” or “a”=“a”时,sql语句恒成立,参数admin毫无意义。

防止sql注入的方式:

  • jdbc中,使用预编译语句:如,select * from user where username = ?,sql语句语义不会发生改变,sql语句中变量用?表示,
    即使传递参数时为“admin or ‘a’= ‘a’”,也会把这整体做一个字符创去查询。
  • Mybatis 框架中的mapper 方式中的 # (占位符)也能很大程度的防止sql注入($(拼接符)无法防止sql注入)

11、SQL之聚合函数?

聚合函数是对一组值进行计算并返回单一的值的函数,它经常与select 语句中的 group by 子句一同使用。
a. avg():返回的是指定组中的平均值,空值被忽略。
b. count():返回的是指定组中的项目个数。
c. max():返回指定数据中的最大值。
d. min():返回指定数据中的最小值。
e. sum():返回指定数据的和,只能用于数字列,空值忽略。
f. group by():对数据进行分组,对执行完group by之后的组进行聚合函数的运算,计算每一组的值。
最后用having去掉不符合条件的组,having子句中的每一个元素必须出现在select列表中(只针对于mysql)


12、SQL之连接查询(左连接和右连接的区别)

外连接:
左连接(左外连接):以左表作为基准进行查询,左表数据会全部显示出来,右表如果和左表匹配的数据则显示相应字段的数据,如果不匹配则显示为null。
右连接(右外连接):以右表作为基准进行查询,右表数据会全部显示出来,左表如果和右表匹配的数据则显示相应字段的数据,如果不匹配则显示为null。
全连接:先以左表进行左外连接,再以右表进行右外连接。
内连接:
显示表之间有连接匹配的所有行。


13、如何从一张表中查出name字段不包含“XYZ”的所有行?

使用子查询,先查出所有包含XYZ的行,再使用Not Exists字段对数据进行过滤


14、说一说drop、delete与truncate的区别(SQL中的drop、delete、truncate都表示删除)

    Delete:用来删除表的全部或者一部分数据行,执行delete之后,用户需要提交(commmit)或者回滚(rollback)来执行删除或者撤销删除, delete命令会触发这个表上所有的delete触发器
    Truncate:删除表中的所有数据,这个操作不能回滚,也不会触发这个表上的触发器,TRUNCATE比delete更快,占用的空间更小;
    Drop:从数据库中删除表,所有的数据行,索引和权限也会被删除,所有的DML触发器也不会被触发,这个命令也不能回滚
    总结:不再需要一张表的时候,用drop/想删除部分数据行时候,用delete/在保留表而删除所有数据的时候用truncate

15、mysql的广播变量 20181025 有赞

spark相关知识点,暂时用不上


16、一条SQL语句执行得很慢的原因有哪些? 20200930

一个 SQL 执行的很慢,我们要分两种情况讨论:

  • 1、大多数情况下很正常,偶尔很慢,则有如下原因
    (1)数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘。
    (2)执行的时候,遇到锁,如表锁、行锁。

  • 2、这条 SQL 语句一直执行的很慢,则有如下原因。
    (1)没有用上索引:例如该字段没有索引;由于对字段进行运算、函数操作导致无法用索引。
    (2)数据库选错了索引。

如何排查:

-- 判断是否真的在等待锁
show processlist
-- 查询索引的基数和实际是否符合,可以重新来统计索引的基数
analyze table t;

17、mysql性能调优(MySQL的优化主要涉及SQL语句及索引的优化、数据表结构的优化、系统配置的优化和硬件的优化四个方面) 好题目,加油***

  • 原则:减少系统瓶颈(CPU饱和:发生在读数据时 IO:读磁盘IO数据 服务器硬件的性能:top,free,iostat和vmstat查看系统的性能状态),减少资源占用,增加系统的反应速度
  • 索引、分库分表、批量操作、分页算法、升级硬盘SSD、业务优化、主从部署
  • 可以查看sql执行计划,找到性能瓶颈(id:查询顺序 select_type:查询类型,普通/联合/子查询 table:关于哪张表 type:使用了何种类型,全表/索引 possible_keys: key: key_len: ref: rows:估算所需读取的行数 extra:额外信息)

1、当只要一行数据时使用limit 1

  • 查询时如果已知会得到一条数据,这种情况下加上 limit 1 会增加性能。因为 mysql 数据库引擎会在找到一条结果停止搜索,而不是继续查询下一条是否符合标准直到所有记录查询完毕。

2、选择正确的数据库引擎
Mysql中有两个引擎MyISAM和InnoDB,每个引擎有利有弊。
MyISAM :适用于一些大量查询的应用,但对于有大量写功能的应用不是很好。甚至你只需要update一个字段整个表都会被锁起来。而别的进程就算是读操作也不行要等到当前update操作完成之后才能继续进行。另外,MyISAM对于select count(* )这类操作是超级快的。
InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用会比MyISAM慢,但是支持“行锁”,所以在写操作比较多的时候会比较优秀。并且,它支持很多的高级应用,例如:事务管理。

3、用not exists代替not in

  • Not exists用到了连接能够发挥已经建立好的索引的作用,not in不能使用索引。Not in是最慢的方式要同每条记录比较,在数据量比较大的操作不建议使用这种方式。

4、对操作符的优化,尽量不采用不利于索引的操作符,

  • 如:in not in is null is not null <> 等
  • 某个字段总要拿来搜索,为其建立索引: Mysql中可以利用alter table语句来为表中的字段添加索引,语法为:alter table表明add index(字段名);

18、MySQL语句优化(limit 20000 加载很慢怎么解决)(即如何发现有问题的SQL、如何分析SQL的执行计划以及如何优化SQL)

1、怎么发现有问题的sql语句?(通过MySQL慢查询日志对有性能问题的SQL进行监控)

  • MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time 值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10s以上的语句。慢查询日志的相关参数如下所示log_output:··(file|Table)通过MySQL的慢查询日志,我们可以查询出执行的次数多占用的时间长的SQL;
  • 可以通过pt_query_disgest(一种mysql慢日志分析工具)分析Rows examine(MySQL执行器需要检查的行数)项去找出IO大的SQL以及发现未命中索引的SQL,对于这些SQL,都是我们优化的对象

2、通过explain查询和分析SQL的执行计划

  • 使用 EXPLAIN 关键字可以知道MySQL是如何处理你的SQL语句的,以便分析查询语句或是表结构的性能瓶颈。通过explain命令可以得到表的读取顺序、数据读取操作的操作类型、哪些索引可以使用、哪些索引被实际使用、表之间的引用以及每张表有多少行被优化器查询等问题。

3、where子句中可以对字段进行 null 值判断吗?
最好不要给数据库留null,含有空值的列很难进行查询优化(你应该用0、一个特殊的值或者一个空串代替空值)

4、select * from admin left join log on admin.admin_id = log.admin_id where log.admin_id>10 如何优化?(政采云)

  • 可以把join优化为子查询:select * from (select * from admin where admin_id>10) T1 left join log on T1.admin_id = log.admin_id。

使用JOIN 时候,应该用小的结果驱动大的结果(left join 左边表结果尽量小,如果有条件应该放到左边先处理,right join 同理反向)同时尽量把牵涉到多表联合的查询拆分多个query。

5、limit的基数比较大时使用 between 政采云考到了哈哈,幸好我早有准备

 例如:select * from admin order by admin_id limit 100000,10 用了id主键做索引更快
- 使用order by + between and优化为:select * from admin where admin_id between 100000 and 100010 order by admin_id。(不要使用*,想查询什么数据就全部查出来)

- 也可使用子查询优化为:select id,title from collect where id >= (select id from collect order by id limit 90000,1) limit 10;        解释原因:limit 10000,10的意思是扫描满足条件的10010行,扔掉前面的10000行,返回最后20行

优化方案:

  • 1、子查询优化法

    • 先找出第一条数据,然后大于等于这条数据的id就是要获取的数据
      缺点:数据必须是连续的,可以说不能有where条件,where条件会筛选数据,导致数据失去连续性
  • 2、尽量使用复合索引

    • 对于有where条件,又想走索引用limit的,必须设计一个索引,将where放在第一位,limit用到的主键放在第2位,而且只能select主键。
  • 3、使用id限定优化

    • 这种方式假设数据表的id是连续递增的,则我们根据查询的页数和查询的记录数可以算出查询的id的范围,可以使用id between and来查询
select * from orders_history where type = 2 and id between 1000000 and 1000100 limit 100;
还有另外一种写法:
select * from orders_history where id >= 1000001 limit 100;

6、尽量避免在列上做运算,这样导致索引失效

-- 例如:
select * from admin where year(admin_time) > 2014;  
-- 优化为: 
select * from admin where admin_time > '2014-01-01′

总结:

  • 1、当一个数据库表过于庞大,limit offset,length中offset值过大,则sql查询语句会非常缓慢,你需要增加order by,并且order by字段需要建立索引
  • 2、如果使用子查询去优化limit,子查询必须是连续的,从某种意义来讲,子查询不应该有where条件,where会过滤数据,使数据失去连续性
  • 3、如果你查询的记录比较大,并且数据传输量比较大,比如包含了text类型的field,则可以通过建立子查询
SELECT id,title,content FROM items WHERE id IN (SELECT id FROM items ORDER BY id limit 900000,10)
-- 如果limit语句的offset较大,你可以通过传递pk键值来减少offset=0,这个主键最好是int型并且auto_increment
SELECT * FROM users WHERE uid > 456891 ORDER BY uid LIMIT 0, 10;

7、order by关键字优化 尽量使用index,否则会使用filesort,效率低

8、group by关键字优化 先排序后进行分组 按照索引建的最佳左前缀

9、开启慢查询日志 日志分析工具mysql dump slow

10、show profile 是mysql提供的分析当前会话中语句执行的资源消耗情况,可以用于sql调优的测量


19、在千万级的数据库查询中,如何提高效率? 面试必备

可以借助商品中心主表为例子,来分析是怎么提升效率的。

1)数据库设计方面
1、对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引
2、应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描
3、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用
4、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了insert及update的效率
5、应尽可能的避免更新索引数据列,因为索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源
*不再需要的数据行,可以设置他的status为false
6、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了
7、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为变长字段存储空间小
8、不再索引列上做任何操作(计算、函数、类型装换),会导致全表扫表

2) SQL语句方面
1、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描;
2、应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描;
3、in 和 not in 也要慎用,否则会导致全表扫描;
4、%abc 模糊查询中,%放在前面也会导致全表扫描;
如何解决?覆盖索引(是一种特殊的多列索引,当多列索引指向一个查询语句中所有的字段时,该多列索引就被称为覆盖索引,覆盖索引可以避免回表,减少树的搜索次数)
例如(身份证号,姓名)作为联合索引实现覆盖索引

在建立联合索引的时候,如何安排索引内的字段顺序?
第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的;
第二个原则:考虑的就是空间;
5、如果在 where 子句中使用参数,也会导致全表扫描;
6、应尽量避免在where子句中对字段进行函数操作;
7、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引;
8、任何地方都不要使用 select * from t ,用具体的字段列表代替"* ";

9、尽量避免向客户端返回大数据量,可以使用数据库的物理分页 可以使用mybatis的分页插件pagehelper,对SQL数据进行物理分页;
10、在查找数据时,如果是确认满足要求的数据只有一条,可以使用limit 1关键字,这样可以在查找到该数据后,不进行下一步的查找,节省效率;

案例分析
例子1:select * from T where k between 3 and 5,需要执行几次树的搜索操作,会扫描多少行?(k为普通索引,id为主键索引)
1. 在k索引树上找到k=3的记录,取得 ID = 300;
2. 再到ID索引树查到ID=300对应的R3;
3. 在k索引树取下一个值k=5,取得ID=500;
4. 再回到ID索引树查到ID=500对应的R4;
5. 在k索引树取下一个值k=6,不满足条件,循环结束 在这个过程中,回到主键索引树搜索的过程,我们称为回表。
可以看到,这个查询过程读了k索引树的3条记录(步骤1、3和5),回表了两次(步骤2和4)。
如果执行的语句是select ID from T where k between 3 and 5,这时只需要查ID的值,而ID的值已经在k索引树上了,因此可以直接提供查询结果,不需要回表。

例子2:(最左前缀法则)如果多个字段设为一个索引a,b,c,查询的时候只使用a,b来查询,这样可以吗?***
对于复合索引:MySQL从左到右使用索引中的字段,一个查询可以只使用索引中的一部分,但只能是最左侧部分。例如索引是key index(a,b,c) 可以支持a|a,b|a,b,c,3种组合进行查找,但是不支持b,c进行查找。

例子3:(索引下推)MySQL 5.6 引入的索引下推优化(index condition pushdown),可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
select * from tuser where name like ‘张%’ and age=10 and ismale=1;
并行复制功能也是5.6才引入

3) java方面:重点内容
1、尽可能的少造对象;
2、合理摆正系统设计的位置。大量数据操作,和少量数据操作一定是分开的 orm搞不定大量数据操作;
3、控制好内存,让数据流起来,而不是全部读到内存再处理,而是边读取边处理; (分页操作时要考虑)
4、合理利用缓存(mybatis的二级缓存)

4) 数据库表结构的优化(包括选择合适数据类型、表的范式的优化、表的垂直拆分和表的水平拆分等手段)

(1)选择合适数据类型
使用较小的数据类型解决问题;
使用简单的数据类型(mysql处理int要比varchar容易);
尽可能的使用not null 定义字段;
尽量避免使用text类型,非用不可时最好考虑分表;

(2)表的范式的优化
一般情况下,表的设计应该遵循三大范式

(3)表的垂直拆分(类目属性和协作属性可以这样拆分)
把含有多个列的表拆分成多个表,解决表宽度问题,具体包括以下几种拆分手段:
把不常用的字段单独放在同一个表中/把大字段独立放入一个表中/把经常使用的字段放在一起;  
这样做的好处是非常明显的,具体包括:拆分后业务清晰,拆分规则明确、系统之间整合或扩展容易、数据维护简单。

(4)表的水平拆分(mycat中讲得更清楚)
表的水平拆分用于解决数据表中数据过大的问题,水平拆分每一个表的结构都是完全一致的。一般地,将数据平分到N张表中的常用方法包括以下两种:

  • 对ID进行hash运算,如果要拆分成5个表,mod(id,5)取出0~4个值;
  • 针对不同的hashID将数据存入不同的表中;
    带来一些问题和挑战:
    包括跨分区表的数据查询、统计及后台报表的操作等问题
    可以参考下阿里drds的方案。

5) 系统配置的优化
操作系统配置的优化:增加TCP支持的队列数
mysql配置文件优化:Innodb缓存池设置(innodb_buffer_pool_size,推荐总内存的75%)和缓存池的个数(innodb_buffer_pool_instances)

6) 硬件的优化
CPU:核心数多并且主频高的
内存:增大内存
磁盘配置和选择:磁盘性能


20、线上应急响应策略(重点:得靠经验来解决)

现象排查方向故障降低措施
1 pinpoint 打点存在大批量300ms以上排查方向:检查db是否存在慢sql/定位调用链中具体接口/pinpoint inspector查看是否存在fullGC/应用dubbo线程池是否耗尽) 打点存在大批量红点(排查方向:确认红点出现源头,判断自己调用其他的业务服务是否存在超时)重启应用
2 应用 cpu突然升高排查方向:应用是否存在fullGC/检查应用流量是否突增/是否存在不合理调用(可参考监控大盘) 1、阈值标准:pinpoint最近5min出现大批量超过5s以上的打点/应用cpu 90%以上持续3min/应用内存90%以上持续3min(瞬间暴增除外)/收到上游最多两个以上业务方反馈2、解决措施:打印线程栈:jstack l <路径文件名.bin>/dump jvm堆 jmap dump:format =路径文件名.bin/重启应用:运维执行命令/jenkins下游业务服务方响应超时 sentinel consumer限流/如果存在恶意攻击是,联系安全部门拦截/如果单业务流量大,针对url或dubbo进行限流/升级应用机器配置(一般是加机器)
3 内存升的很快排查方向:1、排查是否存在新版本发布的影响;2、查询下调用频率较高的接口是否存在内存缓存的使用;3、查询下日志确定应用是否存在大批量的异常抛出2、解决措施:打印线程栈:jstack l <路径文件名.bin>/dump jvm堆 jmap dump:format =路径文件名.bin/重启应用:运维执行命令/jenkins下游业务服务方响应超时 sentinel consumer限流/如果存在恶意攻击是,联系安全部门拦截/如果单业务流量大,针对url或dubbo进行限流/升级应用机器配置(一般是加机器)
4 数据库 cpu持续90%以上10分钟ops是否明显增高(是否爬虫等异常流量)/是否存在大批量数据查询(索引/动态sql)阈值标准:数据库cpu100%持续2min且没有下降趋势/链接数一直增加没有下降的趋势,iops打满且没有下降的趋势/内存使用率80%以上持续5min,解决措施:定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题)
5 连接数线性增高且长时间不下查看是否存在慢sql/查看应用的数据库连接池配置/查看数据库中是否存在死锁 show engine innodb status;解决措施定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题)
6 iops飙高ddl操作 是否针对大表新增索引/是否存在变更大表字段,业务上是否存在大批量操作(写入或读取)解决措施定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题)
7 内存升高确认下数据库连接数是否异常(连接也占用内存)解决措施定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题)
8 中间件 MQ 消息堆积基本不需要处理
10 canal delay延时高排查方向:大批量数据操作,是否存在和其他业务共用canal,受其他业务影响阈值标准:garafa canal delay 延迟超过用户不可接受的范围

23、逻辑架构解析: (20181103)

包括:
最上层的服务不是mysql独有的,大多数基于网络的客户端/服务器的工具或服务都有类似的架构。比如连接处理,授权认证,安全等。

第二层架构:server层(连接器、查询缓存、分析器、优化器、执行器等)
mysql的核心服务功能,包括查询解析、分析、优化、缓存以及所有的内置函数(日期,时间,数学和加密函数)
所有跨存储引擎的功能都在这一层实现:存储过程,触发器,视图等
1、mysql会解析查询、并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引。
2、对于select语句,在解析查询之前,服务器会先检查查询缓存,如果能找到对应的查询,服务器就不必在执行查询解析、优化和执行的整个过程,而是直接返回查询缓存中的结果集。

第三层:存储引擎层(InnoDB、MyISAM、Memory等多个存储引擎)
存储引擎负责mysql中数据的存储和提取。服务器通过api和存储引擎进行通信。接口屏蔽了不同存储引擎间的差异。
存储引擎api包含几十个底层函数,用于执行诸如“开始一个事务”或“根据主键提取一行记录”等操作。但存储引擎不会去解析sql,
不同存储引擎之间也不会相互通信,而只是简单地响应上层服务器的请求。
1、连接器 Connectors
客户端和连接服务,主要完成一些类似于连接处理、授权认证及相关的安全方案。与其他编程语言中的SQL语句进行交互
细节:连接到数据库上后,首先就是连接器,用于跟客户端建立连接、获取权限、维持和管理连接 mysql -h i p − P ip -P ipPport -u$user -p
客户端如果太长时间没动静,连接器就会自动将它断开,这个时间是由参数wait_timeout控制的,默认是8小时
Q:长连接后,你可能会发现,有些时候MySQL占用内存涨得特别快,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是MySQL异常重启
A:两种方案
1、定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
2、如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。
2. 查询缓存 Cache&Buffers
MySQL拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中。key是查询的语句,value是查询的结果
如果查询缓存有命中的查询结果key,查询语句就可以直接去查询缓存中取数据value。这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key 缓存,权限缓存等
Q:但是大多数情况下我会建议你不要使用查询缓存,为什么呢?因为查询缓存往往弊大于利
A:缓存失效太频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。除非业务中有静态表,很长时间才更新
mysql> select SQL_CACHE * from T where ID=10;显示指定查询缓存 MYSQL8.0直接将查询缓存功能删除了
3. 分析器 Parser
作用:SQL命令传递到解析器的时候会被分析器验证和解析。 缓存失效~
主要功能:
1)词法分析 将SQL语句分解成数据结构,并将这个结构传递到后续步骤,后面SQL语句的传递和处理就是基于这个结构的;
2)语法分析 如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的,语句将不会继续执行下去 You have an error in your SQL syntax
4. 优化器 Optimizer
作用:SQL语句在查询之前会使用查询优化器对查询进行优化(索引、关联查询顺序)
5、执行器
开始执行语句,判断有没有执行查询的权限;如果有权限,就打开表继续执行,执行器就会根据表的引擎定义,去使用这个引擎提供的接口,最后将执行结果集返回给客户端。
SQL接口 SQL Interface
作用:接受用户的 SQL 命令,并且返回用户需要查询的结果

Management Services & Utilities
系统管理和控制工具
Connection Pool
连接池。作用:管理缓冲用户连接,线程处理等需要缓存的需求。
6、存储引擎 Engine
存储引擎是 MySQL 中具体的与文件打交道的子系统。也是 MySQL 最具有特色的一个地方。
MySQL 的存储引擎是插件式的。它根据 MySQL AB公司提供的文件访问层的一个抽象接口来定制一种
文件访问机制(这种访问机制就叫存储引擎)
重要的日志模块:redo log (innodb独有 大小固定)
关键点就是先写日志,再写磁盘
当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log(粉板)里面,并更新内存,这个时候更新就算完成了。
同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做.
功能:InnoDB可以用redoLog保证即使数据库发生异常重启,之前提交的记录都不会丢失,称为crash-safe
重要的日志模块:binlog(server层的日志 归档日志)
binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1”
Binlog有两种模式,statement 格式的话是记sql语句,row格式会记录行的内容,记两条,更新前和更新后都有。
更新时:引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务
执行器生成这个操作的binlog,并把binlog写入磁盘;
执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成
两阶段提交(这事务状态保持逻辑上的一致)
将redo log的写入拆成了两个步骤:prepare和commit,这就是"两阶段提交"
反证:如果不用两阶段提交,会发生什么?
1、先写redolog后写binlog
2、先写binlog后写redo log 如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致
需要搭建一些备库来增加系统的读能力的时候,现在常见的做法也是用全量备份加上应用binlog来实现的,这个“不一致”就会导致你的线上出现主从数据库不一致的情况


24、SQL Select语句完整的执行顺序: 666666666 掌握这个真的牛逼了

1、from子句 组装来自不同数据源的数据;
2、where子句 基于指定的条件对记录行进行筛选;
3、group by子句将数据划分为多个分组;
4、使用聚集函数进行计算;
5、使用having子句筛选分组;
6、计算所有的表达式;
7、select 的字段; (查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序 id同:由上至下 id不同:子查询id越大优先级越高)
8、使用order by对结果集进行排序

以上每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只有最后一步生成的表才会会给调用者。如果没有在查询中指定某一个子句,
将跳过相应的步骤
逻辑查询处理阶段简介:
1、 FROM: 对FROM子句中的前两个表执行笛卡尔积(交叉联接),生成虚拟表VT1。
2、 ON: 对VT1应用ON筛选器,只有那些使为真才被插入到TV2。
3、 OUTER (JOIN):如果指定了OUTER JOIN(相对于CROSS JOIN或INNER JOIN),保留表中未找到
匹配的行将作为外部行添加到VT2,生成TV3。如果FROM子句包含两个以上的表,则对上一个联接生成的
结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表位置。
4、 WHERE: 对TV3应用WHERE筛选器,只有使为true的行才插入TV4。
5、 GROUP BY: 按GROUP BY子句中的列列表对TV4中的行进行分组,生成TV5。
6、 CUTE|ROLLUP:把超组插入VT5,生成VT6。
7、 HAVING: 对VT6应用HAVING筛选器,只有使为true的组插入到VT7。
8、 SELECT: 处理SELECT列表,产生VT8。
9、 DISTINCT: 将重复的行从VT8中删除,产品VT9。
10、 ORDER BY: 将VT9中的行按ORDER BY子句中的列列表顺序,生成一个游标(VC10)。
11、 TOP: 从VC10的开始处选择指定数量或比例的行,生成表TV11,并返回给调用者。 where子句中的条件书写顺序
Q:select * from table t where size > 10 group by size order by size的sql语句执行顺序?
A:执行顺序将是 from table t > where size>10 > group by size > select >order by size


25、mysql存储引擎中索引的实现机制;(底层)

1、什么是索引?
索引是对数据库表中一个或多个列的值进行排序的数据结构,以协助快速查询、更新数据库表中数据。往往以索引文件的形式存储在磁盘上(索引加速了数据访问,因为存储引擎不会再去扫描整张表得到需要的数据)
通常的索引指的是B数结构组织的索引(多路搜索树)包括聚集索引,次要索引,复合索引,前缀索引,唯一索引默认使用B+数索引

2、索引的优点
优点:查找排序快、主键索引保证唯一(O(logn) 的时间复杂度)
缺点:存储空间、维护时间
使用场景:查询多、更新少、空值少的数据;
1、索引种类:
普通索引和唯一性索引:索引列的值的唯一性
唯一索引:可以保证行数据的唯一性,不允许其中任何两行具有相同索引值的索引(允许空)

create unique index indexName ON mytable(cliumnname(length));  
drop index indexname ON mytable;

单个索引和复合索引:索引列所包含的列数
单值索引:即一个索引只包含单个列,一个表可以有多个单列索引
复合索引:即一个索引包含多个列
聚簇索引与非聚簇索引:聚簇索引按照数据的物理存储进行划分的
聚集索引(主键索引):提供更快的数据访问速度(堆划分),表中行的物理顺序与键值索引顺序相同。一个表只能包含一个聚集索引
非聚集索引(非主键索引):是把一个很大的范围,转换成一个小的地图,然后你需要在这个小地图中找你要寻找的信息的位置,最后通过这个位置,再去找你所需要的记录

覆盖索引定义:如果查询条件使用的是普通索引(或是联合索引的最左原则字段),查询结果是联合索引的字段或是主键,不用回表操作,直接返回结果,减少IO磁盘读写读取正行数据
最左前缀:联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符
联合索引:根据创建联合索引的顺序,以最左原则进行where检索,比如(age,name)以age=1或age=1 and name=‘张三’可以使用索引,单以name=‘张三’ 不会使用索引,考虑到存储空间的问题,还请根据业务需求,将查找频繁的数据进行靠左创建索引。
索引下推:like 'hello%’and age >10 检索,MySQL5.6版本之前,会对匹配的数据进行回表查询。5.6版本后,会先过滤掉age<10的数据,再进行回表查询,减少回表率,提升检索速度
2、主键、自增主键、主键索引与唯一索引概念区别(按数据的逻辑进行划分)
主键:指字段唯一、不为空值的列(主键是一种特殊的唯一性索引,其可以是聚集索引,也可以是非聚集索引) 对于没有主键的表,innodb会默认创建一个Rowid做主键
主键索引:要求主键值唯一,创建主键的时候,数据库默认会为主键创建一个唯一索引(叶子节点存的是整行数据)
非主键索引:叶子节点内容是主键的值(二级索引)
自增主键:字段类型为数字、自增、并且是主键;
唯一索引:索引列的值必须唯一,但允许有空值。主键是唯一索引,这样说没错;但反过来说,唯一索引也是主键就错误了,因为唯一索引允许空值,主键不允许有空值,所以不能说唯一索引也是主键
Q:基于主键索引和普通索引的查询有什么区别?
A:如果语句是select * from T where ID=500,即主键查询方式,则只需要搜索ID这棵B+树;如果语句是select * from T where k=5,即普通索引查询方式,则需要先搜索k索引树,得到ID的值为500,再到ID索引树搜索一次。这个过程称为回表。也就是说,基于非主键索引的查询需要多扫描一棵索引树。
Q: 重建普通索引k和重建主键索引id,对于两个重建索引的做法,说出你的理解?
A: 如果删除,新建主键索引,会同时去修改普通索引对应的主键索引,性能消耗比较大。删除重建普通索引貌似影响不大,不过要注意在业务低谷期操作,避免影响业务。可以使用sql语句alter table T engine = InnoDB替代重建过程。
Q: “N叉树”的N值在MySQL中是可以被人工调整的么?
A: 可以按照调整key的大小的思路来说:如果你能指出来5.6以后可以通过page大小来间接控制应该能加分吧,面试回答不能太精减计算方法、前缀索引什么的一起上。


26、哪些情况需要创建索引?

1、主键自动建立唯一索引
2、频繁作为查询条件的字段(经常出现在order by, group by, distinct后面的字段)
3、查询中与其它表关联的字段,外键关系建立索引
4、单键/组合索引选择? 高并发下选择组合索引
5、查询中排序的字段,统计,分组的字段
不适合:
1、频繁增删改的字段不适合
2、where条件里用不到的不创建
3、表记录太少时
4、字段重复数据太多时,如性别表


27、mysql索引的实现机制有三种:(哈希表/有序数组/搜索树)**

1、hash索引:给表的某一列计算hash值,排序在hash数组上,hash索引可以一次定位,效率高(等值比较,适用于redis/memcached)
缺点:因为递增主键的存储位置不是连续的,所以哈希索引做区间查询的速度很慢。

2、有序数组索引:往中间插入一个记录就必须得挪动后面所有的记录,成本太高,只适用于静态存储引擎(通常不怎么变更的数据)

3、btree索引:是以B+树为存储结构实现的,B+树是一个平衡的多叉树,从根节点到每个叶子结点的高度差值不超过1,而且同层级的节点间有指针相互连接。基于索引的顺序扫描时,可以利用双向指针快速左右移动,效率非常高,用于数据库,文件系统中。

3、hash和btree区别:
1、hash索引比较的是经过hash计算的值,所以只能进行等值比较,不能用于范围查询;***
2、hash值映射的真正数据在hash表中就不一定按照顺序排序,所以无法利用hash索引来加速任何排序操作。以及like “xx%”这样的部分模糊查询。
3、hash索引也不支持多列联合索引
无论是二叉搜索树还是AVL树,当数据量比较大时,都会由于树的深度过大而造成I/O读写过于频繁,进而导致查询效率低下,因此对于索引而言,多叉树结构成为不二选择。随着数据库技术的发展,跳表、LSM树等数据结构也被用于引擎设计中(数据库底层存储的核心就是基于这些数据模型)


28、聚集索引与非聚集索引区别?20181222 需要补充

聚簇索引,在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引, 即如果存在聚集索引,就不能再指定CLUSTERED 关键字
非簇索引,在非聚集索引中,数据库表中记录的物理顺序与索引顺序可以不相同。一个表中只能有一个聚集索引,但表中的每一列都可以有自己的非聚集索引


29、B树和B+树特点

B树:多路平衡查找树(结点的最大值m称为B_TREE的阶,简称为m叉树)
特点:平衡(叶子节点位于同一层上,到达任意叶子节点的搜索代价相同)、层少、升序
树中每个结点最多有m个孩子结点/若根结点不是叶子节点,则根结点至少有2个孩子结点/除根结点外,其它结点至少有(m/2的上界)个孩子结点
结构:父节点、子节点指针数组、key信息数组(查找值或折半)
n c0 d1 c1 d2 c2 … dn cn 有序//n为结点中关键字个数/di(1<=i<=n)为该结点的n个关键字值的第i个/ci(0<=i<=n)为该结点孩子结点的指针
所有的叶结点都在同一层上,并且不带信息(说明节点不存在)
优点:由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近(文件系统和数据库)

B+树(innoDB存储引擎使用的索引结构)
介绍:在InnoDB中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。InnoDB使用了B+树索引模型,所以数据都是存储在B+树中的
结构区别:
非叶节点仅含有其子树根结点中最大(或最小)关键码、叶子节点保存key数组、有序链表(依关键码的大小自小而大的顺序链接)
所有数据地址必须要到叶子节点才能获取到,所以每次数据查询的次数都一样(除了非终端结点上的关键码等于给定值这种情况)
通常在B+树上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点。
因此可以对B+树进行两种查找运算:一种是从最小关键字起顺序查找,另一种是从根节点开始,进行随机查找
性能区别:IO少、平衡、范围查找,每一个关键字的查询效率相当
B+与普通二叉树比较 IO少:
层数少、层间局部性原理、顺序读写
缺点:(索引维护)
会产生大量的随机IO,随着新数据的插入,按照B+Tree算法,新增加一个数据页,叶子节点会慢慢分裂,逻辑上连续的叶子节点在物理上往往不连续.

解决方案:
1、LSM树 HBase使用LSM树(Log-Structured Merge Tree),同样支持增删读改、顺序扫描操作,而且通过批量存储技术规避磁盘随机写入的问题,LSM树牺牲了部分读性能,用来大幅提高写性能。
LSM设计思想:将对数据的修改增量保持在内存中,达到指定大小限制后将这些修改操作批量写入磁盘。把一棵大树拆分成N棵小树,首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。
2、使用自增主键(从性能和存储空间方面考量)
每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂;有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高


30、数据库索引的存储结构一般是B+树,为什么不适用红黑树等普通的二叉树

1、局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。程序运行期间所需要的数据通常比较集中

  • 1、磁盘会顺序预读 页的整数倍的数据到内存中,减少磁盘的IO次数
  • 2、磁盘顺序读取的效率很高

2、B树的节点大小设置为一页(4k),减少层间索引次数,效率较高;
一次检索最多需要h-1次I/O,渐进复杂度为O(h)=O(logdN)
实际应用中,出度d是非常大的数字,通常超过100,因此h非常小
3、红黑树等高度较高,索引次数较多,即磁盘IO次数较多,性能较慢
4、B+树所有的关键字都出现在叶子节点,内存中可以存入更多的关键字,减少磁盘I/O次数
5、B+树叶子节点通过链表相连,链表中的关键字是有序的,适合范围查找(在数据库中基于范围的查询是非常频繁的,而B树只能中序遍历所有节点,效率太低)


31、B树中插入/删除关键码

对高度为h的m阶B树,新结点一般是插在第h层

  • 1、若该结点中关键码个数小于m-1,则直接插入;
  • 2、若该结点中关键码个数等于m-1,则将引起结点的分裂。以中间关键码为界将结点一分为二,产生一个新结点,并把中间关键码插入到父结点(h-1层)中。
    删除(delete)操作
    判断是否存在:首先查找B树中需删除的元素,如果该元素在B树中存在,则将该元素在其结点中进行删除
    判断是否有左右孩子:如果有,则上移孩子结点中最相近元素(“左孩子最右边的节点”或“右孩子最左边的节点”)到父节点中;如果没有,直接删除

32、什么情况下设置了索引但无法使用?

以“%(表示任意0个或多个字符)”开头的LIKE语句,模糊匹配;
OR语句前后没有同时使用索引
数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型)
对于多列索引,必须满足最左匹配原则(eg:多列索引col1、col2和col3,则索引生效的情形包括 col1或col1,col2或col1,col2,col3)


33、什么样的字段适合创建索引?

经常作查询选择的字段
经常作表连接的字段
经常出现在order by, group by, distinct后面的字段


34、创建索引时需要注意什么?

非空字段:应该指定列为NOT NULL,含有空值的列很难进行查询优化(你应该用0、一个特殊的值或者一个空串代替空值)
取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面
索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高


35、MySQL存储引擎InnoDB、Mysaim的特点?Mysql数据库架构图?锁机制

1、Mysql存储引擎有哪些?(show engines来查看mysql支持的存储引擎)

  • 1、InnoDB存储引擎
    首选 支持事务安全表(ACID),支持行锁定和外键
  • 2、MyISAM存储引擎(MySQL 5.5之前)
    它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事务。
  • 3、MEMORY存储引擎
    将表中的数据存储到内存中,不支持事务

2、InnoDB、Mysaim的特点和区别?

  • 1、MyISAM存储引擎
    不支持事务、表锁和全文索引
    对于一些 OLAP(联机分析处理)系统,操作速度快
    MyISAM不缓存数据文件,只缓存索引文件。
  • 2、InnoDB存储引擎(可能出现死锁)(最大的区别:innodb支持事务,采用了行级锁)
    支持事务,主要面向 OLTP(联机事务处理过程)方面的应用
    是行锁设置、支持外键,并支持类似于Oracle的非锁定读,即默认情况下读不产生锁
    InnoDB通过多版本并发控制(MVCC)来获得高并发性
    默认隔离级别是repeatable,使用一种被称为next-key locking的策略避免幻读

3、MyISAM与InnoDB区别?

  • 1、存储结构:每个MyISAM在磁盘上存储成三个文件(.frm文件存储表定义,数据文件的扩展名为.MYD(MYData),索引文件的扩展名是.MYI(MYIndex));InnoDB所有的表都保存在同一个数据文件中
  • 2、存储空间:MyISAM可被压缩,占据的存储空间较小,支持静态表、动态表、压缩表三种不同的存储格式。InnoDB需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引
  • 3、可移植性/备份及恢复:MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便,同时在备份和恢复时也可单独针对某个表进行操作;InnoDB的方案可以是拷贝数据文件、备份binlog,或者用mysqldump
  • 4、事务支持:MyISAM强调的是性能,每次查询具有原子性,其执行速度比InnoDB类型更快,但是不提供事务支持。InnoDB提供事务、外键等高级数据库功能,具有事务提交、回滚和崩溃修复能力
  • 5、AUTO_INCREMENT:在MyISAM中,可以和其他字段一起建立联合索引。引擎的自动增长列必须是索引,如果是组合索引,自动增长可以不是第一列,它可以根据前面几列进行排序后递增。InnoDB中必须包含只有该字段的索引,并且引擎的自动增长列必须是索引,如果是组合索引也必须是组合索引的第一列
  • 6、表锁差异:MyISAM只支持表级锁,用户在操作MyISAM表时,select、update、delete和insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。InnoDB支持事务和行级锁。行锁大幅度提高了多用户并发操作的新能,但是InnoDB的行锁,只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的
  • 7、全文索引:MyISAM支持FULLTEXT类型的全文索引;InnoDB不支持FULLTEXT类型的全文索引,但是innodb可以使用sphinx插件支持全文索引,并且效果更好
  • 8、表主键:MyISAM允许没有任何索引和主键的表存在,索引都是保存行的地址。对于InnoDB,如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值
  • 9、表的具体行数:MyISAM保存表的总行数,select count() from table;会直接取出出该值;而InnoDB没有保存表的总行数,如果使用select count() from table;就会遍历整个表,消耗相当大
  • 10、CURD操作:在MyISAM中,如果执行大量的SELECT,MyISAM是更好的选择;对于InnoDB,如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表;DELETE从性能上InnoDB更优(truncate table)
  • 11、外键:MyISAM不支持外键,而InnoDB支持外键

37、数据库事务(事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态) 20200930补

1、什么是事务?(引擎层)

  • 事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的,MySQL原生的MyISAM引擎就不支持事务,这也是MyISAM被InnoDB取代的重要原因之一

举例如下:转账操作。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。

1、MySQL事务特性(数据库事务transanction正确执行的四个基本要素)ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)

  • 1、原子性:整个事务中的所有操作,要么全部完成,要么全部不完成
  • 2、一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏,在innodb引擎中由redo log保证
  • 3、隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作 由MCVV实现
  • 4、持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚

2、事务并发带来的问题

  • 脏读:一个事务读取了另一个事务未提交的数据;
  • 不可重复读:不可重复读的重点是修改,同样条件下两次读取结果不同,也就是说,被读取的数据可以被其它事务修改;
  • 幻读:幻读的重点在于新增或者删除,同样条件下两次读出来的记录数不一样

不可重复度和幻读区别:

  • 不可重复读的重点是修改,幻读的重点在于新增或者删除。

  • 例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。

  • 例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。

3、隔离级别是什么?有什么作用?

  • 什么是隔离级别?数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题
    作用:如果没有采取必要的隔离级别,会导致各种并发问题(脏读:读了没有被提交的数据,不可重复读:读取的数据某些字段被更新了,幻读:读取数据的表被增加/减少了数据)

  • 隔离级别的种类(以Innodb为例:基于MVCC和锁)

  • 读未提交(READ UNCOMMITTED):就是事务可以读取其它事务未提交的数据。 //脏读,不可重复读,幻读都会出现

  • 读已提交(READ COMMITTED):允许事务读取已经被其他事务提交的变更数据 //sqlserver oracle默认 我司使用该级别 可避免脏读,但不可重复读,幻读都会出现

  • 可重复读(REPEATABLE READ):(mysql默认)保证同一个事务中的多次相同的查询的结果是一致的,在事务期间,禁止其他事务对这个字段进行更新 //可避免脏读和不可重复读,但幻读仍然存在 (mysql的间隙锁已解决了幻读的问题)
    解决方法:读取数据加行锁
    使用场景:银行账单的数据校对

  • 串行化(SERIALIZABLE):在事务期间,禁止其他事务对这个字段进行插、删、改操作 //所有并发问题都可以解决
    方法:事务串行化顺序执行,读用共享读锁,写用排他写锁,读锁和写锁互斥,若果使用where语句,还会获取间隙锁

隔离级别脏读不可重复读幻影读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE×××
  • MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过SELECT @@tx_isolation;命令来查看,MySQL 8.0 该命令改为SELECT @@transaction_isolation;

  • 与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下,允许应用使用 Next-Key Lock 锁算法来避免幻读的产生。这与其他数据库系统(如 SQL Server)是不同的。所以说虽然 InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读),但是可以通过应用加锁读(例如 select * from table for update 语句)来保证不会产生幻读,而这个加锁度使用到的机制就是 Next-Key Lock 锁算法。从而达到了 SQL 标准的 SERIALIZABLE(可串行化) 隔离级别。

  • 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容)😗*,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)并不会有任何性能损失。

  • InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。

4、实战演示脏读/不可重复读/幻读

使用 2 个命令行mysql ,模拟多线程(多事务)对同一份数据的脏读问题。

MySQL 命令行的默认配置中事务都是自动提交的,即执行SQL语句后就会马上执行 COMMIT 操作。如果要显式地开启一个事务需要使用命令:START TARNSACTION。

我们可以通过下面的命令来设置隔离级别。

SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]

我们再来看一下我们在下面实际操作中使用到的一些并发控制语句:

  • START TARNSACTION |BEGIN:显式地开启一个事务。
  • COMMIT:提交事务,使得对数据库做的所有修改成为永久性。
  • ROLLBACK:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。

脏读(读未提交隔离级别)
在这里插入图片描述

避免脏读(读已提交隔离级别)
在这里插入图片描述

不可重复读

还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题
在这里插入图片描述

可重复读
在这里插入图片描述

防止幻读(可重复读)
在这里插入图片描述
一个事务对数据库进行操作,这种操作的范围是数据库的全部行,然后第二个事务也在对这个数据库操作,这种操作可以是插入一行记录或删除一行记录,那么第一个是事务就会觉得自己出现了幻觉,怎么还有没有处理的记录呢? 或者 怎么多处理了一行记录呢?

幻读和不可重复读有些相似之处 ,但是不可重复读的重点是修改,幻读的重点在于新增或者删除
参考资料:《MySQL技术内幕:InnoDB存储引擎》

5、InnoDB实现事务原理

事务的ACID是通过InnoDB日志和锁来保证。
事务的隔离性是通过数据库锁的机制实现的,持久性通过redo log(重做日志)来实现,原子性和一致性通过Undo log来实现。UndoLog的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为UndoLog)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。
和Undo Log相反,RedoLog记录的是新数据的备份。在事务提交前,只要将RedoLog持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是RedoLog已经持久化。系统可以根据RedoLog的内容,将所有数据恢复到最新的状态

6、分布式事务

什么是分布式事务?

  • 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

分布式事务产生的原因?
从上面本地事务来看,我们可以看为两块,一个是service产生多个节点,另一个是resource产生多个节点

  • service多个节点
    在我们团队,商品的数据分为好多个部分,比如商品详情,商品属性,商品协议数据等等。在公司内部商品详情,商品属性功能由一个微服务团队维护,商品协议数据又是另外的团队维护。
    在这里插入图片描述
  • resource多个节点(由drds保证)
    Mysql一般来说装千万级的数据就得进行分库分表,对于一个支付宝的转账业务来说,你给的朋友转钱,有可能你的数据库是在北京,而你的朋友的钱是存在上海,所以我们依然无法保证他们能同时成功。
    在这里插入图片描述

分布式事务解决方案
太多团队一个人维护几个微服务,太多团队过度设计,搞得所有人疲劳不堪,而微服务过多就会引出分布式事务,这个时候我不会建议你去采用下面任何一种方案,而是请把需要事务的微服务聚合成一个单机服务,使用数据库的本地事务。因为不论任何一种方案都会增加你系统的复杂度,这样的成本实在是太高了,千万不要因为追求某些设计,而引入不必要的成本和复杂度。

  • 例如:如果商品中心往标准中心里面写数据,就会产生分布式事务的问题

  • 第二个案例:用户成为正式供应商后,发消息通知

  • 2PC
    在这里插入图片描述

TCC
在这里插入图片描述

本地消息表
在这里插入图片描述

MQ事务
在这里插入图片描述

结论:能不用分布式事务就不用,如果非得使用的话,结合自己的业务分析,看看自己的业务比较适合哪一种,是在乎强一致,还是最终一致即可。上面对解决方案只是一些简单介绍,如果真正的想要落地,其实每种方案需要思考的地方都非常多,复杂度都比较大,所以最后再次提醒一定要判断好是否使用分布式事务。


38、CAP、base理论及其应用

1、CAP定理,又被叫作布鲁尔定理。对于设计分布式系统来说(不仅仅是分布式事务)的架构师来说,CAP就是你的入门理论.
CAP理论中C是consistency强一致性,对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据上来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
A是availablity可用性,非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的,这里的正确指的是比如应该返回50,而不是返回40。
P是partition tolerance分区容错性 当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。

  • CAP理论就是说在分布式存储系统中,最多只能实现上面的两点,我们选择了CA而放弃了P,那么当发生分区现象时,为了保证一致性,这个时候必须拒绝请求,但是A又不允许,所以分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。(AP是大多数网站架构的选择)(分区容错性必须得实现
  • CP:redis、mongoDB、zookeeper
  • AP:各类分布式系统

2、数据库的写实时性和读实时性需求(数据库事务一致性需求)

  • 对关系数据库来说,插入一条数据之后立刻查询,是肯定可以读出来这条数据的,但是对于很多web应用来说,并不要求这么高的实时性,比方说发一条消息之后,过几秒乃至十几秒之后,我的订阅者才看到这条动态是完全可以接受的

3、对复杂的SQL查询,特别是多表关联查询的需求?

  • 任何大数据量的web系统,都非常忌讳多个大表的关联查询,以及复杂的数据分析类型的报表查询,特别是SNS类型的网站,从需求以及产品设计角度,就避免了这种情况的产生。往往更多的只是单表的主键查询,以及单表的简单条件分页查询,SQL的功能被极大的弱化了。

4、BASE是为了解决关系数据库强一致性引起的问题而引起的可用性降低而提出的解决方案
指标:基本可用(Basically Available)软状态(Soft state)最终一致(Eventually consistent)

  • 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
  • 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。
  • 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。

它的思想是通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上改观。为什么这么说呢,缘由就在于大型系统往往由于地域分布和极高性能的要求,不可能采用分布式事务来完成这些指标。

5、分布式和集群

  • 分布式:不同服务器部署不同的工程,他们之间通过rpc/rmi通信和调用,对外提供服务
  • 集群:不同服务器部署相同的服务模块,通过分布式调度软件进行统一的调度

39、对于一些从Oracle迁移到MySQL的应用,为保证数据库隔离级别的一致,你一定要记得将MySQL的隔离级别设置为“读提交”

方法:将启动参数tx-isolation的值设置成READ-COMMITTED。你可以用show variables来查看当前的值
mysql5.7引入了transaction_isolation用来替换tx_isolation(我司使用该语法),到8.0.3就去除掉后者了。show variables like ‘transaction_isolation’;


39、隔离级别的实现原理MVCC:以“可重复读”为例

1、在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值

        回滚日志                        最新值
  readviewA    readviewB             readviewC        
  将2改为1<-- 将3改为2 <--将4改为3<--当前值4     (同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC))
  对于read-viewA,要得到1,就必须将当前值依次执行图中所有的回滚操作得到

2、什么时候删除?
在不需要的时候才删除,就是当系统里没有比这个回滚日志更早的read-view的时候
注意:
-1、尽量不要使用长事务,长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

  • 2、在MySQL5.5及以前的版本,回滚日志是跟数据字典一起放在ibdata文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有20GB,而回滚段有200GB的库。最终只好为了清理回滚段,重建整个库。

40、事务的启动方式

  • 1、显示启动事务语句,begin或start transaction,配套的提交语句时commit,回滚语句时rollback;
  • 2、set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个select语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行commit或rollback语句,或者断开连接。

注意:可以在information_schema库的innodb_trx这个表中查询长事务,比如下面这个语句,用于查找持续时间超过60s的事务

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;

42、系统里面应该避免长事务,如果你是业务开发负责人同时也是数据库负责人,你会有什么方案来避免出现或者处理这种情况呢?

在开发过程中,尽可能的减小事务范围,少用长事务,如果无法避免,保证逻辑日志空间足够用,并且支持动态日志空间增长。监控Innodb_trx表,发现长事务报警。


43、我们知道:innodb与myisam的区别之一就是对事务的支持方面,innodb通过MVCC机制支持事务的回滚操作,那么能让myisam也支持事务吗?

事务的四个特点:ACID里面, 原子性和持久性做不到,隔离性只能实现基本用不上的串行化(在异常崩溃时也无法保证) 这样的事务不要也罢


44、mysql的锁机制? ***重难点 20201006 补充

我们学习了数据库的事务及其事务的隔离级别,但是数据库是怎样隔离事务的呢?这时候就牵连到了数据库锁。当插入数据时,就锁定表,这叫做”锁表”;当更新数据时,就锁定行,这叫做”锁行”。当多个用户对数据库进行操作时,会带来数据不一致的情况,所以,锁主要是在多用户情况下保证数据库数据完整性和一致性。

1、锁的分类

锁模式分类乐观锁、悲观锁
范围锁行锁、表锁
算法锁临间锁、间隙锁、记录锁
属性锁共享锁(S)、排他锁(X)
状态锁意向共享锁、意向排他锁

1、从对数据库事务操作的粒度来划分;
锁的粒度主要有以下几种类型–》 都是悲观锁

  • 1、行锁:索引项加锁,粒度最小,并发性最高
  • 2、页锁:一次锁定一页。25个行锁可升级为一个页锁。
  • 3、表锁:粒度大,并发性低
  • 4、数据库锁:控制整个数据库操作(修改数据库字段会触发)

2、从对数据操作的类型(读/写)来划分;

  • 1、读锁(共享锁): 针对同一份数据,多个读操作可以同时进行而不会互相影响
  • 2、写锁(排它锁):当前写操作没完成前,他会阻断其他写锁和读锁

总结:Mysql中的锁机制基本上都是采用的悲观锁来实现的。

3、行锁实现
行锁就是一锁锁一行或者多行记录,mysql的行锁是基于索引加载的,所以行锁是要加在索引响应的行上,即命中索引
在这里插入图片描述

如上图所示,数据库表中有一个主键索引和一个普通索引,Sql语句基于索引查询,命中两条记录。此时行锁一锁就锁定两条记录,当其他事务访问数据库同一张表时,被锁定的记录不能被访问,其他的记录都可以访问到。

行锁的特征:锁冲突概率低,并发性高,但是会有死锁的情况出现。
在这里插入图片描述

窗口A先修改了id为3的用户信息后,还没有提交事务,此时窗口B再更新同一条记录,然后就提示Lock wait timeout exceeded; try restarting transaction ,由于窗口A迟迟没有提交事务,导致锁一直没有释放,就出现了锁冲突,而窗口B一直在等待锁,所以出现了超过锁定超时的警告了。

2、表锁实现
表锁就是一锁锁一整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作。表锁响应的是非索引字段,即全表扫描,全表扫描时锁定整张表,sql语句可以通过执行计划看出扫描了多少条记录。
在这里插入图片描述

3、在mysql中,行锁又衍生了其他几种算法锁,分别是 记录锁、间隙锁、临键锁;我们依次来看看这三种锁,什么是记录锁呢

3.1 记录锁
上面我们找到行锁是命中索引,一锁锁的是一张表的一条记录或者是多条记录,记录锁是在行锁上衍生的锁,记录锁的特征:

记录锁:记录锁锁的是表中的某一条记录,记录锁的出现条件必须是精准命中索引并且索引是唯一索引,如主键id。

3.2 间隙锁
间隙锁又称之为区间锁,每次锁定都是锁定一个区间,隶属行锁。既然间隙锁隶属行锁,那么,间隙锁的触发条件必然是命中索引的,当我们查询数据用范围查询而不是相等条件查询时,查询条件命中索引,并且没有查询到符合条件的记录,此时就会将查询条件中的范围数据进行锁定(即使是范围库中不存在的数据也会被锁定),我们通过代码演示一下:

首先,我们打开两个窗口,在窗口A中我们根据id做一个范围更改操作,不提交事务,然后在范围B中插入一条记录,该记录的id值位于窗口A中的条件范围内,我们看看运行效果:
在这里插入图片描述

程序报错:Lock wait timeout exceeded; try restarting transaction 。这就是间隙锁的作用。间隙锁只会出现在可重复读的事务隔离级别中,mysql5.7默认就是可重复读。间隙锁锁的是一个区间范围,查询命中索引但是没有匹配到相关记录时,锁定的是查询的这个区间范围,上述代码中,所锁定的区间就是 (1,3]这个区间,不包含1,但是包含3,并且不包含4,也就是说这里是一个左开右闭的区间。
如果将隔离级别改为 读已提交,测试间隙锁,会发现间隙锁没有生效。

设置事务隔离级别为不可重复读
set session transaction isolation level read committed;
查看当前事务级别
SELECT @@tx_isolation

3.3 临键锁
mysql的行锁默认就是使用的临键锁,临键锁是由记录锁和间隙锁共同实现的,上面我们学习间隙锁时,间隙锁的触发条件是命中索引,范围查询没有匹配到相关记录。而临键锁恰好相反,临键锁的触发条件也是查询条件命中索引,不过,临键锁有匹配到数据库记录
间隙锁所锁定的区间是一个左开右闭的集合,而临键锁锁定是当前记录的区间和下一个记录的区间。
在这里插入图片描述

从上图我们可以看到,数据库中只有三条数据1、5、7,当修改范围为1~8时,则锁定的区间为(1,+∞),锁定额不单是查询范围,并且还锁定了当前范围的下一个范围区间,此时,查询的区间8,在数据库中是一个不存在的记录值,并且,如果此时的查询条件是小于或等于8,也是一样的锁定8到后面的区间。

如果查询的结尾是一个存在的值,此时又会怎样呢?现在数据库有三条数据id分别是1、5、7,我们查询条件改为大于1小于7再看看。
在这里插入图片描述

我们可以看到,由于7在数据库中是已知的记录,所以此时的锁定后,只锁定了(1,7],7之后的数据都没有被锁定。我们还是可以正常插入id为8的数据及其后面的数据。所以,临键锁锁定区间和查询范围后匹配值很重要,如果后匹配值存在,则只锁定查询区间,否则锁定查询区间和后匹配值与它的下一个值的区间

3.4 为什么会出现这种情况呢?为什么临键锁后匹配会这样呢?
在这里,我们不妨看看mysql的索引是怎么实现的,前面文章中有提到树结构,mysql的索引是基于B+树实现的,每个树节点上都有多个元素,即关键字数,当我们的索引树上只有1、5、7时,我们查询1~8,这个时候由于树节点关键字中并没有8,所以就把8到正无穷的区间范围都给锁定了。
那么,如果我们数据库中id有1、5、7、10,此时我们再模糊匹配id为1~8的时候,由于关键字中并没有8,所以找比8大的,也就找到了10,根据左开右闭原则,此时10也是被锁定的,但是id为11的记录还是可以正常进行插入的。

2、怎么判断是行锁还是表锁? 20181016 有赞

3、乐观锁,悲观锁
乐观锁和悲观锁的区别?不是mysql或数据库中独有的概念,而是并发编程的基本概念
悲观锁(synchronized):每次拿数据的时候都觉得数据会被人更改,所以拿数据的时候就把这条记录锁掉,这样别人就没法改这条数据了,一直到你的锁释放

关系型数据库,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。当数据库执行select…for update时会获取被select中的数据行的行锁,因此其他并发执行的select…for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果
问题是: select… for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题

乐观锁:MVCC
即很乐观,查询数据的时候总觉得不会有人更改数据,等到更新的时候再判断这个数据有没有被人更改,有人更改了则本次更新失败

  • 实现了乐观锁的方式:记录数据版本或者时间戳
 SELECT data AS old_data, version AS old_version FROM …;
//根据获取的数据进行业务操作,得到new_data和new_version
UPDATE SET data = new_data, version = new_version WHERE version = old_version
if (updated row > 0) {
    // 乐观锁获取成功,操作完成
} else {
    // 乐观锁获取失败,回滚并重试}
  • 底层机制是这样:
    在数据库内部update同一行的时候是不允许并发的,即数据库每次执行一条update语句时会获取被update行的写锁,直到这一行被成功更新后才释放。
    因此在业务操作进行前获取需要锁的数据的当前版本号,然后实际更新数据时再次对比版本号确认与之前获取的相同,并更新版本号,即可确认这其间没有发生并发的修改。
    如果更新失败,即可认为老版本的数据已经被并发修改掉而不存在了,此时认为获取锁失败,需要回滚整个业务操作并可根据需要重试整个过程

悲观锁与乐观锁的应用场景

  • 1、读多写少更适合用乐观锁,读少写多更适合用悲观锁。乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能
  • 2、火车余票查询和购票系统:同时查询的人可能很多,虽然具体座位票只能是卖给一个人,但余票可能很多,而且也并不能预测哪个查询者会购票,这个时候就更适合用乐观锁

数据库衍生的职位:
数据库应用工程师,很多业务开发者就是这种定位,综合利用数据库和其他编程语言等技能,开发业务应用 java相关
数据库工程师,更加侧重于开发数据库、数据库中间件等基础软件 java相关
数据库管理员(DBA),这是一个单独的专业领域
java与数据库交互的技术:JDBC JPA/Hibernate MyBatis、SpringJDBC Template


45、mysql遇到的死锁问题?面试必备 发生在innobd引擎中(高并发时,发生在同一个事务中先delete(获取间隙锁)再insert的情况,把多行数据锁定了,同时获取了数据段的共享锁)

  • 间隙锁的问题:为了解决innodb引擎的幻读问题
  • 什么是间隙锁?
    innodb中行锁的一种, 但是这种锁锁住的却不止一行数据,他锁住的是多行,是一个数据范围,主要作用是为了防止出现幻读,但是它会把锁定范围扩大
  • 怎么解决?
    修改代码逻辑,数据存在才删除,尽量不去删除不存在的记录。

46、面试题:如何锁定一行?描述:高并发下,某线程select了一条记录但还没来得及update时,另一个线程仍然可能会进来select到同一条记录****

一般解决办法就是使用锁和事务的联合机制

  • 1.把select放在事务中,否则select完成, 锁就释放了
  • 2.要阻止另一个select,则要手工加锁, select默认是共享锁, select之间的共享锁是不冲突的, 所以, 如果只是共享锁, 即使锁没有释放, 另一个select一样可以下共享锁, 从而select出数据

47、对锁的优化建议?

1、尽可能让所有数据检索都通过索引来完成,避免无缩影行锁升级为表锁
2、合理设计索引,尽量缩小锁的范围
3、尽可能减小检索条件,避免间隙锁
4、尽量控制事务大小,减少锁定资源量和时间长度


48、分布式事务解密?

1、什么是分布式事务?
一次大的操作由不同的小操作组成,这些小操作分布在不同的服务器上,属于不同的应用。分布式事务是为了保障不同数据库的数据一致性。
2、分布式事务产生的原因?

  • 1、数据库的分库分表;
  • 2、网站的SOA化,例如同时对订单和库存进行操作,就涉及到订单数据库和库存数据库。

3、MySQL事务特性
事务:事务就是一组原子性的SQL查询,或者说一个独立的工作单元。
数据库事务transanction正确执行的四个基本要素。ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)

  • 1、原子性:整个事务中的所有操作,要么全部完成,要么全部不完成;
  • 2、一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏;
  • 3、隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作;
  • 4、持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

4、分布式事务在电商系统中的应用场景

  • 1、支付:对买家进行扣款,同时对卖家进行加钱,卖家和买家不在同一个数据库中;
  • 2、下单:下单时会发生扣库存和更新订单状态两个动作,分属于不同数据库;
  • 3、店铺创建:店铺创建之后需要初始化仓库,店铺表和仓库表在不同的库;
  • 4、商品审核:商品表和审核详情表分属不同的库。

5、分布式事务解决方案

  • 1、基于XA的两阶段提交(淘汰)
    XA协议分为两部分:事务管理器和资源管理器(本地),事务管理器是全局的调度者,负责各个本地资源的提交和回滚;
    第一阶段:执行事务1和事务2
    第二阶段:提交事务1和事务2
    虽然没有彻底解决数据库的一致性问题,但是不一致出现的概率大大降低了。
    缺点:数据库连接占用的时间增加了,系统整体的吞吐量降低了。无法满足高并发场景,XA在mysql中支持不够
  • 2、消息事务+最终一致性(RocketMQ
    消息事务是基于消息中间件的两阶段提交,将本地事务和发消息放在一个分布式事务里,要么本地操作成功并发消息,要么都失败。
    实现原理:
    1、A系统向消息中间件发送一条预备消息 //出错则事务失败
    2、消息中间件保存预备消息并返回成功 //出错则事务失败
    3、A执行本地事务 //出错会回滚预备消息,怎么回滚,A系统实现了一个消息中间件的回调接口,MQ会不断执行回调接口
    4、A发送提交消息给消息中间件 //出错不需要回滚,MQ会自己对消息进行提交,从而完成整个消息事务
    好处:基于MQ的两阶段提价用于高并发的场景,分布式事务被拆解为一个消息事务(A系统的本地操作+发消息),一个B系统的本地操作,消息一定会发送出来,如果B没收到,消息会一直重传,也可以设置重传次数,提高MQ可用性。
  • Demo 待补充

3、TCC编程模式(两阶段提交的变形)
将业务逻辑分为三块:try预留业务资源(类似DML锁定资源),confirm确认执行业务操作(类似commit),cancel取消执行业务操作(类似rollback)
以下单为例:try阶段会扣除库存,confirm阶段更新订单状态,如果更新订单失败,会进入cancel阶段,恢复库存
TCC开源框架:tcc-transaction (补偿性分布式事务框架)


49、MySql集群/分布式事务实现(我的集群是使用读写分离,写MySql一台,读MySql多台)

1、MySql集群本质上是一个存储方案,其具有不共享(不共享的对等节点可以令集群中任意一台服务器上的更新操作,立即在其他服务器上可见)、分布式结点架构的特点,可以提供较好的容错性,并提高性能。
在数据更新的过程中,使用 “读已提交隔离级别” 保证所有结点的数据一致性,使用 “两阶段提交机制” 保证所有结点都有相同的数据(如果任何一个写操作事变,则更新失败)
2、数据存储 (分布式事务)
Mysql Cluster采用同步复制实现数据节点组内的主从同步,以此来保证组内节点数据的一致性。
同步过程主要通过 两阶段提交机制 实现:

  • 第一阶段:准备提交
    1)执行提交语句时,事务将会被Master(主)发送给所有的Slave(从),每个Slave开始准备提交事务。
    2)Slave准备事务完毕后向Master发送OK或ABORT消息,告知Master事务的准备情况。

  • 第二阶段:通过收到的消息判断提交或中止
    3) Master 接收所有 Slave 发送的 OK 或 ABORT 消息
    Master 如果收到所有 Slave 的 OK 消息,就会向所有 Slave 发送提交消息,执行提交事务
    Master 如果收到来自任何一个Slave的ABORT消息,就会向所有 Slave 发送 ABORT消息,取消提交事务。
    4) Slave接收Master发送的提交或取消请求
    Slave 收到提交请求,执行提交事务,并向Master发送事务已提交的确认;
    Slave 收到取消请求,则会撤销所有改变并释放所占用的资源,中止事务,并向Master发送事务已中止的确认
    5) Master 收到所有 Slave 的确认后,则将报告该事务被提交(或中止),然后继续处理下一个事务
    总结:同步复制共需 4 次消息传递,所以 Mysql Cluster 在数据更新的速度上比单机Mysql 要慢


50、阿里云drds方案处理分库分表

  • 商品中心的方案

  • 背景:商品主表共1亿6千多万条数据(截止20210810,有效数据共1亿1千万条数据)

  • 架构:使用阿里云提供的drds,分为三层,第一层是drds代理层(读写库sg85 只读库wr20),第二层是主rds数据库,共4个实例(16台机器),从rds数据库,共8个实例(32台机器)
    项目的读操作,只会读取从rds数据库中的部分实例

  • 用户中心的方案


51、Mycat是什么,有什么功能,如何配置,底层实现?

1、海量数据的存储问题
传统的关系性数据库已经无法满足快速查询与插入数据的需求。这个时候NoSQL的出现暂时解决了这一危机。它通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升。有些场合NoSQL是无法满足使用场景的,比如有事务与安全指标的。从单机mysql->memcached+mysql+垂直拆分->mysql主从复制->分库分表+水平拆分+mysql集群

2、如果使用关系型数据库解决海量存储的问题呢?
此时就需要做数据库集群,为了提高查询性能将一个数据库的数据分散到不同的数据库中存储。 使用mycat来管理数据库集群。

3、mysql性能瓶颈分析
数据库连接数,默认是100个连接数
单表数据量大,阿里规定单表数据量500万条 数据量大,IO操作就多
硬件资源(QPS/TPS每秒查询书,每秒事务量)

4、大数据量数据库性能的解决方案?
分库分表 索引优化(数据量1个亿且查询不太复杂时,没什么问题) 读写分离


52、MySql分库分表与读写分离 (使用MyCat来管理)

1、数据库分片

  • 1、什么是数据库分片
    指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库上面,以达到分散单台设备负载的效果

  • 2、数据库分库分表带来的问题(重点)
    ①join 操作问题
    业务分库后,原本在同一个数据库中的表分散到不同数据库中,导致无法使用 SQL 的 join
    查询。
    ②事务问题
    事务问题 原本在同一个数据库中不同的表可以在同一个事务中修改,业务分库后,表分散到不同的数据库中,无法通过事务统一修改。虽然数据库厂商提供了一些分布式事务的解决方案(例如,MySQL 的 XA),但性能实在太低,与高性能存储的目标是相违背的。
    ③成本问题
    业务分库同时也带来了成本的代价,本来 1 台服务器搞定的事情,现在要 3 台,如果考虑
    备份,那就是 2 台变成了 6 台。

  • 3、分库分表有垂直切分和水平切分
    垂直切分(复杂度低):即将表按照功能模块、关系密切程度划分出来,部署到不同的库上。我们会建立定义数据库 workDB、商品数据库 payDB、用户数据库 userDB、日志数据库 logDB 等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等
    垂直拆分带来的问题?
    分布式事务(通过模块调用接口来解决)
    –》从连接数和硬件资源两方面进行了解决
    水平切分(复杂度高):当一个表中的数据量过大(超过1000万,就要警觉起来)时,我们可以把该表的数据按照某种规则,例如userID散列,进行划分,然后存储到多个结构相同的表,和不同的库上。
    复杂性:
    (1)路由:水平分表后,某条数据具体属于哪个切分后的子表,需要增加路由算法进行计算,这个算法会引入一定的复杂性。
    范围路由算法:一般建议分段大小在100万至2000万之间,缺点是分布不均匀,假如按照1000万来进行分表,有可能某个分段实际存储的数据量只有 1000 条,而另外一个分段实际存储的数据量有 900 万条。
    Hash 路由:选取某个列(或者某几个列组合也可以)的值进行 Hash 运算,然后根据
    Hash 结果分散到不同的数据库表中。Hash 路由的优点是表分布比较均匀,缺点是扩
    充新的表很麻烦,所有数据都要重分布。
    配置路由:配置路由就是路由表,用一张独立的表来记录路由信息。以用户 ID 为例,
    我们新增一张 user_router 表,这个表包含 user_id 和 table_id 两列,根据 user_id 就可
    以查询对应的 table_id。配置路由的缺点就是必须多查询一次,会影响整体性能。

例如,我们的userDB中的用户数据表中,每一个表的数据量都很大,就可以把userDB切分为结构相同的多个userDB:part0DB、part1DB 等,再将userDB上的用户数据表 userTable,切分为很多 userTable:userTable0、userTable1 等,然后将这些表按照一定的规则存储到多个userDB。

拆分后:影响了哪些操作
join操作
水平分表后,数据分散在多个表中,如果需要与其他表进行 join 查询,需要在业务代码或
者数据库中间件中进行多次 join 查询,然后将结果合并。
count操作
水平分表后,虽然物理上数据分散到多个表中,但某些业务逻辑上还是会将这些表当作一个
表来处理。例如,获取记录总数用于分页或者展示,水平分表前用一个 count() 就能完成的操作,在分表后就没那么简单了。常见的处理方式有下面两种:
①count() 相加:具体做法是在业务代码或者数据库中间件中对每个表进行 count() 操作,然
后将结果相加。这种方式实现简单,缺点就是性能比较低。例如,水平分表后切分为 20 张
表,则要进行 20 次 count(*) 操作,如果串行的话,可能需要几秒钟才能得到结果。
记录数表:具体做法是新建一张表,假如表名为“记录数表”,包含 table_name、row_count 两个字段,每次插入或者删除子表数据成功后,都更新“记录数表”。
order by 操作
水平分表后,数据分散到多个子表中,排序操作无法在数据库中完成,只能由业务代码或者
数据库中间件分别查询每个子表中的数据,然后汇总进行排序。

方案1:模ID运算
方案2:按日期,按地区(华东、华北、华中、华南)
–》从存储容量角度解决瓶颈问题
③在现实项目中,往往是这两种情况兼而有之,这就需要做出权衡,甚至既需要垂直切分,又需要水平切分。我们的图书推荐系统便综合使用了垂直与水平切分,我们首先对数据库进行垂直切分,然后,再针对一部分表,通常是商品数据表,进行水平切分。

2、读写分离(主从模式):

  • 1、什么是读写分离
    在实际的应用中,绝大部分情况都是读远大于写(增删改)Mysql提供了读写分离的机制,所有的写操作都必须对应到Master,读操作可以在Master和Slave机器上进行,
    Slave与Master的结构完全一样,甚至Slave下还可以挂Slave,通过此方式可以有效的提高DB集群的每秒查询率。
    一个写节点Master后面跟着多个读节点,所有的写操作都是先在Master上操作,然后同步更新到Slave上
  • 2、MySql基于binlog的主从复制原理(通过数据库厂商提供的bin log机制)
    master–》binary log《-》io Thread–write–》relay log(中继日志)《–》sql Thread
    复制的基本原理?
    1、binlog事件 //master将改变记录到二进制文件binary log;
    2、relay log //slave将master的binlog事件拷贝到他的中继日志
    3、slave重做中继日志中的事件,将改变应用到自己的数据库中,Mysql复制是异步的且串行化
  • 3、主从复制带来的问题?
    可能出现时延,使用级联主从来解决,或是代码判断,要是真的出现时延,再进行解决
    判断主从是否出现延时seconds-behind-master:0
    若是超过5秒,则强制去主库读取数据
    =>一般的业务不可能把主从搞出延时,老师只遇见过一次:到同行数据时,600万用户数据同时加载
    =》因为数据库之间是在内网传输,延时一般很小

3、总结:

  • 1、当读压力很大的时候,可以考虑添加Slave机器的分式解决,但是当Slave机器达到一定的数量就得考虑分库了。
  • 2、当写压力很大的时候,就必须得进行分库操作
  • 3、全局的ID,不能使用mysql的自增id,要使用全局的id

3、什么是MyCat?
面向企业应用开发的“大数据集群”,支持事务、ACID、一个数据库中间件产品(作为后面MySql集群的proxy使用,默认端口:8066)
作用:提高可用数据分片集群,支持读写分离,支持MySql双主多从,一主(写数据库)多从(读数据库)
当添加一条记录时要向哪个数据库中插入呢?这些问题处理起来都是非常的麻烦。这种情况下可以使用一个数据库中间件mycat来解决相关的问题
用法:现在把数据库中所有的item一千五百万本(图书表)分片存储到三个数据节点中。可以是三台mysql数据库。
节点一:db1存储500万条数据 节点二:db2 500万;节点三:db3 500万;
如何分配:1、根据主键id按段切分 2、根据id取模运算
配置schema。xml文件,管理mycat的逻辑库,表,分片规则,数据节点,数据源 rule= auto-sharding-long mycat会根据此规则自动分片
读写分离:一个写节点master后面跟着多个读节点。读节点的数量取决于系统的压力。写数据库和读数据库建立主从复制,使用mysql厂商自带的binlog,然后建立mycat和mysql的心跳检查。
使用:mysql主数据库修改my.conf文件,musqld下面添加数据库及IP的配置信息 状态:File/position/binlog_Do_DB/binlog_ignore-DB。 从数据库修改my.conf文件 change master to master_host=’’,master_post=’’,master_user=’’,master_password=’‘master_log_file=’’,master_log_pos=’’
当slave_io_Running:YES且slave_SQL_Running:YES,说明配置成功
mycat配置 支持mysql主从复制状态绑定的读写分离机制 balance writeType switchType

4、中间件操作数据库的步骤

  • 1、解析sql语句
  • 2、数据源管理
  • 3、数据源分配
  • 4、请求/响应
  • 5、结果整合

53、MyCat使用案例

1、概念:逻辑库(使用mycat来管理)db_user,db_store 逻辑表:分片表user,全局表(数据字典 冗余),ER表(user地址 只能存放在单一的数据库中,跟着主表一起走),非分片表(门店表,店员表)
应用程序分为db_user(用户表、数据字典,用户地址表)和db_store(订单表,店员表)
经常使用的数据不变性,在后台的每个mysql中都保存一份(如数据字典最好冗余查询**)
用户表进行模2运算,store表进行主从复制

2、conf目录下,有三个关键的配置文件
scheme.xml管理mycqt实例中的逻辑库,表,分片规则,DataNode,DataSource
server.xml
rule.xml 定义了我们队表进行拆分所设计到的规则定义
有连续分片和离散分片两种
连续分片扩容无需迁移数据,范围查询资源消耗小 但是存在热点数据问题
离散分片:分片均匀,并发强 缺点:移植性差

3、现有系统如何使用mycat?
当当、美团正在做这方面的工作
mysql Dump命令生成backup.sql文件, 控制台:mysql -f backup.sql或Source backup.sql

54、mycat关联查询的问题

解决方案:
1、用好ER表,子表跟随父表
2、善用全局表 冗余数据
3、注解方式


55、mycat的分布式事务?XA 明天补充?20181222

如何保证强一致性
两阶段提交 弱XA的两阶段提交
TCC补偿机制


56、选择合适的分布式主键方案?(元芳)

后续补充


57、选择合适的数据存储方案?

后续补充


58、ObjectID规则

后续补充


59、跨地区容灾(20181103)

使用双机热备进行跨地区容灾。双机热备,简单来讲,就是令两个数据库保持状态自动同步。对于处于双机热备当中的数据库,对其中任何一个进行操作都可以自动同步到另外一个,这样,就保持两个数据库当中的数据始终一致。
好处:

  • 1、异地容灾,其中一个坏了可以切换到另一个
  • 2、负载均衡,可以将请求分摊到其中任何一台上,提高网站吞吐量

备份工作原理:
两个原始数据相同的数据库 A 和 B,A 数据库中执行过的 sql 语句在B数据库里也同步执行一遍,通过这样,A、B数据库就可以一直保持同步
类似于读写分离的技术,使用了binlog加mycat来管理。

对于一个 Mysql 服务器,一般有两个线程负责复制和被复制,当开启复制之后:

  • 1、主服务器Master,把所有改动都写入 Binary Log 中。
  • 2、从服务器Slave读取Master的Binary Log, 写入中继日志Relay Log,而后本地的sql线程会负责读取中继日志Relay Log,并执行一遍。

60、数据库连接池?(Sping Boot 默认数据库连接连接池 hikari)

1、什么是连接池(Connection pooling)?

  • 数据库连接池是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放
    数据库池连接数量一直保持一个不少于最小连接数的数量,当数量不够时,数据库会创建一些连接,直到一个最大连接数,之后连接数据库就会等待。

2、连接池有哪些:

  • 1、自定义连接池(关键:linkedlist存放连接+装饰者模式增强close方法)
    创建一个类实现DataSource接口,实现其中的getConnection方法,用于从连接池中获取连接,提供一个集合,用于存放连接,因为移除/添加操作过多,所以选择LinkedList,然后写一个方法addBack(conn)来归还连接
    问题:
    1、创建连接池的时候能不能面向接口编程
    2、额外增加连接池的方法,那么程序员需要记住这些方法.能不能不额外去提供一些方法
    3、用户调用getConnection()获得连接后,必须使用addBack(conn)方法进行连接的归还,如果用户调用 conn.close() 将连接真正的释放,连接池中将出现无连接可用
    要去增强Connection的close方法
    close()原有的功能,释放连接资源;期望的功能:将当前连接归还连接池
    方法增强总结:
    1、继承,子类继承父类,重写父类的方法
    前提:必须有父类,且存在继承关系
    2、装饰者模式,此设计模式专门用于增强方法
    前提:增强的类和被增强的类实现相同的接口,在增强的类中能够获得被增强的类的引用
    缺点:需要将接口的所有方法都实现
    3、动态代理:在运行时动态的创建代理类,完成增强操作
    基于JDK的动态代理:被代理的对象必须实现接口,需要使用反射技术
    4、字节码增强,运行时创建目标类子类,从而进行增强
    常见第三方框架:cglib、javassist
    装饰者模式对close方法进行增强
    1、装饰者固定结构:接口A,已知实现类C,需要装饰者创建代理类B
    2、创建类B,并实现接口A;提供类B的构造方法,参数类型为A,用于接收A接口的其他实现类(C)
    3、给类B添加类型为A成员变量,用于存放A接口的其他实现类
    4、增强需要的方法;实现不需要增强的方法
class B implements A{
    private A a;
    public B(A a){
        this.a = a;
}
/* 以下是增强的方法 */
public void close() throws SQLException {
    //将调用当前close的链接Connection对象添加到连接池中
    this.pool.add(this);
}
/* 以下是不需要增强的方法  */
public void commit() throws SQLException {
    this.conn.commit();
}
将连接添加到连接池中,添加的连接被装饰者类包装过的
```java
MyConnection myConn = new MyConnection(conn,pool);
pool.add(myconn);
//接口的实现类
Connection conn = JdbcUtils.getConnction();

2、DBCP(tomcat)是apache上的一个java连接池项目,也是tomcat使用的连接池组件,需要两个包:commons-dbcp,commons-pool,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后放回去
核心API:basicDataSource BasicDataSourceFactory
参数:驱动,url,用户名密码,初始化连接数,最大连接数,最小空闲连接,超时等待时间
注意:现在tomcat使用的连接池是 tomcat jdbc connection pool
3、C3P0连接池的使用(hibernate/spring)
核心API:comboPooledDataSource
4、dbutils DBUtils是java编程中的数据库操作实用工具,DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码
需要导入的包:commons-dbutils-1.6
dbutils三大核心功能

  • 1、QueryRunner:提供对sql语句操作的API
    1、queryRunner(DataSource ds) //提供数据源,dbutils底层自动维护连接connection
    2、update(String sql,Object…params) //执行更新操作
    3、query(String sql,ResultSetHandler rsh,Object…params) //执行查询
  • 2、ResultSetHandler接口,用于定义select操作后,怎样封装结果集
    对结果集共6种处理方法,其中重要的有三种
    1、beanHandler 将结果集中第一条记录封装到一个指定的javaBean中
    2、BeanListHandler 将结果集中每一条记录封装到指定的javabean中,讲这些javaBean再封装到list集合中
    3、ScalarHandler 它适用于单数据,例如select count(*) from table 操作
  • 3、DbUtils类,工具类,定义了关闭资源与事务处理的方法
    1、closeQuietly(Connection conn) //关闭连接,如果有异常try后不抛
    2、commitAndCloseQuietly(Connection conn) //提交并关闭连接
    3、rollBackAndCloseQuietly(Connection conn) //回滚并关闭连接
    自定义dbUtils
    以后再补

5、Druid
1、druid简介
阿里出品,淘宝和支付宝专用数据库连接池,结合了c3p0和DBCP的优点,同时加入了日志监控,可以很好地监控连接池连接和SQL执行情况
2、druid组成
包括三个部分 1、基于filter-chain模式的插件体系;2、druidDataSource 高效可管理的数据库连接池;3、SQL parser
3、druid功能简介

  • 1、替换dbcp和c3p0,druid提供了一个高效的、可拓展的数据库连接池
  • 2、可以监控数据库访问性能,druid内置提供了一个功能强大的statFilter插件,能够详细统计SQl的执行性能
  • 3、数据库密码加密,druidDriver和DruidDataSource都支持PasswordCallback
  • 4、Sql执行日志,druid提供了不同的LogFilter,能支持commons-logging,log4j和jdkLog,可以按需选择相应的logFilter
  • 5、拓展jdbc,如果对jdbc层有编程需求,可以通过druid提供的filter机制,编写jdbc层的扩展插件

4、最佳配置

<! -- 配置初始化大小、最小、最大 -->//通常来说,只需要修改initialSize、minIdle、maxActive
 <property name="initialSize" value="1" />
 <property name="minIdle" value="1" />
 <property name="maxActive" value="20" />

poolPreparedStatements参数 //是否缓存preparedStatement,性能会有大概20%的提升 对于分库分表较多的数据库,此参数建议配置为false

5、特有功能说明
1、ExceptionSorter(无需额外配置)
当网络断开或者数据库服务器Crash时,连接池里面会存在“不可用连接”,连接池需要一种机制剔除这些“不可用连接”。
在Druid和JBoss连接池中,剔除“不可用连接”的机制称为ExceptionSorter
原理:
根据异常类型/Code/Reason/Message来识别“不可用连接”

  • 3、连接池的机制是:
    1、程序初始化时创建连接池
    2、使用时向连接池申请可用连接
    3、使用完毕,将连接返还给连接池
    4、程序退出时,断开所有连接,并释放资源
    优点:连接池还减少了用户必须等待建立与数据库的连接的时间

  • 4、数据库连接池的使用
    1、导包 c3p0 mchange-commons-java
    2、初始化代码放在ConnectionManager构造函数中
    2、在使用连接池时,只在第一次初始化时,比较耗时,完成初始化之后,使用连接池进行数据库操作明显比不使用连接池花费的时间少

  • 5、连接池的性能对比

功能DBCPDruidc3p0tomcat-jdbcHikariCP
1、是否支持PSCache
2、监控jmxjmx/log/httpjmx/logjmxjmx
3、扩展性
4、sql拦截/解析支持
5、代码简单中等复杂简单简单
6、特点依赖于common-pool阿里开源 代码逻辑复杂优化力度大,功能简单功能全面 且不易维护起源于boneCP
7、连接池管理LinkedBlockingDeque数组FairBlockingQueuethreadlocal+CopyOnWriteArrayList

总结:
性能方面
HikariCP>Druid>tomcat-jdbc>dbcp>c3p0。HikariCP的高性能得益于最大限度的避免锁竞争,Druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池

6、连接池中的连接是长连接还是短连接?
长连接适合于要进行大量数据传输的情况:如数据库,redis,mencache,使用长连接而长时间没有对数据库进行操作,在timeout后,mysql server就会关闭此链接,但是在高并发的情况下,使用短连接更合适

7、连接池中的连接时基于什么协议的连接?为什么?
Http连接池:

  • 1、降低延迟
    如果不采用连接池,每次连接发起Http请求的时候都会重新建立TCP连接(经历3次握手),用完就会关闭连接(4次挥手)
  • 2、支持更大的并发

62、JDBC 对事务的支持

对于JDBC而言,每条单独的语句都是一个事务,即每个语句后都隐含一个commit(Connection 提供了一个auto-commit的属性来指定事务何时结束)

try{  
    conn.setAutoCommit(false);  //将自动提交设置为false        
    ps.executeUpdate("修改SQL"); //执行修改操作  
    ps.executeQuery("查询SQL");  //执行查询操作                 
    conn.commit();      //当两个操作成功后手动提交     
}catch(Exception e) {  
    conn.rollback();    //一旦其中一个操作出错都将回滚,使两个操作都不成功  
    e.printStackTrace();  
} //为了能够将多条SQL当成一个事务执行,必须首先通过Connection关闭auto-commit模式,然后通过Connection的setTransactionIsolation()方法设置事务的隔离级别,
//最后分别通过Connection的commit()方法和rollback()方法来提交事务和回滚事务

63、说说反模式设计?20181222

  • 反模式时指在对经常面对的问题经常使用的低效或者有待优化的设计模式/方法
  • 每个程序员要注意的9种反模式
    1、过早优化 在你有足够的信息能确定在哪优化、如何优化之前,就展开优化
    2、单车车库 花大量时间来辩论和决定琐碎、太主观的问题
    3、分析瘫痪 对问题的过度分析,阻碍了行动和进展,应该选择迭代
    4、上帝类 违反了单一责任原则,难以单元测试、调试和记录文档
    5、新增类恐惧症 认为更多的类必然使得设计更加复杂
    6、内部平台效应 避免重新发明你的操作系统或开发平台已经做得很多的功能 不要重复造轮子,利用他人的工作
    7、魔法数和字符串 使用未命名的数字或字符串字面量,而不是在代码里命名为常量
    8、无用的幽灵类 无用类没有真正的责任,经常用来指示调用另一个类的方法或增加一层不必要的抽象层

64、MySQL的Binlog日志处理工具(Canal,Maxwell,Databus,DTS)对比

1、Canal

定位:基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了mysql。
原理:canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议,mysql master收到dump请求,开始推送binary log给slave(也就是canal),canal解析binary log对象(原始为byte流)
在这里插入图片描述

在这里插入图片描述
整个parser过程大致可分为几步:

  • Connection获取上一次解析成功的位置(如果第一次启动,则获取初始制定的位置或者是当前数据库的binlog位点)
  • Connection建立连接,发生BINLOG_DUMP命令
  • Mysql开始推送Binary Log
  • 接收到的Binary Log通过Binlog parser进行协议解析,补充一些特定信息
  • 传递给EventSink模块进行数据存储,是一个阻塞操作,直到存储成功
  • 存储成功后,定时记录Binary Log位置

在这里插入图片描述
数据过滤:支持通配符的过滤模式,表名,字段内容等
数据路由/分发:解决1:n (1个parser对应多个store的模式)
数据归并:解决n:1 (多个parser对应1个store)
数据加工:在进入store之前进行额外的处理,比如join

2、Maxwell

在这里插入图片描述
canal 由Java开发,分为服务端和客户端,拥有众多的衍生应用,性能稳定,功能强大;canal 需要自己编写客户端来消费canal解析到的数据。
maxwell相对于canal的优势是使用简单,它直接将数据变更输出为json字符串,不需要再编写客户端。

3、Databus

Databus是一种低延迟变化捕获系统,已成为LinkedIn数据处理管道不可或缺的一部分。Databus解决了可靠捕获,流动和处理主要数据更改的基本要求。Databus提供以下功能:

  • 源与消费者之间的隔离
  • 保证按顺序和至少一次交付具有高可用性
  • 从更改流中的任意时间点开始消耗,包括整个数据的完全引导功能。
  • 分区消费
  • 源一致性保存

在这里插入图片描述

4、阿里云的数据传输服务DTS

数据传输服务(Data Transmission Service,简称DTS)是阿里云提供的一种支持 RDS(关系型数据库)、NoSQL、OLAP 等多种数据源之间数据交互的数据流服务。DTS提供了数据迁移、实时数据订阅及数据实时同步等多种数据传输能力,可实现不停服数据迁移、数据异地灾备、异地多活(单元化)、跨境数据同步、实时数据仓库、查询报表分流、缓存更新、异步消息通知等多种业务应用场景,助您构建高安全、可扩展、高可用的数据架构。
优势:数据传输(Data Transmission)服务 DTS 支持 RDBMS、NoSQL、OLAP 等多种数据源间的数据传输。它提供了数据迁移、实时数据订阅及数据实时同步等多种数据传输方式。相对于第三方数据流工具,数据传输服务 DTS 提供更丰富多样、高性能、高安全可靠的传输链路,同时它提供了诸多便利功能,极大得方便了传输链路的创建及管理。
个人理解:就是一个消息队列,会给你推送它包装过的sql对象,可以自己做个服务去解析这些sql对象。免去部署维护的昂贵使用成本。DTS针对阿里云RDS(在线关系型数据库)、DRDS等产品进行了适配,解决了Binlog日志回收,主备切换、VPC网络切换等场景下的订阅高可用问题。同时,针对RDS进行了针对性的性能优化。出于稳定性、性能及成本的考虑,推荐使用。

65、使用mysql中遇到的各种坑 review

第一点:性能优化专题

每天早晨我都会对着镜子问自己,“如果今天是我生命中的最后一天,我还会做我今天要做的这些事吗?”如果连续很多天的答案都是“不”,我知道需要做出改变了,记住自己终将会死去,是我最重要的自我检验,并帮助我做出了许多人生中的重要决定,因为几乎任何事,所有的期望,所有的荣耀,对失败的恐惧,一切都会在死亡面前淡去,只留下真正重要的东西,时刻提醒自己将会死去,便可以帮助你避开担心失去身外之物的恐惧,你本就一无所有,没有理由不去追随你的内心,求知若饥,虚心若愚。 --史蒂夫·乔布斯

  • 1
    点赞
  • 1
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

数据库面试题大库随着随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研究rabbitmq爬虫相关技术的发展以及应用。随着互联技术的普及和应用,人类社会已经进入大数据时代,各行各业也都需要大量数据的支持,而根据大数据五V特性之一数据价值密度低,从大量数据爬去出有用的信息就是大难点。基于Python的爬虫技术可以自动完成网络数据的收集、解析、格式化存储,而rabbitmq可以实现分布式爬虫技术,大大提升工作效率。下面主要以时间顺序列出研
©️2021 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值