Welcome to xpt’s blog! 2021年准备秋招期间整理的一些笔记,分享给大家!

文档分享的初衷是给师弟师妹们作为参考,主要是适合想去大厂+测试开发岗的朋友们。
建议大家自己整理文档,把我的文档作为参考,有些东西自己整理,自己去写出来,才是最适合你自己的!
文章还未精细整理,如存在错误之处,可以邮件or微信反馈给我呀,感激不尽!

想进大厂,要抓住提前批免笔试的机会!(例如京东、字节、百度等报名时间一般为七月,面试时间为报名后的一周内,面试一般为3轮,面试相关经验后续我会单独再写blog分享^_^,也欢迎大家来跟我talk,一定知无不言。)

本人情况:普通211、研究生、有京东、百度、以及字节提前批测开岗offer。7月初开始准备,准备太迟,一边准备一边投简历+面试。

  • 投递简历时间:京东(7.14),字节(7.30),百度(7.30)
  • 三轮面试时间:京东(7.21-7.22-7.26),字节(8.4-8.6-8.9),百度(8.9-8.12-8.16)
  • 意向书时间:京东(8.12),字节(8.16),百度(9.9)

京东提前批开始很早,我投的时候已经是第二批。经过京东几轮面试,熟悉了面试流程,大概掌握了测开岗会问些什么问题。
字节和百度提前批我是在ddl前一天投递,其实已经算很迟了,hc不多了。
投递要趁早,很多岗位有固定hc。
多拿offer,才有谈薪资的底气。

我面试的岗位有以下:
1、测试开发岗(京东、百度、以及字节提前批)
2、银行java开发岗(所以我会整理一点java,银行问的都很简单,所以我这里对java的整理比较少)

整理的内容均来源于历年网络上分享的面经(主要来源于牛客),以及我面试时被问过的问题,list如下:
(1)——计算机网络
(2)——操作系统
(3)——数据库
(4)——数据结构
(5)——python
(6)——java
(7)——linux
(8)——常考编程题
(9)——测试开发相关知识

面试QA整理(3)——数据库

基于MySQL数据库的几种连接查询(内连接、外连接 join)

内连接、外连接( 左(外)连接、右(外)连接、全(外)连接)。

内连接、左连接(左外连接)、右连接(右外连接)、全连接(全外连接)

  • inner join内连接就是左右俩表的交集

  • 左(外)连接,左表(a_table)的记录将会全部表示出来,而右表(b_table)只会显示符合搜索条件的记录。右表记录不足的地方均为NULL。(在右表 (table_name2) 中没有匹配的行,右表选择列置为null)

  • 右(外)连接,左表(a_table)只会显示符合搜索条件的记录,而右表(b_table)的记录将会全部表示出来。左表记录不足的地方均为NULL。

  • 全连接,显示符合条件的数据行,同时显示左右不符合条件的数据行,相应的左右两边显示NULL,即显示左连接、右连接和内连接的并集

    • MySQL是不支持全外的连接的, full join写法适合Oracle和DB2
    • MySQL数据库不支持全外连接查询 但是可以通过UNION来实现【左连接语句 UNION 右连接语句】

from子句中on条件主要用来连接表,其他不属于连接表的条件可以使用where子句来指定;

  • cross join,交叉连接,实际上就是将两个表进行笛卡尔积运算,结果表的行数等于两表行数之积
  • 交叉连接(CROSS JOIN)没有WHERE 子句,它返回连接表中所有数据行的笛卡尔积,其结果集合中的
    • 数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。

【索引】是帮助MySQL高效获取数据的排好序数据结构

数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树

索引的优缺点

索引的优点
  1.创建唯一性索引,保证数据库表中每一行数据的唯一性
  2.大大加快数据的检索速度,这也是创建索引的最主要的原因
  3.减少磁盘IO(向字典一样可以直接定位)
索引的缺点
1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
2.索引需要占用额外的物理空间
3.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度

什么情况适合/不适合建立索引

一、哪些情况下适合建索引

  1. 频繁作为where条件语句查询的字段
  2. 关联字段需要建立索引,例如外键字段,student表中的classid, classes表中的schoolid 等
  3. 排序字段可以建立索引
  4. 分组字段可以建立索引,因为分组的前提是排序
  5. 统计字段可以建立索引,例如count(),max()

二、哪些情况下不适合建索引
1.频繁更新的字段不适合建立索引
2.where条件中用不到的字段不适合建立索引
3.表数据可以确定比较少的不需要建索引
4.数据重复且分布比较均匀的的字段不适合建索引(唯一性太差的字段不适合建立索引),例如性别,真假值

  1. 参与列计算的列不适合建索引

