1、数据库优化查询方法
外键、索引、联合查询、选择特定字段等等
2、简述mysql和redis区别
redis: 内存型非关系数据库,数据保存在内存中,速度快
mysql:关系型数据库,数据保存在磁盘中,检索的话,会有一定的Io操作,
访问速度相对慢
3、列出常见MYSQL数据存储引擎
InnoDB:支持事务处理,支持外键,支持崩溃修复能力和并发控制。
如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),
那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,
也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。
MyISAM:插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和
读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、
并发性要求比 较低,也可以使用。
MEMORY:所有的数据都在内存中,数据的处理速度快,但是安全性不高。
如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。
它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用
在相对较小的数据库表。
4、日常工作中你是怎么优化SQL的?
加索引--避免返回不必要的数据--适当分批量进行--优化sql结构
--分库分表--读写分离
5、说说分库与分表的设计
水平分库:以字段为依据,按照一定策略(hash、range等),
将一个库中的数据拆分到多个库中
水平分表:以字段为依据,按照一定策略(hash、range等),
将一个表中的数据拆分到多个表中。
垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。
垂直分表:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表
(主表和扩展表)中。
6、事务的隔离级别有哪些?MySQL的默认隔离级别是什么?
读未提交(Read Uncommitted)
读已提交(Read Committed)
可重复读(Repeatable Read)
串行化(Serializable)
Mysql默认的事务隔离级别是可重复读(Repeatable Read),其他数据库一般默认为
读已提交。(我们可以把mysql的默认隔离级别修改为读已提交,
这句话面试官不问不要说,不要给自己挖坑)
7、什么是幻读,脏读,不可重复读呢?
事务A、B交替执行,事务A被事务B干扰到了,因为事务A读取到事务B未提交的数据,
这就是脏读
在一个事务范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,
这就是不可重复读。
事务A查询一个范围的结果集,另一个并发事务B往这个范围中插入/删除了数据,
并静悄悄地提交,然后事务A再次查询相同的范围,两次读取得到的结果集不一样了,
这就是幻读。
8、说一下数据库的三大范式
第一范式:数据表中的每一列(每个字段)都不可以再拆分
第二范式:在第一范式的基础上,分主键列完全依赖于主键,
而不能是依赖于主键的一部分
第三范式:在满足第二范式的基础上,表中的非主键只依赖于主键,
而不依赖于其他非主键
9、索引有哪几种类型?
主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。
唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。
全文索引:是目前搜索引擎使用的一种关键技术,对文本的内容进行分词、搜索
覆盖索引:查询列要被所建的索引覆盖,不必读取数据行
组合索引:多列值组成一个索引,用于组合搜索,效率大于索引合并
10、创建索引有什么原则呢?
最左前缀匹配原则
频繁作为查询条件的字段才去创建索引
频繁更新的字段不适合创建索引
索引列不能参与计算,不能有函数操作
优先考虑扩展索引,而不是新建索引,避免不必要的索引
在order by或者group by子句中,创建索引需要注意顺序
区分度低的数据列不适合做索引列(如性别)
定义有外键的数据列一定要建立索引。
对于定义为text、image数据类型的列不要建立索引。
删除不再使用或者很少使用的索引
11、什么是数据库事务?
数据库事务(简称:事务),是数据库管理系统执行过程中的一个逻辑单位,
由一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,
是一个不可分割的工作单位。
12、mysql 事务的特性,ACID
原子性、一致性、隔离性、持久性
13、B树和B+树的区别?
1、B-树是一类树,包括B-树、B+树、B*树等,是一棵自平衡的搜索树,
它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。
B-树是专门为外部存储器设计的,如磁盘,它对于读取和写入大块数据有良好的性能,
所以一般被用在文件系统及数据库中。
2、B+树是B-树的变体,也是一种多路搜索树, 它与 B- 树的不同之处在于:
所有关键字存储在叶子节点出现,内部节点(非叶子节点并不存储真正的 data)
为所有叶子结点增加了一个链指针
因为内节点并不存储 data,所以一般B+树的叶节点和内节点大小不同,
而B-树的每个节点大小一般是相同的,为一页
14、mysql 行锁
MySQL的行锁又分为共享锁(S锁)和排他锁(X锁)。
一般普通的select语句,InnoDB不加任何锁,我们称之为快照读
select * from test;
通过加S锁和X锁的select语句或者插入/更新/删除操作,我们称之为当前读
select * from test lock in share mode;
select * from test for update;
insert into test values(…);
update test set …;
delete from test …;
以上的当前读,读取的都是记录的最新版本。对读取记录都会加锁,
除了第一条语句lock in share mode是对记录加S锁(共享锁)外,
其他的操作都是加X锁(排他锁)。
15、什么是死锁?怎么解决?
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,
从而导致恶性循环的现象
常见的解决死锁的方法
如果不同程序会并发存取多个表, 尽量约定以相同的顺序访问表,可以大大降低死锁机会。
在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,
通过表级锁定来减少死锁产生的概率;
16、什么是脏读?幻读?不可重复读?
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,
由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,
这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,
例如有一个事务查询了几列(Row)数据, 而另一个事务却在此时插入了新的几列数据,
先前的事务在接下来的查询中, 就会发现有几列数据是它先前所没有的。
17、索引有哪些使用场景
1)、应该创建索引的场景
主键应该创建主键索引。
频繁作为查询条件的字段应该创建索引。
查询中需要与其他表进行关联的字段应该创建索引。
需要排序的字段应该创建索引。
需要统计或分组的字段应该创建索引。
优先考虑创建复合索引。
2)、不应创建索引的场景
数据记录较少的表。
经常需要增删改操作的字段。
数据记录重复较多且分布平均的字段(如性别、状态等)。
1、redis 是单线程还是多线程?
大家所熟知的 Redis 确实是单线程模型,指的是执行 Redis 命令的核心模块是单线程的,
而不是整个 Redis 实例就一个线程,Redis 其他模块还有各自模块的线程的
2、Redis 不仅仅是单线程
一般来说 Redis 的瓶颈并不在 CPU,而在内存和网络。如果要使用 CPU 多核,
可以搭建多个 Redis 实例来解决。
其实,Redis 4.0 开始就有多线程的概念了,比如 Redis 通过多线程方式在后台删除对象、
以及通过 Redis 模块实现的阻塞命令等。
缓存穿透
当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,
当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,
而数据库中也没有这些数据,我们称这种现象为"缓存穿透"。
解决方案:
在接口访问层对用户做校验,如接口传参、登陆状态、n秒内访问接口的次数;
利用布隆过滤器,将数据库层有的数据key存储在位数组中,
以判断访问的key在底层数据库中是否存在;
缓存击穿
缓存击穿和缓存穿透从名词上可能很难区分开来,它们的区别是:
穿透表示底层数据库没有数据且缓存内也没有数据,击穿表示底层数据库有数据而缓存内
没有数据。当热点数据key从缓存内失效时,大量访问同时请求这个数据,
就会将查询下沉到数据库层,此时数据库层的负载压力会骤增,
我们称这种现象为"缓存击穿"。
解决方案:
延长热点key的过期时间或者设置永不过期,如排行榜,首页等一定会有高并发的接口;
利用互斥锁保证同一时刻只有一个客户端可以查询底层数据库的这个数据,
一旦查到数据就缓存至Redis内,避免其他大量请求同时穿过Redis访问底层数据库;
缓存雪崩
缓存雪崩是缓存击穿的"大面积"版,缓存击穿是数据库缓存到Redis内的热点数据失效
导致大量并发查询穿过redis直接击打到底层数据库,而缓存雪崩是指Redis中
大量的key几乎同时过期,然后大量并发查询穿过redis击打到底层数据库上,
此时数据库层的负载压力会骤增,我们称这种现象为"缓存雪崩"。
解决方案:
在可接受的时间范围内随机设置key的过期时间,分散key的过期时间,
以防止大量的key在同一时刻过期;
对于一定要在固定时间让key失效的场景(例如每日12点准时更新所有最新排名),
可以在固定的失效时间时在接口服务端设置随机延时,将请求的时间打散,
让一部分查询先将数据缓存起来;
延长热点key的过期时间或者设置永不过期,这一点和缓存击穿中的方案一样;
缓存预热
缓存预热如字面意思,当系统上线时,缓存内还没有数据,如果直接提供给用户使用,
每个请求都会穿过缓存去访问底层数据库,如果并发大的话,很有可能在上线当天就会宕机,
因此我们需要在上线前先将数据库内的热点数据缓存至Redis内再提供出去使用,
这种操作就成为"缓存预热"。
缓存预热的实现方式有很多,比较通用的方式是写个批任务,
在启动项目时或定时去触发将底层数据库内的热点数据加载到缓存内。
缓存更新
缓存服务(Redis)和数据服务(底层数据库)是相互独立且异构的系统,
在更新缓存或更新数据的时候无法做到原子性的同时更新两边的数据,
因此在并发读写或第二步操作异常时会遇到各种数据不一致的问题。
缓存更新的设计模式有四种:
Cache aside:查询:先查缓存,缓存没有就查数据库,然后加载至缓存内;
更新:先更新数据库,然后让缓存失效;或者先失效缓存然后更新数据库;
Read through:在查询操作中更新缓存,即当缓存失效时,Cache Aside 模式是由
调用方负责把数据加载入缓存,而 Read Through 则用缓存服务自己来加载;
Write through:在更新数据时发生。当有数据更新的时候,如果没有命中缓存,
直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后由缓存自己更新数据库;
Write behind caching:俗称write back,在更新数据的时候,只更新缓存,
不更新数据库,缓存会异步地定时批量更新数据库;
缓存降级
缓存降级是指当访问量剧增、服务出现问题(如响应时间慢或不响应)或
非核心服务影响到核心流程的性能时,即使是有损部分其他服务,
仍然需要保证主服务可用。可以将其他次要服务的数据进行缓存降级,
从而提升主服务的稳定性。
Redis 能够处理速度快的原因主要有以下几个方面:
1、内存数据库:Redis是一种内存数据库,数据存储在内存中,因此读写速度非常快。
2、单线程:Redis是单线程的,避免了多线程带来的上下文切换和锁竞争的开销,
因此可以更高效地利用CPU资源。
3、非阻塞式IO:Redis使用了非阻塞式IO,当执行一个IO操作时,它不会被阻塞,
而是会立即返回,这样就可以执行其他操作,提高了Redis的响应速度。
4、基于事件驱动:Redis采用了基于事件驱动的模型,通过监听socket事件,
当事件发生时,执行相应的操作,避免了轮询的开销,提高了Redis的性能。
5、轻量级:Redis的代码量相对较小,且没有复杂的依赖关系,因此它的启动速度
非常快,这也是它处理速度快的重要原因之一。
综上所述,Redis之所以能够处理速度快,是因为它采用了多种优化策略和技术,
从而实现了高效的数据存储和快速的读写操作。