回答这个问题首先一定要结合业务场景(我们当时有一个接口测试时候响应非常慢,压测大概5秒钟)
可以使用开源工具:阿尔萨斯(Arthas)、普罗米修斯(Prometheus)、Skywalking
可以使用explain或者desc来获取MySQL如何执行select语句的信息。其中可以查询到这个SQL语句执行过程中的一些字段。 主要是通过key和key_len来进行判断是否可能会命中索引。
MySQL的默认存储引擎是InnoDB,并且所有的存储引擎中只有InnoDB是事务性存储引擎。
InnoDB是MySQL的默认存储引擎,支持ACID事务,支持行级锁,外键约束等特性。
MyISAM是MySQL的另一种常见的存储引擎,具有较低的存储空间和内存消耗,适用于大量读操作的场景。然而,MyISAM不支持事务、行级锁和外键约束,因此在并发写入和数据完整性方面有一定的限制。主要用户查询多,增删改较少的场景。
Memory引擎将数据存储在内存中,适用于对性能要求较高的读操作,但是在服务器重启或崩溃时数据会丢失。它不支持事务、行级锁和外键约束。
优点: InnoDB是事务型存储引擎,它支持ACID特性,支持行级锁(一个事务对某行数据操作时,只会锁定某一行数据,不锁定其他行,效率高),提供了更好的MVCC机制,支持外键约束,支持缓存,支持全文索引。
缺点: 相较于MyISAM,InnoDB的存储和管理需要更多的内存和磁盘空间,同时也对CPU的要求较高。
优点: MyISAM简单易于管理,支持表锁(进行dml操作时会锁定整张表),主要用户查询多,增删改较少的场景。支持全文索引,存储表的总行数。
缺点: MyISAM不支持事务和行级锁,不支持主外键
总结
索引是一种用于快速查询和检索数据的数据结构,其本质可以看成是一种排序好的数据结构。它出现的作用就是为了加快查询效率。
索引的作用就相当于书的目录。打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一页的去找我们需要查的那个字,速度很慢。如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。
优点:
- 使用索引可以大大加快 数据的检索速度(大大减少检索的数据量), 这也是创建索引的最主要的原因。
- 提高数据库检索效率,降低数据库的IO成本(不需要全表扫描)。
- 通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗。
- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
缺点:
- 创建索引和维护索引需要耗费许多时间。当对表中的数据进行增删改的时候,如果数据有索引,那么索引也需要动态的修改,会降低 SQL 执行效率。
- 索引需要使用物理文件存储,也会耗费一定空间。
大多数情况下,索引查询都是比全表扫描要快的。但是如果数据库的数据量不大,那么使用索引也不一定能够带来很大提升。
常见的索引结构有: B 树, B+树 和 Hash。在 MySQL 中,无论是 Innodb 还是 MyIsam,都使用了 B+树作为索引结构。B+树索引按照索引列的值进行排序,并将数据分层存储在索引树的节点中,这样可以通过比较索引值,快速定位到符合条件的数据行。
为什么不使用红黑树呢,红黑树是平衡的二叉树,也是能够稳定查找数据。但是红黑树是二叉树,每个节点最多只能两个孩子节点,所以当某个数据库表中的数据非常大的时候,红黑树将非常的高,查找效率也会不大。B+树是多叉树,阶数更多,路径更短。
数据库的索引和数据都是存储在硬盘中的,我们可以把读取一个节点当作一次批判IO操作。B+树存储千万级的数据只需要3-4层高度就可以满足,着意味着千万级的表查询目标数据最多需要3-4次磁盘IO,所以相较于B树和红黑树磊说,磁盘读写代价更低,查询效率高。
B+和B,B+树旨在叶子节点存储数据,而B树非叶子节点也要存储数据,所以B+树的单个节点的数据量更小,在相同的磁盘IO次数下,就能查询更多的节点。还有B+树叶子节点采用的是双链表结构,适合MySQL中常见的基于范围的顺序查找,B树在这一点是无法做到的。
B+和Hash,Hash在做等职查询的时候效率非常快,搜索复杂度仅为O(1),但是Hash表不适合做范围查询,它更适合做等值的查询,这也是B+树索引要比Hash表索引有更广泛适用场景的原因。
底层存储方式:
聚簇索引:所查即所得,找到了索引就是找到了数据,创建原则就是有主键就默认主键。数据与索引是放在一块的,B+树的叶子节点存放了整行数据,并且有且只有一个。
非聚簇索引(二级索引):索引和数据是分离的,B+树的叶子节点存储的是对应的主键,需要根据主键,再次回表查询,在MyISAM引擎中,除了主键列,其他都是非聚簇索引。
回表查询:通过二级索引找到对应的主键值,然后通过主键去聚簇索引中查找整行数据,这个过程就是回表。
按照应用维度分:
主键索引:查询加速,列值唯一(不能为NULL),表中只有一个
唯一索引:查询加速,列值唯一
覆盖索引: 查询使用了索引,返回的列中必须在索引中全部能找见。使用id查询,直接走聚集索引查询,一次可以查询到所有的数据,性能高。如果返回的列中没有创建索引,就会触发回表查询,所以在平时的开发中尽量避免使用select*。
联合索引(聚集索引):多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,与其他索引类型不同,全文索引并不存储整个字符串的值,而是存储关键字
超大分页一般是在数据量比较大的时候,我们使用limit分页查询,并且需要对数据进行排序,这个时候效率就很低,我们可以采用覆盖索引和子查询来解决。先分页查询数据的id字段,确定了id之后,再用子查询来过滤,只查询这个id列表中的数据就可以了,因为查询id的时候走的是覆盖索引,所以效率会提升很多。
哪些情况创建索引
哪些情况不建议创建索引
事务就是逻辑上的一组操作,在同一个事务中,如果有多条sql语句执行,要么都执行,要么都不执行。
关系型数据库都有ACID的特点
A(原子性)、C(一致性)、I(隔离性)、D(持久性)
原子性:事务是一个不可分割的工作单位,事务的中间操作要么全部完成,要么全部不做,不可能停滞在中间环节。
隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
持久性:事务一旦提交,数据就持久保存在硬盘中
一致性:执行事务前后,数据保持一致,比如转账,不管成功与否,转账人与收账人总金额保持不变
只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。
查看隔离级别
SELECT @@global.transaction_isolation,@@transaction_isolation;
设置隔离级别
-- 设置左边的
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 设置右边的
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
读未提交:一个事务可以读取到另一个事务未提交的数据,这会带来脏读(垃圾数据,因为A事务可能会回滚),幻读,不可重复读问题,将隔离级别改为读已提交,可以解决脏读问题。
读已提交:一个事务只能读取另一个事务已经提交的数据,其避免了脏读,但仍然存在不可重复读和幻读问题
不可重复度: A事务中对数据库进行了两次查询,在两次查询中,B事务修改了数据库中的数据,导致A事务中两次查询的数据不同,这就是不可重复度
可重复读:同一个事务中多次读取相同的数据返回的结果是一样的,可以避免脏读和不可重复读,但可能会导致幻读。
幻读: 可重复读隔离级别—>一个事务查询一个范围内的数据时,另一个并发事务向这个范围内添加了一个新的数据,当之间的事务再次查询这个范围的数据时,就会发现之前没有的记录,这就是幻读。
串行化:最高的隔离级别,事务串行执行,事务不存在并发执行,可以避免脏读、不可重复读和幻读,但是效率低下.
在MySQL中提供了两种概念,一个缓冲池(buffer pool),一个数据页(page)。
InnoDB存储引擎提供了两种事务日志,redolog(重做日志)和undolog(回滚日志),redolog用于保证事务持久性,undolog则是事务原子性和一致性实现的。
**原子性的实现:**当事务回滚时能撤销所有已执行的sql语句。这个操作依赖的时undolog日志,undolog属于逻辑日志,里面记录的都是执行过所有操作的相反操作,用于回滚后将已执行的sql语句进行撤销。
**持久性的实现:**redolog叫做重做日志,用于记录事务中所有的修改操作,包括修改前和修改后的数据,这样即使系统崩溃,这些修改也不会丢失,系统恢复后,MySQL可以读取日志,重新执行这些操作。
多版本并发控制 Multi-Version Concurrent Control,是MySQL提高性能的一种方式,就是配合undolog使事务可以并发执行。
读未提交: 没有特殊的并发控制机制,读操作不会获取任何锁,在读取数据之前不会进行任何检查。
读已提交: 读已提交又称当前读,每次读的时候都会给版本链拍照,所以读到的数据都是最新的(已提交)。这是通过在读取数据时加上共享锁,然后在读取完成后立即释放锁来实现的。
可重复读: 快照读使用行级锁或快照隔离,当事务开始之后,第一次读取会给版本拍照,下次读取直接从版本快照中直接读取,所以一个事务中读取到的数据是一直的。
每次执行修改操作时,MySQL不会直接修改原始数据,而是创建一个新的版本。这个新版本包含了修改后的数据,以及生成这个版本的事务ID。当一个事务需要读取数据时,MySQL会根据事务的隔离级别和事务ID,从版本链中选择一个合适的版本。这样,即使在并发执行多个事务的情况下,每个事务也都能看到一个一致的数据视图。
MySQL中的锁按粒度分,主要分为全局锁、表锁、行级锁
然后全局锁主要分为共享锁和排他锁,在全局锁中,是锁住所有的数据库表,放置的是备份操作中进行的操作影响最终备份的结果,共享锁可以和共享锁不排斥,共享锁和排他锁排斥,即加共享锁时,排他锁只能等候,排他锁和任何锁都互斥。
表锁主要分为表锁,元数据锁,意向锁,表锁也主要分为共享锁和排他锁,在元数据锁中,对一张表进行操作的时候,数据库会自动添加元数据锁,然后操作完成自动关闭。意向锁就是当修改一行数据时,有一个表锁,这时候要加表锁的话需要去进行全表查询,所以数据库就是用意向锁,当进行增删操作时,添加行锁,同时为这个表添加一个意向锁,当有其他表锁过来是,先去判断意向锁是不是排斥的,如果排斥就等待,这里意向锁和意向锁之间不会排斥。
行级锁分为行锁、间隙锁、临键锁,行锁分为共享锁和排他锁,间隙锁的话就是用来锁住数据与数据之间的间隙,防止事务在操作过程中突然添加数据,出现幻读,临键锁是锁住中间的间隙和两边的数据,方式出现幻读。
MySQL中的锁主要分为两种:共享锁和排他锁,共享锁允许多个事务同时获取相同资源的读取访问权限,而排他锁则只允许一个事务获取资源的写入访问权限。
如果数据库场景读的操作比较多的时候,为了避免写的操作所造成的性能影响,可以采用读写分离的架构。不让数据的写入影响了读操作。
MySQL主从复制的核心就是二进制日志,二进制日志(binlog)记录了所有的DDL(数据定义语言)和DML(数据操纵语言)语句,但不包括数据查询语句。
数据量特别大,比如一张表的数据超过500万,就可以考虑。
但是现在分库分表用的比较少了,因为现在硬件的性能非常高,一般一张MySQL表里面的数据都可以存到1亿左右,而且还不卡,因为都是通过内存淘汰链表和Free链表来更新数据。目前最多就是用用分库,分表几乎用不到了,因为10年前那会一张表一般就存储2000万数据。