☆数据库索引的类型(普通索引、唯一索引、主键索引、组合索引、聚集索引、非聚集索引

普通索引(INDEX)这是最基本的索引,它没有任何限制

唯一索引(UNIQUE) 避免同一个表中某数据列中的值重复

  • 索引列的值必须唯一, 但允许有空值。如果是组合索引,则列值的组合必须唯一。
  • 与主键索引的区别:主键索引只能有一个,唯一索引可有多个

主键索引(PRIMAY KEY)特殊的唯一索引, 不允许有空值。 一个表只能有一个主键。

联合索引

全文索引(FULLTEXT)快速定位特定数据

  • 只能用于MyISAM类型的数据表;只能用于CHAR ,VARCHAR,TEXT数据列类型;使用大型数据集

聚集索引:聚集索引的顺序就是数据的物理存储顺序,聚集索引:叶节点包含了完整的数据记录

非聚集索引:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同(相当于就是个二级索引啊

非聚集索引和聚集索引的区别在于, 通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据,

B+树一个节点有多大?一千万条数据,B+树多高?

  • B+树一个节点的大小设为一页或页的倍数最为合适。因为如果一个节点的大小 < 1页,那么读取这个节点的时候其实读取的还是一页,这样就造成了资源的浪费。
  • 在 MySQL 中 B+ 树的一个节点大小为“1页”,也就是16k。之所以设置为一页,是因为对于大部分业务,一页就足够了:
  • 首先InnoDB的B+树中,非叶子节点存的是key + 指针;叶子节点存的是数据行。
  • 对于叶子节点,如果一行数据大小为1k,那么一页就能存16条数据;
  • 对于非叶子节点,如果key使用的是bigint,则为8字节,指针在mysql中为6字节,一共是14字节,则16k能存放 16 * 1024 / 14 = 1170 个索引指针
  • 于是可以算出,对于一颗高度为2的B+树,根节点存储索引指针节点,那么它有1170个叶子节点存储数据,每个叶子节点可以存储16条数据,一共 1170 x 16 = 18720 条数据。
  • 而对于高度为3的B+树,就可以存放 1170 x 1170 x 16 = 21902400 条数据(两千多万条数据),也就是对于两千多万条的数据,我们只需要高度为3的B+树就可以完成,通过主键查询只需要3次IO操作就能查到对应数据。所以在 InnoDB 中B+树高度一般为3层时,就能满足千万级的数据存储,所以一个节点为1页,也就是16k是比较合理的。

☆索引数据结构(二叉树,红黑树,Hash表,B树,B+ 树

二叉树,红黑树,Hash表,B树,B+ 树

B+ 树(B树变种)
非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
叶子节点包含所有索引字段
叶子节点用指针连接,提高区间访问的性能

image-20210803154123502

B树
叶节点具有相同的深度,叶节点的指针为空
所有索引元素不重复
节点中的数据索引从左到右递增排列

image-20210803153830548

红黑树的约束:

  1. 节点可以是红色的或者黑色的
  2. 根节点叶子节点每个红色节点的子节点是黑色的
  3. 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

红黑树的特点:
速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍

为什么说B+树更适合数据库索引?

B+ 树(B树变种)
非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
叶子节点包含所有索引字段
叶子节点用指针连接,提高区间访问的性能

1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。

2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。

为什么mongodb里用B树、mysql里面用B+树

是Mysql中数据遍历操作比较多,所以用B+树作为索引结构。

而Mongodb是做单一查询比较多,数据遍历操作比较少,所以用B树作为索引结构。

  • B+ 树(B树变种)
    非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
    叶子节点包含所有索引字段
    叶子节点用指针连接,提高区间访问的性能

(1)B树的树内存储数据,因此查询单条数据的时候,B树的查询效率不固定,最好的情况是O(1)。我们可以认为在做单一数据查询的时候,使用B树平均性能更好。但是,由于B树中各节点之间没有指针相邻,因此B树不适合做一些数据遍历操作

(2)B+树的数据只出现在叶子节点上,因此在查询单条数据的时候,查询速度非常稳定(都要查到叶子节点)。因此,在做单一数据的查询上,其平均性能并不如B树。但是,B+树的叶子节点上有指针进行相连,因此在做数据遍历的时候,只需要对叶子节点进行遍历即可,这个特性使得B+树非常适合做范围查询

基于B+树的索引和基于hash的索引的区别

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。

B+树索引和哈希索引的明显区别是:

  • 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
  • 如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
  • 同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
  • 哈希索引也不支持多列联合索引的最左匹配规则
  • B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题

【mysql引擎】MyISAM索引实现(非聚集)

image-20210803155858999

MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。

☆【mysql引擎】InnoDB索引实现(聚集)

如果想看自己的数据库默认使用的那个存储引擎,可以通过使用命令SHOW VARIABLES LIKE 'storage_engine'

InnoDB引擎采用B+Tree结构来作为索引结构。

聚集索引:叶节点包含了完整的数据记录

B+Tree在B-Tree的基础上有两点变化:
(1)数据是存在叶子节点中的
(2)数据节点之间是有指针指向的

InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID)

InnoDB的存储文件有两个,后缀名分别是 .frm(表的定义文件)和 .idb(表的数据文件);

InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构)

InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键)

必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。

image-20210803162510756

为什么InnoDB表必须有主键,并且推荐使用整型自增ID作为主键?

自增ID可以保证每次插入时B+索引是从右边扩展的,可以避免B+树和频繁合并和分裂(对比使用UUID)。如果使用字符串主键和随机主键,会使得数据随机插入,效率比较差。

为什么非主键索引结构叶子节点存储的是主键值? (一致性和节省存储空间)

innodb引擎的4大特性

插入缓冲(insert buffer),二次写(double write),自适应哈希索引(ahi),预读(read ahead)

innodb默认隔离级别:可重复读(Repeated Read)

InnoDB默认级别:可重复读(Repeated Read)

MyISAM与InnoDB 的区别 & 如何选择?

  1. InnoDB支持事务,MyISAM不支持;

    对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;

  2. InnoDB支持外键,而MyISAM不支持。

对一个包含外键的InnoDB表转为MYISAM会失败;

  1. InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;

    而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针

  2. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。

    而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快(注意不能加有任何WHERE条件);
    因为InnoDB的事务特性,在同一时刻表中的行数对于不同的事务而言是不一样的,因此count统计会计算对于当前事务而言可以统计到的行数,而不是将总行数储存起来方便快速查询

  3. MyISAM表格可以被压缩后进行查询操作

  4. InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁

    InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。

  5. InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键),而Myisam可以没有

  6. Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI
    Innodb:frm是表定义文件,ibd是数据文件
    Myisam:frm是表定义文件,myd是数据文件,myi是索引文件

