1、mysql有哪些数据库类型,和oracle对比是什么样的?
mysql |
oracle |
说明 |
INT |
NUMBER |
整数型 |
FLOAT |
FLOAT |
单精度浮点数 |
DOUBLE |
FLOAT(24) |
双精度浮点数 |
CHAR |
CHAR |
字符串型 |
VARCHAR |
VARCHAR2 |
字符串型 |
LONGTEXT |
CLOB |
大字段 |
DATE |
DATE |
日期 |
DATETIME |
DATE |
时间 |
2、mysql中char和varchar的区别是什么,应该如何选择?
- 区别:
- char是定长的,char会根据声明的长度分配空间,然后用空格对尾部进行填充;varchar是可变长度的(虽然会根据字符串长度分配存储空间,但是在内存中依然使用声明长度进行排序作业)
- char对英文字符占用1个字节,中文字符占用2个字节;varchar均是2个字节
- char大小0-255bytes,varchar0-65535bytes
- char和varchar都可以设置默认值
- 怎么选择?
- 经常变化的数据来说,char要优于varchar,因为char不容易产生存储碎片
- 对于短列或者固定长度的数据,char的存储效率高于varchar(CHAR比VARCHAR更快,因为CHAR是固定长度的,而VARCHAR需要增加一个长度标识,处理时需要多一次运算)
- 对于innodb存储引擎,Innodb buffer pool足够大的时候,其实char和varchar没明显差别,但是buffer pool小于表大小时,磁盘读写会影响性能,varchar更短,实际下来varchar性能会比char更高,另外即便是会变化长度的数据,涉及到移动数据,操作也是先在内存中完成的,因此也不会影响很多效率。
- 因此,一般情况下可以优先选用varchar,除非是第二条中那种固定长度的数据
3、数据库设计,三大范式?
范式(NF):可以理解成一张数据表的表结构所符合发某种设计标准的级别。
- 1NF:字段是最小单元,不可再分
- 2NF:满足1NF,表中字段必须完全依赖主键(全部主键)
- 3NF:满足2NF,非主键外的字段,不能相互依赖
- BCNF/4NF:太严苛,一般不用
名称 |
优点 |
缺点 |
范式 |
范式化的表,减少数据冗余,数据表更新操作快,占用存储空间少 |
查询的时候需要多表关联查询,难进行索引优化 |
反范式 |
反范式就是通过冗余数据提高数据库查询性能,可以减少表的关联以及更好进行索引优化 |
会存在大量冗余数据,数据维护成本高。 |
因此在工作中,一般是将范式和反范式结合使用(主要还是看实际情况)。
4、索引,索引类型?
索引:对数据库表中一列或者多列的值进行排序的数据结构,用于快速访问数据库表中的特定信息。
索引的分类:
- 从物理结构上划分:
- 聚簇索引:索引的排序方式和表中数据记录排序方式一致的索引。也就是说,聚簇索引的顺序就是数据的物理存储顺序,存储数据时,按照聚簇索引的顺序进行排序,然后重新存储在磁盘上,因为数据的物理存放顺序只有一种,因此一个表只能有一个聚簇索引(innodb中,索引和数据放在一个文件中,查到索引key,就可以直接得到数据行内容)
- 非聚簇索引:索引顺序和物理存储顺序不同 ---> 一般用在某字段中数据唯一性比较高时(myISAM的索引文件和数据文件是分离的,查找时,先从索引中找到对应元素,获得数据行地址,然后通过地址去数据文件中查找数据行。innodb中的非主键索引也是非聚簇索引)
- 按照应用划分:
- 普通索引:基本索引类型,运行在定义索引的时候插入重复值和控制,就是单纯为了提高查询效率 【ALTER TABLE table_name ADD INDEX index_name (column)】
- 唯一索引:索引列中的值必须是唯一的,但是允许为空值【ALTER TABLE table_name ADD UNIQUE index_name (column)】
- 主键索引:特殊的唯一索引,也是聚簇索引,不允许空值
- 组合索引:表中多个字段组合创建的索引,遵循最左前缀匹配规则
- 全文索引:只有myIsam才能用,只支持char,varchar,text类型字段
- 按照索引稠密程度:
- 稠密索引:每个索引值都有一个索引项 --- 非聚簇索引必须是稠密索引
- 稀疏索引:索引值不一定有索引项
5、索引的优缺点?
- 优点:
- 提高数据检索的效率,这也是创建索引的最主要原因
- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
- 分组或者排序查询时,也能减少分组或者排序时间
- 缺点:
- 索引需要占用物理空间
- 创建和维护索引需要耗费时间,并且时间随着数据量的增大而增加,因此虽然索引大大提升了查询效率,但是增删改数据速度会降低
6、索引的设计原则?
- 对查询频次高,数据量多的表创建索引。 小表(数量百万级以内)不建议创建,有的时候直接查询数据花费的时间比遍历索引花费的时间多不了多少。
- 针对查询频次高的字段创建索引。 比如从where条件中提取,如果组合比较多,尽量创建符合索引--- 利用最左匹配规则
- 针对经常需要排序,分组,联合操作的字段建立索引,order by , group by , union ,distinct 等
- 尽可能使用短索引,索引值很长的情况下查询速度会收到影响,此时应尽量使用前缀索引。
7、为什么不使用二叉查找树作为索引?
二叉查找树的查询效率为O(logn),当树过高的时候,查找效率会下降,另外,索引文件是存在磁盘上的,文件系统从磁盘读取数据的时候,一般以页为单位进行读取,4K或者8K,如果一个页内的数据过少,会涉及到多次IO,大大降低效率。
因此,二叉查找树这种树高随着数据量增大急剧增大,并且每次更新都要左旋右旋维持平衡的二叉树,不适合用于索引结构。
8、为什么使用B+树而不是B树来做索引?
- B+树IO次数少:B+树的非叶子节点只存关键字,而不存储数据,因此单个页就能存储更多的关键字,有利于减少磁盘IO次数
- B+树更适合范围查找:由于数据只存在叶子节点上,并且叶子节点之间用链表有序连接,因此范围查找效率远高于B树
9、最左匹配原则?
说的是联合索引最左优先。以左边为起点的任何连续的索引都可以被匹配上,遇到范围查询时会停止匹配。
在建立联合索引的时候,联合索引依然是一个B+树,比如建立一个联合索引(a,b)索引结构如下:
a索引:1,1,2,2,3,3
b索引:1,2,1,4,1,2
可以看到,联合索引中,a是有序的,b是无序的,但是在a等值的情况下b是有序的,这种情况下:b=2,不会走索引,a>1 and b=4;前半段a可以走索引,b就不能走索引。
10、覆盖索引?
其实就是需要查询的字段在索引中包含了。比如聚簇索引,包含了键值和数据行,非聚簇索引但是只查询某一个字段(个人理解就是不需要回表查询。)
11、索引下推?
索引下推,其实目的就是为了减少回表次数,优化查询性能。(Using index condition)
mysql的架构包括 server层- 引擎层- 文件系统层,其中server层负责SQL语法解析,生成执行计划,并调用引擎层去执行数据的存储和检索。
- 没有索引下推优化的时候,进行索引查询的时候,由于联合索引的最左匹配原则,有些索引会失效,这个时候,mysql会首先根据可用的索引来查找记录,得到主键值后逐一回表查询。
- 使用索引下推后,mysql在取出索引的同时,会直接判断where中是否有条件可以在索引中直接过滤,这样就可以减少回表的次数。
举个栗子:联合索引是(name, age) 查询条件是 where name like "张%" and age = 10
不使用索引下推:可以看到首先会匹配出来四条,然后直接获取主键id去回表查询,相当于回表四次
使用索引下推:可以看到匹配出来四条之后,因为索引中包含了查询条件age,此时mysql会直接根据age进行过滤,然后在回表,只有两次回表,因此性能会提高。
12、 mysql存储引擎中,myIsam 和 innodb的区别?
- innodb支持事务,myIsam不支持事务 (因此如果数据库崩溃,innodb可以根据事务日志进行数据恢复)
- innodb支持外键,myIsam不支持外键,(因此含有外键的innodb表转为myIsam会报错)
- innodb索引和数据存储在一起,myIsam不存储在一起
- 均是B+树的存储结构,但是innodb主键是聚簇索引,myIsam非聚簇索引
- innodb 不保存表的行数信息(因为事务特性,不同事务行数不一样),因此查询需要全表扫描,myIsam有一个变量记录了表行数,因此查询条数的时候很快,select count(*) from table -- 但是不能加where条件
- innodb支持表锁和行锁(行锁是默认的,行锁基于索引实现,因此如果没有命中索引会退化为表锁),myIsam只支持表锁
- innodb必须要有唯一索引(主键)如果没有会自动寻找或者生成一个隐藏的row_id当主键,myIsam可以没有主键。
---- 通常情况下,默认使用innodb存储引擎,myIsam适用于以插入为主的程序(新闻门户,博客系统 ?)
13、innodb为什么推荐使用整型的自增的主键?
自增的id可以保证每次插入时,B+树的索引是从右边拓展的,相比于自定义ID(如UUID)可以避免B+树的频繁合并和分裂,如果使用字符串主键或者随机主键,数据插入就会变得随机,效率也会降低。
推荐使用整型是因为整型的比较效率高。
14、innodb中的锁算法?
- Record lock:单个行记录上的锁 (RC/RR都支持)
- Gap lock:间隙锁,锁定索引记录间隙,确保索引记录的间隙不变(RR支持)
- Next-key lock:记录锁和间隙锁组合,同时锁住数据和数据前后范围(RR支持)
innodb这种行锁实现特点意味着:只有通过索引条件检索数据,才会使用行锁,否则都是使用表锁。
在RR隔离级别上,innodb对于行查询先使用next-key lock,当查询的唯一性索引命中的时候,会将next-key lock 优化降级为record lock。
举例:
- select ... from 语句:使用MVCC机制实现非阻塞读,不加锁
- select ... from lock in share mode 语句:追加共享锁,innodb会使用next-key lock锁进行处理,如果扫描发现唯一索引,会降级为record lock
- select ... from for update 语句:追加排他锁,innodb会使用next-key lock锁进行处理,如果扫描发现唯一索引,会降级为record lock (可以看到共享锁和排他锁其实处理方式一样)
- update ... where 语句:innodb会使用next-key lock锁进行处理,如果扫描发现唯一索引,会降级为record lock
- delete ... where 语句:innodb会使用next-key lock锁进行处理,如果扫描发现唯一索引,会降级为record lock
- insert 语句: innodb会在将要插入的一行设置一个排他的record lock
15、存储过程
存储过程是一个预编译的SQL语句,是一组为了完成特定功能的SQL语句集,经过第一次编译后,再次调用就不需要重新编译了。
- 为什么要用到储存过程?
- 个人理解:
- 1、需要多次执行的SQL语句集,可提升效率,(比单纯的执行SQL语句快)
- 2,方便加逻辑控制(比如版本升级的时候,新增表,删除表,新增字段,删除字段,修改字段等等。)
- 和函数的区别?
- 函数有1个返回值,存储过程是通过参数返回,可以有多个,也可以没有
- 函数在语句中直接调用,存储过程必须单独调用。
16、where和having之间的区别?
select 字段 from 表名 [where 条件] group by 分组字段名 [having 分组后的过滤条件]
- 执行时机不同:where是执行之前进行过滤,不满足where条件,不参与分组,而having是分组之后对结果进行过滤
- 判断条件不同:where不能对聚合函数进行判断,having可以
17、SQL的执行过程
https://www.cnblogs.com/lansetiankongblog/p/10968984.html
前面提到过,mysql分为server层,存储引擎层以及文件系统层。
server层包括连接器,缓存,分析器,优化器,执行器等,涵盖了MySQL绝大多数的核心服务功能以及所有的内置函数(如日期,时间等等),所有跨存储引擎的功能都在这一层实现;
存储引擎层则负责数据的存储和提取,架构模式是插件式的,mysql5.5以后innodb是默认存储引擎。
接下来讲一下SQL的执行过程:
- 首先是连接数据库
- 连接器负责客户端建立连接,获取权限、维持和管理连接。用户密码认证通过后,连接器会到权限表中查询用户拥有的权限,用于后续的权限判断--- 这就意味着即便修改这个用户的权限,只要此次连接不断,就不会有影响。
- 长连接:连接建立成功后,如果客户端持续有请求,则一直使用同一个连接,短连接则是指每次执行完很少几次查询就断开连接,下次在重连
- 连接的建立比较复杂,因此建议尽量使用长连接,但是mysql执行过程中临时使用的内存是管理在连接对象里面的,长时间不断开,内存增长的特别快,继而导致OOM
- 怎么解决呢?第一个是定期断开长连接(或者判断执行过一个占用很大内存的查询后断开)
- 第二个是 mysql5.7版本以后,可以通过执行mysql_reset_connection 来重新初始化连接资源,这个过程不需要重连和权限验证,但是会将连接恢复到刚刚创建完的状态。
- 查询缓存
- mysql拿到一个查询请求后,会首先到缓存看看,是否执行过这个语句,如果执行过,就直接返回结果给客户端(select语句必须完全一样)
- 不在缓存中,就执行后续流程,然后把结果存在缓存中。
- 但是mysql的缓存其实很鸡肋,第一个是刚刚说了,缓存命中必须select语句完全一致,第二个就是缓存失效十分频繁,只要对一个表更新,这个表的所有缓存都会失效,mysql8.0之后直接将查询缓存的功能删掉了。
- 分析器
- 缓存没有命中,就开始执行语句了,mysql首先通过分析器对SQL语句进行解析,词法分析和语法分析 --- 这样mysql才知道你要做什么
- 优化器
- 知道做什么之后,还会在执行之前进行优化,比如表里多个索引的时候使用哪一个索引,多表关联的时候连接顺序等。
- 执行器
- 执行语句(执行前还会判断对这个表有没有执行权限),有权限就会调用执行引擎去执行SQL。
18、如何判断一个SQL是否走了索引?
只需要在查询语句开头使用explain 关键字即可。
几个重要的参数:
- id:id代表select子句或者操作表的顺序,如果包括子查询,则会出现多个id,值越大,优先级越高,值相同则按照从上到下执行
- select_type:查询类型
- simple:简单的select查询
- primary:查询中包含任何复杂的子查询,则最外层被标记为primary
- subquery: 在select或者where列表中包含了子查询
- derived:在from列表中包含了子查询
- union: 当使用UNION、UNION ALL时,若除第一条select语句之外的其他语句出现在UNION、UNION ALL之后,就会别标记为UNION
- union result:当使用UNION时会产生一个临时表用于存放UNION的数据,这个临时表会被标记为UNION RESULT
- table :数据是哪个表的
- type:从最好到最差依次是 system>const>eq_ref>ref>range>index>All,一般情况最少要达到range级别,最好能到ref级别
- system:表只有一行记录,是const的特例,一般不会出现
- const: 表示通过索引一次找到
- eq_ref:唯一性索引扫描,常见于用主键或者唯一索引查到数据
- ref: 非唯一性索引扫描,返回匹配某个单独值的行,可能会有多个符合条件的结果 (属于查找和扫描的混合体)、
- range:只检索给定范围的行,如where语句中出现了 between,in,<> 等,扫描效果比全表扫描好
- index:只扫描全部索引
- all: 全表扫描,最差的一种查询
- possible_key: 显示可能用到的索引,一个或者多个,查询到的索引不一定是真正被用到的
- key:实际使用的所有,如果为null,则说明没有使用索引
- key_len:索引中用到的字节数,不损失经度的情况下,长度越短越好,key_len显示的值为索引字段最大可能长度,不一定是实际长度
- ref: 索引使用情况,const常数代表索引使用正常
- rows : 代表查询优化器最终查询的行数
- filetered:查询过滤的百分比,值越大越优
- extra:
- firstmatch
- using index : 覆盖索引
- using where: 查找使用索引,需要回表查询
- using condition pushdown: 索引下推 (using index condition ?)
- unsing index & unsing where: 查询使用了索引,但是因为字段都在 索引中,不需要回表查询。
19、索引失效的几种情况,以及如何优化SQL查询?
索引失效的情况
- like 以 “%”开头,索引会失效, 当like 条件前缀没有%,后缀有%的时候,会使用索引
- or 字段 :
- 左右查询字段均为索引的时候,索引生效
- 左右查询字段只有一个是索引,索引不生效
- 查询条件对索引使用函数或者表达式计算,索引失效
- 查询条件对字符串进行比较
- 联合索引不满足最左匹配原则
- 索引列上使用is null,或者 is not null操作 (优化:给默认值)
- 使用 not in,!=, <>等操作符,(优化:如key<>0 改成 key< 0 or key>0)
- mysql优化器判断全表扫描更快(数减少)
SQL优化经验:其实就是对应着尽可能走索引
- explain 命令分析SQL查询语句
- where左侧查询字段尽量不使用函数或者表达式
- 避免select * 查询,因为这个是全表查询
- 索引列如果有null等,建议给一个默认值标记
- 避免not in,!=, <>等操作符,可以改成or条件,但是要保证or左右查询字段都有索引
- 使用between and 来替代 in 查询
- 枚举类型的字段,使用enum而不是vachar
- 对表进行水平切割或者垂直分割
- 有些复杂的逻辑放在后端代码里面等等
20、分页优化?
mysql的分页,想到的是offset,limit操作,但是随着页数的增大,查询性能是指数性增大的。这是因为mysql查询不是跳过offset的行数,而是去offset+limit行,然后丢弃offset行,返回limit行,当offset特别大的时候,效率就会很低。
覆盖索引+延迟关联进行优化:通过覆盖索引查询返回所需要的主键,然后根据这些主键关联原表来获得所需要的行数据。
21、SQL查询出的数据是什么样子的:
- 单表查询:根据where条件过滤表中记录,形成中间表(这个中间表对用户不可见),然后根据select的选择相应的列返回最终结果
- 两表连接查询:对两表求笛卡尔积,并用on条件和连接类型进行过滤形成中间表,然后根据select返回指定列。
- 多表连接:先对第一个和第二个连接查询,然后在和第三个连接查询。。。
22 、连接查询:
22.1 :内连接:
inner join分为显式的和隐式的
隐式内连接,没有inner join 关键字,形成中间表为两个表的笛卡尔积
SELECT O.ID,O.ORDER_NUMBER,C.ID,C.NAME
FROM CUSTOMERS C,ORDERS O
WHERE C.ID=O.CUSTOMER_ID;
显式内连接,有inner join,形成的中间表为两个表经过on条件过滤之后的笛卡尔积
SELECT O.ID,O.ORDER_NUMBER,C.ID,C.NAME
FROM CUSTOMERS C INNER JOIN ORDERS O ON C.ID=O.CUSTOMER_ID;
22.2 :外连接
外连接不止列出与连接条件想匹配的行,而是左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的数据行。
LEFT JOIN或LEFT OUTER JOIN
如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
SELECT O.ID,O.ORDER_NUMBER,O.CUSTOMER_ID,C.ID,C.NAME
FROM ORDERS O LEFT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;
RIGHT JOIN 或 RIGHT OUTER JOIN
如果右表的某行在左表中没有匹配行,则将为左表返回空值
SELECT O.ID,O.ORDER_NUMBER,O.CUSTOMER_ID,C.ID,C.NAME
FROM ORDERS O RIGHT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;
FULL JOIN 或 FULL OUTER JOIN
MySQL是不支持全外的连接的,这里给出的写法适合Oracle和DB2。但是可以通过左外和右外求合集来获取全外连接的查询结果。
SELECT O.ID,O.ORDER_NUMBER,O.CUSTOMER_ID,C.ID,C.NAME
FROM ORDERS O FULL OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;
mysql中写法:
SELECT O.ID,O.ORDER_NUMBER,O.CUSTOMER_ID,C.ID,C.NAME
FROM ORDERS O LEFT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID
UNION
SELECT O.ID,O.ORDER_NUMBER,O.CUSTOMER_ID,C.ID,C.NAME
FROM ORDERS O RIGHT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;
例子
a表 |
b表 |
id |
name |
id |
job |
parent_id |
1 |
张3 |
1 |
23 |
1 |
2 |
李四 |
2 |
34 |
2 |
3 |
王武 |
3 |
34 |
4 |
1) 内连接
select a.*,b.* from a inner join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
2)左连接
select a.*,b.* from a left join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
3 王武 null
3) 右连接
select a.*,b.* from a right join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
4) 完全连接
select a.*,b.* from a full join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
3 王武 null
23、Unix和mysql之间进行时间戳的转换:
unix_timestamp : 从mysql时间戳转为Unix时间戳
from_unixtime:从Unix时间戳转为mysql时间戳
24、百万级别或者以上的数据应该如何删除?
因为索引的存在,使得对数据的增删改都需要额外维护索引文件,降低增删改的效率,因此在删除百万级别的数据库的时候,可以如下操作:
- 首先删除索引
- 删除无用数据
- 删除完成时候,重新创建索引
25、union 和 union all
通过union连接的SQL分别单独取出的列数必须相同
使用union时,多个相等的行会被合并,并且这个合并是比较耗时的,如果没有特别要求,尽量使用union all进行合并,过滤的时候在代码里面进行过滤。