数据库
一条SQL查询语句是如何执行的?
1连接器:连接器负责跟客户端建立连接、获取权限、维持和管理连接。
2查询缓存: MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以key-value 对的形式,被直接缓存在内存中。
3分析器:你输入的是由多个字符串和空格组成的一条SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。
4优化器:优化器是在表里面有多个索引的时候,决定使用哪个索引; 或者在一个语句有多表关联(join )的时候,决定各个表的连接顺序。
5执行器: MySQL通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。
数据库的事务隔离级别有哪些?
1读未提交(Read Uncommitted):
○允许一个事务读取另一个事务尚未提交的数据修改。
○最低的隔离级别,存在脏读、不可重复读和幻读的问题。
2读已提交(Read Committed):
○一个事务只能读取已经提交的数据。其他事务的修改在该事务提交之后才可见。
○解决了脏读问题,但仍可能出现不可重复读和幻读。
3可重复读(Repeatable Read):
○事务执行期间,多次读取同一数据会得到相同的结果,即在事务开始和结束之间,其他事务对数据的修改不可见。
○解决了不可重复读问题,但仍可能出现幻读。
4序列化(Serializable):
○最高的隔离级别,确保事务之间的并发执行效果与串行执行的效果相同,即不会出现脏读、不可重复读和幻读。
事务的四大特性有哪些?
事务的四大特性通常被称为 ACID 特性
1原子性:确保事务的所有操作要么全部执行成功,要么全部失败回滚,不存在部分成功的情况。
2一致性:事务在执行前后,数据库从一个一致性状态转变到另一个一致性状态。
3隔离性:多个事务并发执行时,每个事务都应该被隔离开来,一个事务的执行不应该影响其他事务的执行。
4持久性:一旦事务被提交,它对数据库的改变就是永久性的,即使在系统故障或崩溃后也能够保持。
MySQL的执行引擎有哪些?
MySQL的执行引擎主要负责查询的执行和数据的存储, 其执行引擎主要有MyISAM、InnoDB、Memery 等。
●InnoDB引擎提供了对事务ACID的支持,还提供了行级锁和外键的约束,是目前MySQL的默认存储引擎,适用于需要事务和高并发的应用。
●MyISAM引擎是早期的默认存储引擎,支持全文索引,但是不支持事务,也不支持行级锁和外键约束,适用于快速读取且数据量不大的场景。
●Memery就是将数据放在内存中,访问速度快,但数据在数据库服务器重启后会丢失。
MySQL为什么使用B+树来作索引
B+树是一个B树的变种,提供了高效的数据检索、插入、删除和范围查询性能。
●单点查询:B 树进行单个索引查询时,最快可以在 O(1) 的时间代价内就查到。从平均时间代价来看,会比 B+ 树稍快一些。但是 B 树的查询波动会比较大,因为每个节点既存索引又存记录,所以有时候访问到了非叶子节点就可以找到索引,而有时需要访问到叶子节点才能找到索引。B+树的非叶子节点不存放实际的记录数据,仅存放索引,所以数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O次数会更少。
●插入和删除效率:B+ 树有大量的冗余节点,删除一个节点的时候,可以直接从叶子节点中删除,甚至可以不动非叶子节点,删除非常快。B+ 树的插入也是一样,有冗余节点,插入可能存在节点的分裂(如果节点饱和),但是最多只涉及树的一条路径。B 树没有冗余节点,删除节点的时候非常复杂,可能涉及复杂的树的变形。
●范围查询:B+ 树所有叶子节点间有一个链表进行连接,而 B 树没有将所有叶子节点用链表串联起来的结构,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。存在大量范围检索的场景,适合使用 B+树,比如数据库。而对于大量的单个索引查询的场景,可以考虑 B 树,比如nosql的MongoDB。
说一下索引失效的场景?
索引失效意味着查询操作不能有效利用索引进行数据检索,从而导致性能下降,下面一些场景会发生索引失效。
1使用OR条件:当使用OR连接多个条件,并且每个条件用到不同的索引列时,索引可能不会被使用。
2使用非等值查询:当使用!=或<>操作符时,索引可能不会被使用,特别是当非等值条件在WHERE子句的开始部分时。
3对列进行类型转换: 如果在查询中对列进行类型转换,例如将字符列转换为数字或日期,索引可能会失效。
4使用LIKE语句:以通配符%开头的LIKE查询会导致索引失效。
5函数或表达式:在列上使用函数或表达式作为查询条件,通常会导致索引失效。
6表连接中的列类型不匹配: 如果在连接操作中涉及的两个表的列类型不匹配,索引可能会失效。例如,一个表的列是整数,另一个表的列是字符,连接时可能会导致索引失效。
undo log、redo log、binlog 有什么用?
●undo log是Innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC。
●redo log是物理日志,记录了某个数据页做了什么修改,每当执行一个事务就会产生一条或者多条物理日志。
●binlog(归档日志)是Server层生成的日志,主要用于数据备份和主从复制。
什么是慢查询?原因是什么?可以怎么优化?
数据库查询的执行时间超过指定的超时时间时,就被称为慢查询。
原因:
●查询语句比较复杂:查询涉及多个表,包含复杂的连接和子查询,可能导致执行时间较长。
●查询数据量大:当查询的数据量庞大时,即使查询本身并不复杂,也可能导致较长的执行时间。
●缺少索引:如果查询的表没有合适的索引,需要遍历整张表才能找到结果,查询速度较慢。
●数据库设计不合理:数据库表设计庞大,查询时可能需要较多时间。
●并发冲突:当多个查询同时访问相同的资源时,可能发生并发冲突,导致查询变慢。
●硬件资源不足:如果MySQL服务器上同时运行了太多的查询,会导致服务器负载过高,从而导致查询变慢
优化:
1运行语句,找到慢查询的sql
2查询区分度最高的字段
3explain:显示mysql如何使用索引来处理select语句以及连接表,可以帮助选择更好的索引、写出更优化的查询语句
4order by limit形式的sql语句,让排序的表优先查
5考虑建立索引原则
Redis有什么优缺点?为什么用Redis查询会比较快
(1) Redis有什么优缺点?
Redis 是一个基于内存的数据库,读写速度非常快,通常被用作缓存、消息队列、分布式锁和键值存储数据库。它支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等, Redis 还提供了分布式特性,可以将数据分布在多个节点上,以提高可扩展性和可用性。但是Redis 受限于物理内存的大小,不适合存储超大量数据,并且需要大量内存,相比磁盘存储成本更高。
(2)为什么Redis查询快
●基于内存操作: 传统的磁盘文件操作相比减少了IO,提高了操作的速度。
●高效的数据结构:Redis专门设计了STRING、LIST、HASH等高效的数据结构,依赖各种数据结构提升了读写的效率。
●单线程:单线程操作省去了上下文切换带来的开销和CPU的消耗,同时不存在资源竞争,避免了死锁现象的发生。
●I/O多路复用:采用I/O多路复用机制同时监听多个Socket,根据Socket上的事件来选择对应的事件处理器进行处理。
Redis的数据类型有那些?
Redis 常见的五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及 Zset(sorted set:有序集合)。
1字符串STRING:存储字符串数据,最基本的数据类型。
2哈希表HASH:存储字段和值的映射,用于存储对象。
3列表LIST:存储有序的字符串元素列表。
4集合SET:存储唯一的字符串元素,无序。
5有序集合ZSET:类似于集合,但每个元素都关联一个分数,可以按分数进行排序。
Redis版本更新,又增加了几种数据类型,
●BitMap: 存储位的数据结构,可以用于处理一些位运算操作。
●HyperLogLog:用于基数估算的数据结构,用于统计元素的唯一数量。
●GEO: 存储地理位置信息的数据结构。
●Stream:专门为消息队列设计的数据类型。
Redis是单线程的还是多线程的,为什么?
Redis在其传统的实现中是单线程的(网络请求模块使用单线程进行处理,其他模块仍用多个线程),这意味着它使用单个线程来处理所有的客户端请求。这样的设计选择有几个关键原因:
1简化模型:单线程模型简化了并发控制,避免了复杂的多线程同步问题。
2性能优化:由于大多数操作是内存中的,单线程避免了线程间切换和锁竞争的开销。
3原子性保证:单线程执行确保了操作的原子性,简化了事务和持久化的实现。
4顺序执行:单线程保证了请求的顺序执行。
但是Redis的单线程模型并不意味着它在处理客户端请求时不高效。实际上,由于其操作主要在内存中进行,Redis能够提供极高的吞吐量和低延迟的响应。
此外,Redis 6.0 引入了多线程的功能,用来处理网络I/O这部分,充分利用CPU资源,减少网络I/O阻塞带来的性能损耗。
Redis持久化机制有哪些
●AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
●RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
●混合持久化方式:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;
缓存雪崩、击穿、穿透和解决办法
1缓存雪崩是指在某个时间点,大量缓存同时失效,导致请求直接访问数据库或其他后端系统,增加了系统负载。
对于缓存雪崩,可以通过合理设置缓存的过期时间,分散缓存失效时间点,或者采用永不过期的策略,再结合定期更新缓存。
1缓存击穿是指一个缓存中不存在但是数据库中存在的数据,当有大量并发请求查询这个缓存不存在的数据时,导致请求直接访问数据库,增加数据库的负载。典型的场景是当一个缓存中的数据过期或被清理,而此时有大量请求访问这个缓存中不存在的数据,导致大量请求直接访问底层存储系统。
对于缓存击穿,可以采用互斥锁(例如分布式锁)或者在查询数据库前先检查缓存是否存在,如果不存在再允许查询数据库,并将查询结果写入缓存。
1缓存穿透是指查询一个在缓存和数据库都不存在的数据,这个数据始终无法被缓存,导致每次请求都直接访问数据库,增加数据库的负载。典型的情况是攻击者可能通过构造不存在的 key 大量访问缓存,导致对数据库的频繁查询。
对于缓存穿透,可以采用布隆过滤器等手段来过滤掉恶意请求,或者在查询数据库前先进行参数的合法性校验。
如何保证数据库和缓存的一致性
Cache Aside
●原理:先从缓存中读取数据,如果没有就再去数据库里面读数据,然后把数据放回缓存中,如果缓存中可以找到数据就直接返回数据;更新数据的时候先把数据持久化到数据库,然后再让缓存失效。
●问题:假如有两个操作一个更新一个查询,第一个操作先更新数据库,还没来及删除缓存,查询操作可能拿到的就是旧的数据;更新操作马上让缓存失效了,所以后续的查询可以保证数据的一致性;还有的问题就是有一个是读操作没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,也会造成脏数据。
●可行性:出现上述问题的概率其实非常低,需要同时达成读缓存时缓存失效并且有并发写的操作。数据库读写要比缓存慢得多,所以读操作在写操作之前进入数据库,并且在写操作之后更新,概率比较低。
Read/Write Through
●原理:Read/Write Through原理是把更新数据库(Repository)的操作由缓存代理,应用认为后端是一个单一的存储,而存储自己维护自己的缓存。
●Read Through:就是在查询操作中更新缓存,也就是说,当缓存失效的时候,Cache Aside策略是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对调用方是透明的。
●Write Through:当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由缓存自己更新数据库(这是一个同步操作)。
Write Behind
●原理:在更新数据的时候,只更新缓存,不更新数据库,而缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作非常快,带来的问题是,数据不是强一致性的,而且可能会丢。
●第二步失效问题:这种可能性极小,缓存删除只是标记一下无效的软删除,可以看作不耗时间。如果会出问题,一般程序在写数据库那里就没有完成:故意在写完数据库后,休眠很长时间再来删除缓存。