如何选择:

  1. 是否要支持事务,如果要请选择innodb,如果不需要可以考虑MyISAM;
  2. 如果表中绝大多数都只是读查询,可以考虑MyISAM,如果既有读也有写,请使用InnoDB。
  3. 系统奔溃后,MyISAM恢复起来更困难,能否接受;
  4. MySQL5.5版本开始Innodb已经成为Mysql的默认引擎(之前是MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用InnoDB,至少不会差。

☆数据库的三大范式1NF,2NF,3NF

1.1NF,第一范式(确保每列保持原子性)

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。

2.2NF,第二范式(确保表中的每列都和主键相关)

必须是满足第一范式的前提下, 第二范式要求,确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言,因为如果出现不完全依赖,只可能发生在联合主键的情况下)。

比如要设计一个订单表,设计五个字段“订单id”、“商品id”、“顾客id”、“商品名称”、“顾客姓名”,因为一个订单中可能会有多种商品,一个顾客可能会下多个订单,所以要将“订单id”、“商品id”、“顾客id”作为数据库表的联合主键,但是“商品名称”只和“商品id”有关系,“顾客姓名”只和“顾客id”有关系,这样就不满足第二范式。需要拆表

image-20210723181127392

3.3NF,第三范式(确保每列都和主键列直接相关,而不是间接相关)

必须是满足第二范式的前提下,第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。【叫做不能有传递依赖】

比如在设计一个订单数据表的时候,可以将客户id作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、电话、地址等)的字段。

BCNF范式

BCNF范式消除了可能存在的主属性主码的部分依赖和传递依赖

BCNF范式在3NF基础上消除对主码子集的依赖

以考试分数表为例:身份证号,学号,科目,分数。

image-20210808201135632

首先该表满足第三范式,表中存在有如下依赖关系:

(身份证号,科目)→(学号,分数)
(学号,科目) → (身份证号,分数)

由以上依赖关系可以得知(身份证号,科目)和(学号,科目) 为表关系中的候选码

主属性:身份证号、学号、科目
非主属性:分数

但是,由于存在如下决定关系:

(身份证号)——>(学号)

(学号)——>(身份证号)

即存在关键字段决定关键字段的情况,因此其不符合BCNF。

解决方法:把考试分数系表分解为两个关系表:

image-20210808201510297

这样这个数据库表是符合BCNF的,并消除了删除异常、插入异常和更新异常。

4NF-消除多值依赖

image-20210808201630368

什么是事务?

mysql中,事务其实是一个最小的不可分割的工作单元。事务能够保证一个业务的完整性。

事务:在数据库系统中,一个事务是指由一系列连续的数据库操作组成的一个完整的逻辑过程。这组操作执行前后,系统需要处于一个可预知的、一致的状态。

☆数据库的四大特性!ACID特性

ACID是指在 数据库管理系统(DBMS)中事务所具有的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

  • 1、原子性:事务被视为不可分割的最小单元,在一个事务中所有的操作要么都成功,要么都失败。
    • 如银行转账,A向B账户转账1000元,这里可分为三个操作,1.A向B转账、2.银行处理、3.B账户收到转账。原子性就是保证这三个操作要么都成功,要么都失败,如果1、2操作成功,3失败了,那么1、2操作要进行回滚
    • 回滚可以通过日志来实现,主要是基于MySQL日志系统的redo和undo机制。日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作。
  • 2、一致性:一个事务执行前和执行后都必须处于一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。
    • 一旦一个事务结束了,不管成功还是失败,系统所处的状态和它的业务规则是一致的。也就是说,数据应当不会被破坏。
    • 拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来还得是5000,这就是事务的一致性。
  • 3、隔离性:隔离性是指两个事务之间互不干扰。一个事务所做的修改在最终提交以前,对其他事务是可不见的。
    • 当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
  • 4、持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
    • 持久性需要考虑到事物在执行过程中可能出现的各种异常,并对异常做出相应的处理。

mysql中如何控制(开启,关闭)事务?

  • select @@autocommit;语句可以查询事务是否开启,1为开启。mysql默认是开启事务的(自动提交)。

    rollback;回滚,事务开启时是撤销不了的。

  • 设置mysql自动提交为false:set autocommit=0 ;

    ​ 这时插入数据后,再rollback;是可以撤销的。

    ​ 但是比如插入数据后输入commit手动提交,然后再rollback;是不能撤销的。

  • 事务开启之后,一旦commit 提交,就不可以回滚(也就是当前的这个事务在提交的时候就结束了|
    begin;,或者start transaction;

    ——都可以帮我们手动开启一个事务,而且rollback;是可以撤销的

    想要提交就commit手动提交,commit之后不可撤销。

☆数据库四种隔离等级 & 事务并发问题

MySQL定义了四种隔离级别:

  • ① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。【都可以避免】
    • 完全的串行化读,所有SELECT语句都被隐式的转换成SELECT … LOCK IN SHARE MODE,即读取使用表级共享锁,读写相互都会阻塞。隔离级别最高。
    • 提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。
  • ② Repeatable read (可重复读):可避免脏读、不可重复读的发生。【不可避免幻读】
    • 在同一个事务内的查询都是事务开始时刻一致的,InnoDB的默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻读。(写锁,等读完)
    • 在第一个事务中的两次读数据之间,即使第二个事务对数据进行修改,第一个事务两次读到的的数据是一样的。这样就发生了在一个事务内两次读到的数据是一样的,因此称为是可重复读
  • ③ Read committed (读已提交、不可重复读):可避免脏读的发生。
    • 只能读取到已经提交的数据。即解决了脏读,但未解决不可重复读。(读锁,等写完)
  • ④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。【都不能避免】
    • 如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。
    • a事务对数据进行操作,在操作的过程中,事务没有被提交,但是b可以看见a操作的结果。
    • SELECT语句以非锁定方式被执行,所以有可能读到脏数据,隔离级别最低。(读不锁)

事务并发问题
1)脏读(Dirty Reads)
事务A读取了事务B的更新的数据,但是事务B回滚了(未提交),导致A读取的为脏数据。

2)不可重复读(Non-Repeatable Reads)
事务A读取同一数据两次,但是在两次之间事务B对该数据进行了修改并提交,导致事务A读取两次读取不一致。

3)幻读(Phantom Reads)
事务A修改全表的数据,在事务A未提交时,事务B向表中插入或删除数据并提交,导致事务A读取的数据与需要修改的数据不一致。

PS: 不可重复读针对的是数据的修改,幻读针对的时数据的新增和删除。解决不可重复读问题只需要给对应记录上行锁,而解决幻读需要对表加锁。

  • 实现事物隔离性主要有两种方式

    读写锁和MVCC(Multi-Version Concurrency Control)多版本并发处理方式

不考虑事务的隔离性,会发生的几种问题:

  • 1,脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。

当一个事务正在多次修改某个数据,而在这个事务还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。

  • 2,不可重复读:不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。

    • 例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发生了不可重复读。
    • 不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据
    • 在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……
  • 3,虚读(幻读):幻读是事务非独立执行时发生的一种现象。

    • 例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
    • 幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

如何查看、修改数据库的隔离级别?

如何查看数据库的隔离级别?(默认是REPEATABLE-READ)

1
2
3
4
5
mysq 8.0:
-- 系统级别的
select @@global.transaction_isolation;
-- 会话级别的
select @@transaction_isolation;

如何修改数据库的隔离级别?

1
set global transaction isolation level read committed;

☆关系型数据库和非关系型数据库(区别、各自特性优缺点

区别总结:

1、数据存储结构不同

  • 非关系型数据库存储数据的格式,可以是K-V键值对文形式、文档形式、图片形式等,对于数据的格式十分灵活。非关系型数据库可以存储基础类型以及对象或集合等;
  • 而关系型数据库只支持基础类型。(例如在 MySQL 中,主要支持数值类型、日期/时间类型、字符串字符类型)
  • 因此如果业务的数据结构并不是固定的或者经常变动比较大的,那么非关系型数据库是个好的选择

2、可扩展性不同

  • 关系型数据库一般都有固定的表结构,不是很容易进行扩展,灵活度稍欠缺。
  • 非关系型数据库具有高扩展性:基于键值对,数据没有耦合性,容易扩展。

3、数据一致性方面不同

  • 关系型数据库的最大特点就是事务的一致性
  • 而非关系型数据库一般不支持ACID特性。
  • 因此如果业务对于数据的一致性要求很高,那么非关系型数据库并不一个很好的选择
  • 非关系型数据库是基于CAP模型,而传统的关系型数据库是基于ACID模型的
    • 什么是CAP?Consistency一致性,Availability可用性,Partition tolerance分区容错性。

4、读写速度不同

  • 关系型数据库,数据读写必须经过sql解析,大量数据、高并发下读写性能不足,对传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。
  • 非关系型数据库,在高并发,大数据下读写能力较强。无需经过sql层的解析,读写性能很高。
  • 而且,nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。

5、复杂查询方面

  • 关系型数据库,支持SQL,可用于一个表以及多个表之间非常复杂的查询。例如进行join等复杂查询。
  • 非关系型数据库,数据结构相对复杂,复杂查询方面稍欠。

6、成本方面

  • 关系型数据库,昂贵,像oracle需要花费大量成本购买使用。
  • nosql数据库部署简单,成本低,基本都是开源软件。

7、存储载体不同

  • nosql可以使用硬盘或者随机存储器作为载体
  • 而关系型数据库只能使用硬盘。

关系型数据库通过外键关联来建立表与表之间的关系,非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定。

当前主流的关系型数据库有Oracle、DB2、Microsoft SQL Server、Microsoft Access、MySQL等。

非关系型数据库有 MongoDB、NoSql、Redis、Memcached、HBase 。

非关系型数据库也叫Nosql数据库,全称是not only sql

非关系型数据库提出另一种理念,例如,以键值对存储,且结构不固定,每一个元组可以有不一样的字段,每个元组可以根据需要增加一些自己的键值对,这样就不会局限于固定的结构,可以减少一些时间和空间的开销。使用这种方式,用户可以根据需要去添加自己需要的字段,这样,为了获取用户的不同信息,不需要像关系型数据库中,要对多表进行关联查询。仅需要根据id取出相应的value就可以完成查询。


关系型数据库的特性
1、关系型数据库,是指采用了关系模型来组织数据的数据库;
2、关系型数据库的最大特点就是事务的一致性;
3、简单来说,关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织。

关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织
优点:
1、易于维护:都是使用表结构,格式一致;丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率;
2、使用方便:通用的SQL语言使得操作关系型数据库非常方便;
3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、为了维护一致性所付出的巨大代价,就是其读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠;
3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。
数据读写必须经过sql解析,大量数据、高并发下读写性能不足。


非关系型数据库的特性
1、使用键值对存储数据;
2、分布式;
3、一般不支持ACID特性;
4、非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合。可以是文档或者键值对等

非关系型数据库的优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式;而关系型数据库则只支持基础类型。
2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、无需经过sql层的解析,读写性能很高;
4、高扩展性:基于键值对,数据没有耦合性,容易扩展;
5、成本低:nosql数据库部署简单,基本都是开源软件。

非关系型数据库的缺点
1、不提供sql支持,学习和使用成本较高;
2、无事务处理;
3、数据结构相对复杂,复杂查询方面稍欠。

☆非关系型数据库Mangodb(简介、增删改查命令

MongoDB是用C++语言编写的非关系型数据库。

MongoDB是一个开源、 高性能、无模式的文档型数据库。是最像关系型数据库(MySQL) 的非关系型数据库。

它支持的数据结构非常松散,是一种类似于JSON的格式叫BSON,所以它既可以存储比较复杂的数据类型,又相当的灵活。

MongoDB中的记录是一个文档, MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。

特点是高性能、易部署、易使用,存储数据十分方便,主要特性有:
面向集合存储,易于存储对象类型的数据
模式自由(不用像MySQL需要指定列啥的
支持动态查询
支持完全索引,包含内部对象
支持复制和故障恢复
使用高效的二进制数据存储,包括大型对象
文件存储格式为BSON(一种JSON的扩展)

image-20210805171429968

image-20210805144217821

image-20210805144202411

mysql和redis两种数据库有什么区别

mysql是关系型数据库,主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。

redis是缓存数据库,即将数据存储在缓存中,用于存储使用频繁的数据,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限。

缓存就是数据交换的缓冲区(cache),当浏览器执行请求时,首先会对在缓存中进行查找,如果存在,就获取;否则就访问数据库。

redis和mysql的区别总结

(1)类型上

从类型上来说,mysql是关系型数据库,redis是缓存数据库

(2)作用上

mysql用于持久化的存储数据到硬盘,功能强大,速度较慢,基于磁盘,读写速度没有Redis快,但是不受空间容量限制,性价比高

redis用于存储使用较为频繁的数据到缓存中,读取速度快,基于内存,读写速度快,也可做持久化,但是内存空间有限,当数据量超过内存空间时,需扩充内存,但内存价格贵

(3)需求上

mysql和redis因为需求的不同,一般都是配合使用
需要高性能的地方使用Redis不需要高性能的地方使用MySQL。存储数据在MySQL和Redis之间做同步。

☆Redis面试题总结

redis 简介
简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以存写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。


为什么要用 redis /为什么要用缓存

主要考虑两个角度:性能和并发

如果说数据要去硬盘上读取,那么过程会比较慢。如果将频繁使用的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快

大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用 Redis 做一个缓冲操作。直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,让请求到 Redis这里而不用经过数据库。


Redis支持哪几种数据类型?

  1. string:最基本的数据类型,二进制安全的字符串,最大512M。

    最常规的 set/get 操作,Value 可以是 String 也可以是数字。一般做一些复杂的计数功能的缓存。

  2. list:按照添加顺序保持顺序的字符串列表。

    使用 List 的数据结构(双向链表,可以做简单的消息队列的功能。比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。另外,可以利用 lrange 命令,就是从某个元素开始读取多少个元素,可以做基于 Redis 的分页功能,性能极佳,用户体验好。可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高。

  3. set:无序的字符串集合,不存在重复的元素。

    因为 Set 堆放的是一堆不重复值的集合。所以可以做全局去重的功能。另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

  4. Zset:有序集合。

    多了一个权重参数 Score,集合中的元素能够按 Score 进行排列。可以做排行榜应用,取 TOP N 操作。Sorted Set 可以用来做延时任务。

    举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 SortedSet 结构进行存储。

  5. hash:key-value对的一种集合。

    这里 Value 存放的是结构化的对象,比较方便的就是操作其中的某个字段。比如我们可以Hash数据结构来存储用户信息,商品信息等等。


Redis到底是多线程还是单线程?单线程的 Redis 为什么这么快

单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

(1) 绝大部分请求是纯粹的内存操作(非常快速)

(2) 采用单线程,避免了不必要的上下文切换和竞争条件

(3) 采用了非阻塞 I/O 多路复用机制

仔细说一说 I/O 多路复用机制

文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。

Redis-client 在操作的时候,会产生具有不同事件类型的 Socket。在服务端,有一段 I/O 多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。


redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis支持持久化,Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:

快照(snapshotting,RDB),只追加文件(append-only file,AOF)

对比区别:

  • AOF文件比RDB更新频率高,优先使用AOF还原数据。
  • AOF比RDB更安全,但AOF 文件比 RDB 文件大,且恢复速度慢
  • RDB性能比AOF好,数据集大的时候,AOF比 rdb 启动效率低。
  • 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

(1)RDB:是Redis DataBase缩写快照。RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。

优点:

  • 1、只有一个文件 dump.rdb,方便持久化。
  • 2、容灾性好,一个文件可以保存到安全的磁盘。
  • 3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
  • 4.相对于数据集大时,比 AOF 的启动效率更高。

缺点:数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)

(2)AOF持久化,开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。当重启Redis会重新将持久化的日志中文件恢复数据。

优点:

  • 1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。
  • 2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
  • 3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))

缺点:

  • 1、AOF 文件比 RDB 文件大,且恢复速度慢。
  • 2、数据集大的时候,比 rdb 启动效率低。

如何选择合适的持久化方式

  • 一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
  • 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
  • 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
  • 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

Redis 4.0 对于持久化机制的优化

Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。

如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。

Redis持久化数据和缓存怎么做扩容?

  • 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。
  • 如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。

Redis主要消耗什么物理资源?内存。

Redis的内存用完了会发生什么?
如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。

Redis如何做内存优化?
可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面


知道跳跃表吗?在 Redis 中主要用在哪些地方?

zset 有序集合,可以使用范围查找,排行榜功能或者topN功能。

zset 有序集合 就是用跳表来实现的。

跳跃表是一种有序的数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

  • 跳跃表基于单链表加索引的方式实现
  • 跳跃表以空间换时间的方式提升了查找速度
  • Redis有序集合在节点元素较大(比如很长的字符串)或者元素数量较多时使用跳跃表实现

像这种链表加多级索引的结构,就是跳跃表:

image-20210805220023861

思考一个问题,为什么元素数量比较多或者成员 比较长的字符串的时候Redis要使用跳跃表来实现?
跳跃表在链表的基础上增加了多级索引以提升查找的效率,其是一个空间换时间的方案, 必然会带来个问题:索引是 占内存的。

原始链表中存储的有可能是很大的对象,索引结点只要存储关键值和几个指针,并不需要要存储对象,因此当节点本身比较大或者元素数量比较多的时候,其优势必然会被放大,而缺点则可以忽略。


Redis的过期键的删除策略

Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。

Redis中同时使用了惰性过期和定期过期两种过期策略。

过期策略通常有以下三种:

  • 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  • 惰性过期:只有当客户端访问一个key时,才会判断该key是否已过期,过期则清除。
    • 该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  • 定期过期:redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。
    • 不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略
    • 该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

Redis的内存淘汰策略有哪些
Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。

  • 全局的键空间选择性移除

    • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
    • allkeys-lru:当内存不足以容纳新写入数据时,首先通过LRU算法驱逐最久没有使用的键。(这个是最常用的)
    • allkeys-random:当内存不足以容纳新写入数据时,随机移除某个key。
    • allkeys-lfu:从所有键中驱逐使用频率最少的键
  • 设置过期时间的键空间选择性移除

    • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最久没有使用的key。
    • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
    • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
    • volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键

总结:Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。

LRU

  1. 新增key value的时候首先在链表结尾添加Node节点,如果超过LRU设置的阈值就淘汰队头的节点并删除掉HashMap中对应的节点。

  2. 修改key对应的值的时候先修改对应的Node中的值,然后把Node节点移动队尾。

  3. 访问key对应的值的时候把访问的Node节点移动到队尾即可。

image-20210805225025923

Redis缓存雪崩、穿透、击穿

缓存处理流程:前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果

缓存雪崩:redis缓存key同一时间大量失效,导致大量请求全部打到数据库,造成数据库挂掉

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。

和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案

  1. 设置缓存数据的过期时间,防止同一时间大量数据过期现象发生,随机初始化缓存失效时间。
  2. redis一般都是集群部署,将热点数据均匀分布在不同的缓存数据库中。
  3. 设置热点数据永远不过期。
  4. 不断的用定时任务去刷新缓存

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

解决方案:

  1. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击,但是不能可能会用不同的ID攻击。

  2. IP拉黑,但可能会用不同的IP来攻击。

  3. 接口层增加参数合法性校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;

  4. 布隆过滤器,就是一个二进制数据的集合。当一个数据加入这个集合时,经历如下:

    • 通过K个哈希函数计算该数据,返回K个计算出的hash值
    • 这些K个hash值映射到对应的K个二进制的数组下标
    • 将K个下标对应的二进制数据改成1。

    布隆过滤器筛掉非法的key,就是缓存中不存在且数据库也不存在的数据。布隆过滤器跟redis缓存其实是配合的,都是为了减少无效查询,当然redis缓存可以增加热数据的查询效率,布隆过滤器一般用redis的bitmap来存储,他不可能存数据库的全部信息,只是用来判断数据在或者不在。
    所以流程是1 查redis的布隆过滤器(如果未命中直接结束) 2 查redis缓存数据 3 查询数据库 淘汰更新缓存。

缓存击穿是指突然该key失效,一瞬间大量该key的请求打到数据库上

缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

解决方案:

  1. 设置热点数据永远不过期。
  2. 加互斥锁,在请求数据库这边给他上锁,那么这个时候就只有一个线程,能抢到这个锁,所以也就只有一个线程能操作这个数据库,那么对数据库的压力就很小,当查询到数据之后呢,还会把缓存重新写到redis里去。其他没有抢到锁的线程,让他先睡几毫秒,然后再重新去redis里面去查询

CAP——非关系型数据库是基于CAP模型

一个经典的分布式系统理论。CAP理论告诉我们:一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中两项。

1、一致性

在分布式环境下,一致性是指数据在多个副本之间能否保持一致的特性。在一致性的需求下,当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一直的状态。

2、可用性

可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。

3、分区容错性

分区容错性约束了一个分布式系统具有如下特性:分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。

#和$区别

面试回答:

  • #对传入的参数视为字符串。

  • $引用参数时,不做任何处理,直接将值拼接在sql语句中。

  • #的方式引用参数,mybatis会先对sql语句进行预编译,然后再引用值,能够有效防止sql注入,提高安全性。$的方式引用参数,sql语句不进行预编译,不能避免注入攻击。


向sql语句传参,在sql语句中引用这些参数的时候,有两种方式:#, $。

开启预编译,如果是$是在此时将参数一起编译;如果是#参数不编译,会在执行sql查询的时候,将参数当成字符串放进去,也就是同样的sql语句只需要编译一次。

两者的区别:

  • 使用#方式引用参数的时候,会把传入的参数当成是一个字符串,自动添加双引号。

  • $引用参数时,不做任何处理,直接将值拼接在sql语句中。

他们之间的区别用最直接的话来说就是:

#是一个占位符,$是拼接符。

#相当于对数据加上双引号,$相当于直接显示数据。

  • 1、#对传入的参数视为字符串。

    • #的方式引用参数,mybatis会先对sql语句进行预编译,然后再引用值
    • 也就是它会预编译select * from user where name = #{name}
    • 比如我传一个csdn,那么传过来就是select * from user where name = ‘csdn’
  • 2、$引用参数时,不做任何处理,直接将值拼接在sql语句中。select * from user where name=${name}

    • 比如我穿一个csdn,那么传过来就是select * from user where name=csdn
  • 3、使用 # 能够防止sql注入,$不能避免注入攻击。

    • 比如:用户进行一个登录操作,后台sql验证式样的:select * from user where username=#{name} and password = #{pwd}
    • 如果前台传来的用户名是“wang”,密码是 “1 or 1=1”,用#的方式就不会出现sql注入,而如果换成$方式,sql语句就变成了select * from user where username=wang and password = 1 or 1=1,这样的话就形成了sql注入。
  • 4、Mybatis 默认情况下,将对所有的 sql 进行预编译。

  • MyBatis排序时使用order by 动态参数时需要注意,用$而不是#。

mysql预编译

预编译:指的是数据库驱动在发送 sql 语句和参数给 DBMS 之前对 sql 语句进行编译,这样 DBMS 执行 sql 时,就不需要重新编译。

为什么需要预编译?预编译的好处:
JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以优化 sql 的执行。

  • 1、预编译之后DBMS可以省去编译,直接运行sql。
    • 预编译之后的 SQL 多数情况下可以直接执行,DBMS 不需要再次编译。
  • 2、一般而言,越复杂的SQL,编译的复杂度将越大,预编译可以将多个操作合并成一个操作。
  • 3、预编译语句对象可以重复利用。(把一个 SQL 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个 SQL,可以直接使用这个缓存的 PreparedState 对象。)
  • 4、可以将这类SQL语句中的值用占位符替代,不需要每次编译,可以直接执行,只需执行的时候,直接将每次请求的不同的值设置到占位符的位置。
  • 5、预编译可以视为将sql语句模板化或者说参数化。

mysql如何开启预编译

  • Mysql是默认 没有开启预编译的,需要在配置中加上
    jdbc:mysql://xxx.22.11.31:3306/dbname?useServerPrepStmts=true

(有兴趣的可以查看mysql驱动包里面的ConnectionImpl类是如何操作预编译的。这里提一下缓存PreparedStatement,在执行预编译时会先去判断是否存在缓存,如果存在则对参数清空,绑定新的参数。如果不存在则调用数据库进行预编译处理生成一个PreparedStatement对象。)

那如果按照默认不开启预编译的话,会是什么情况? 每次JDBC都需要编译一条新的sql,即使查询的是同样的sql,也是重新编译一条sql

img

开启预编译,如果是${}是在此时将参数一起编译,如果是#{}参数不编译,会在执行sql查询的时候,将参数当成字符串放进去,也就是同样的sql语句只需要编译一次。

img

所以使用${}会编译你参数的这个参数,会有sql风险,而使用#{}仅仅是 select * from t_user where id = xxx ,将参数不编译以字符串的形式,放到xxx的位置,参数你写别的sql想注入就会报错。 能用#{}尽量用#{}!

mysql 读写分离

1、what 读写分离

读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

读写分离就是在主服务器上修改,数据会同步到从服务器,从服务器只能提供读取数据,不能写入,实现备份的同时也实现了数据库性能的优化,以及提升了服务器安全。

2、why 那么为什么要读写分离呢?
因为数据库的“写”(写10000条数据到oracle可能要3分钟)操作是比较耗时的。
但是数据库的“读”(从oracle读10000条数据可能只要5秒钟)。
所以读写分离,解决的是,数据库的写入,影响了查询的效率。

3、when 什么时候要读写分离?
数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询多的情况下会考虑使用,利用数据库 主从同步 。可以减少数据库压力,提高性能。当然,数据库也有其它优化方案。memcache 或是 表折分,或是搜索引擎。都是解决方法。

如何优化MySQL

① SQL语句及索引的优化

② 数据库表结构的优化:使得数据库结构符合三大范式与BCNF

③ 系统配置的优化

④ 硬件的优化

SQL语句的优化:
1、尽量避免使用子查询(先查外表再匹配内表,而不是先查内表,当外表的数据很大时,查询速度会非常慢。)
2、避免函数索引(WHERE YEAR(d) >= 2016;即使d字段有索引,也会直接全表扫描。d >= ‘2016-01-01’)
3、用IN来替换OR(MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。对于连续的数值,能用 between 就不要用 in 了)
4、LIKE前缀%号、双百分号、_下划线查询非索引列或*无法使用到索引,如果查询的是索引列则可以
5、读取适当的记录LIMIT M,N,而不要读多余的记录
6、避免数据类型不一致
7、分组统计可以禁止排序sort,总和查询可以禁止排重用union all
8、避免随机取记录(MySQL不支持函数索引,会导致全表扫描
9、禁止不必要的ORDER BY排序
10、批量INSERT插入
11、不要使用NOT等负向查询条件(你可以想象一下,对于一棵B+树,根节点是40,如果你的条件是等于20,就去左面查,你的条件等于50,就去右面查,但是你的条件是不等于66,索引应该咋办?还不是遍历一遍才知道。
12、**尽量不用select **(SELECT *增加很多不必要的消耗(cpu、io、内存、网络带宽)
13、区分in和exists(区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以**IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。
*

​ 14、优化Group By语句

​ 如果对group by语句的结果没有排序要求,要在语句后面加 order by null(group 默认会去排序)

​ 尽量让group by过程用上表的索引,确认方法是explain结果里没有Using temporary 和 Using filesort;

​ 如果group by需要统计的数据量不大,尽量只使用内存临时表;也可以通过适当调大tmp_table_size参数,来避免用到磁盘临时表;

​ 如果数据量实在太大,使用SQL_BIG_RESULT这个提示,来告诉优化器直接使用排序算法(直接用磁盘临时表)得到group by的结果。

​ 15、多表关联尽量用join,减少子查询的使用。表的关联字段如果能用主键就用主键,也就是尽可能的使用索引字段。如果关联字段不是索引字段可以根据情况考虑添加索引。

​ 索引的优化:
​ 1、Join语句的优化
​ 2、避免索引失效

​ 1)如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列。

最左前缀匹配原则:在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

​ 2)不在索引列上做任何操作 (计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描。
​ 3)存储引擎不能使用索引中范围条件右边的列。如这样的sql: select * from user where username='123' and age>20 and phone='1390012345',其中username, age, phone都有索引,只有username和age会生效,phone的索引没有用到。

​ 4)尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致))

   如select age from user减少select *

​ 5)mysql在使用不等于(!= 或者 <>)的时候无法使用索引会导致全表扫描。

​ 6)is null, is not null 也无法使用索引,在实际中尽量不要使用null。

​ 7)like 以通配符开头(‘%abc..’)mysql索引失效会变成全表扫描的操作。

   所以最好用右边like 'abc%'。如果两边都要用,可以用select age from user where username like '%abc%',其中age是必须是索引列,才可让索引生效

如果数据库查询很慢该怎么定位问题

https://www.cnblogs.com/ibytecoding/p/13859625.html

image-20210815230704693

========mysql实例========

【学生表,老师表,统计每个老师教了多少学生,降序排序】

1
2
3
4
5
select teachername,count(*) as A
from student left join teacher
on student.teacherid=teacher.teacherid
group by student.teacherid
order by A desc;

【☆按小时统计每个不同的小时数据出现的个数】sql 的 DATE_FORMATE()函数

1
2
3
select DATE_FORMAT(a_time,'%Y-%m-%d %H') a_hour,count(id) hour_count
from abc_log
group by a_hour;
image-20210804104554233 image-20210804104627399

【sql查询今天温度比昨天高的日期】DATEDIFF函数

日期之间差一天,即将表Weather定义为两个表A、B。

把A表看到昨日数据,B表看作今日数据。即A表的日期加1天等于B表的日期,B表的温度大于A表温度即可。

image-20210806013113170

1
2
SELECT A.starttime FROM Weather AS A
INNER JOIN Weather AS B ON A.temperature>B.temperature and DATEDIFF(B.starttime,A.starttime) = 1
1
2
SELECT A.starttime FROM Weather AS A
INNER JOIN Weather AS B ON A.temperature>B.temperature and DATE_ADD(A.starttime,INTERVAL 1 DAY ) = B.starttime

【查询分数>80分的学生名字,查询列表的第10-20条】

1
2
3
4
select name
from scoretablename
where 成绩>80
limit 9,11

【查找所有分数大于80的学生】

1
select name from test group by name having min(code)>80;

【按字段进行分组group by】

SQL如何查询一张表的所有字段并按其中一个字段进行分组

假设一个表tab有一个id字段、一个Name字段,内容如下

id Name
3 张三
5 李四
1 王五
1 赵六

sql 语句
select * from tab group by id

这条SQL的结果应该是

id Name
1 王五
3 张三
5 赵六

第一个Name显示的是王五 因为sql group by满足条件的有多个时是取第一个的

group by 一般结合合计函数一起使用
比如 sql语句
select id,count(*) AS total from tab group by id
用于统计每个id有多少个
结果

id total
1 2
3 1
5 1

sql语句中的AS可以省略,一般as 后边是别名;能省略,但是不建议,建议写上as,增加可读性

【给定id,name,score,统计每一个分数的人数】

1
2
3
select score,count(id)
from table_name
group by score;

【数据库查询时间最新(order by降序取desc)的前十行(limit)】

MYSQL,根据日期查询最新10条数据

image-20210721012951842 image-20210721013012784

ORDER BY 关键字用于对结果集进行排序。

ORDER BY 关键字默认按照升序对记录进行排序。如果需要按照降序对记录进行排序,您可以使用 DESC 关键字。

limit 子句用于限制 SELECT 语句中查询的数据的数量。

查询数据库中最近的十条数据,并按时间升序进行排列

  • 最近的十条数据:取最近的数据,降序排列,取十条
image-20210721013202198
  • 但是要求按时间升序进行排列:
    • 即:将查询的结果作为一个中间表,然后再次进行查询
image-20210721013234284

【组合两个表】join连接:left join解释一下

LEFT JOIN 关键字会从左表 (table_name1) 那里返回所有的行,即使在右表 (table_name2) 中没有匹配的行。对于这样的行,右表选择列置为null

注释:在某些数据库中, LEFT JOIN 称为 LEFT OUTER JOIN。

175. 组合两个表

编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:

1
FirstName, LastName, City, State
image-20210721013559792
1
2
3
select FirstName, LastName, City, State
from Person left join Address
on Person.PersonId = Address.PersonId

【查询student表中两门科成绩90分以上的学生学号】

查询student表中两门科成绩90分以上的学生学号的SQL语句?
(学号,姓名,课程号,成绩)

1
2
3
4
5
select 学号
from student
where 成绩>90
group by 学号
HAVING COUNT(1)>=2

【☆查出表中学生成绩最好的学生信息】

image-20210804100831418

【查找or删除重复的电子邮箱】

查找重复的电子邮箱

1
2
3
4
select Email
from person
group by Email
HAVING COUNT(1)>=2
image-20210722102030346

196. 删除重复的电子邮箱

1
2
3
DELETE p1 
FROM Person p1,Person p2
WHERE p1.Email = p2.Email AND p1.Id > p2.Id
image-20210722102350174

【mysql语句:查重复城市信息….返回表中有两名以上学员的城市的所有学员信息】

ID Name City Address
1 包彦钦 北京 北京市东四北大街520号
2 王洪涛 北京 北京市西单北大街151号
3 张立涛 大连 大连市西岗区新开路69号
4 佘高峰 济南 山东省济南市泺文路50号
5 徐俊 上海 上海市静安区南阳路46号
6 张忠飞 上海 上海市肇嘉浜路414号
7 徐俊 上海 上海市乳山路150号
返回表中有两名以上学员的城市的所有学员信息

1
2
3
4
select * 
from table_name
group by City
HAVING COUNT(1)>=2

sql优化——count(1)、count(*)与count(列名)的区别

https://blog.csdn.net/wx1528159409/article/details/95643499