索引 1 2 3 MySQL 索引使⽤的数据结构主要有 BTree 索引和哈希索引。 对于哈希索引来说,底层的数据结构就是哈希表,因此在绝⼤多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快; 其余⼤部分场景,建议选择 BTree 索引。
什么是索引 1 2 3 4 5 索引是一种用于快速查询和检索数据的数据结构。常见的索引结构有: B 树、B +树和 Hash 。 索引的作用就相当于目录的作用。 比如,我们在查字典的时候,如果没有目录,那我们就只能一页页的去找我们需要查的那个字,速度很慢。 如果有目录,我们只需要先去目录里查找字的位置,然后直接翻到那一页。
索引的优缺点 1 2 3 4 5 6 7 优点: 可以大大加快数据的检索速度(大大减少的检索的数据量),这也是创建索引的最主要的原因。 另外,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 缺点: 创建索引和维护索引耗费许多时间:当对表中的数据进行增删改的时候,如果数据有索引,那么索引也需要动态的修改,会降低SQL执行效率。 占用物理存储空间:索引需要使用物理文件存储,也会耗费一定空间。
B树和B+树区别 1 2 3 4 5 B 树的所有节点既存放键(key)也存放数据(data);而B +树只有叶子节点存放 key 和 data,其他内节点只存放key。B 树的叶子节点都是独立的;B +树的叶子节点有一条引用链指向与它相邻的叶子节点。B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了;而B +树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
Hash索引和B+树索引的优缺点 1 2 3 4 5 6 7 Hash 索引 优点:Hash 索引定位快 缺点:Hash 冲突问题、Hash 索引不支持顺序和范围查询 B+树索引 优点:单次请求涉及的磁盘IO次数少、查询效率稳定、遍历效率高 缺点:会产生大量的随机IO,主要存在以下两种情况:主键不是有序递增的、即使主键是有序递增的,大量写请求的分布仍是随机的
索引的类型 1 2 3 4 5 6 7 主键索引(Primary Key ) 数据表的主键列使用的就是主键索引。一张数据表有只能有一个主键,并且主键不能为null ,不能重复。 MySQL 的InnoDB的表中,当没有显示的指定表的主键时,InnoDB会自动先检查表中是否有唯一索引的字段,如果有,则选择该字段为默认的主键,否则InnoDB将会自动创建一个6Byte的自增主键。 二级索引(辅助索引) 又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。 唯一索引,普通索引,前缀索引等索引属于二级索引。
聚集索引 1 2 3 4 5 6 7 8 聚集索引:索引结构和数据一起存放的索引。主键索引属于聚集索引。 MySQL 中,InnoDB引擎的表的.ibd 文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B +树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。 优点:查询速度非常的快。因为整个B +树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。 缺点: 依赖于有序的数据(因为B +树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,插入或查找的速度比较慢)。 更新代价大(如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且况聚集索引的叶子节点还存放着数据,修改代价肯定是较大的)
非聚集索引 1 2 3 4 5 6 非聚集索引:索引结构和数据分开存放的索引。二级索引属于非聚集索引。 MySQL 中,MYISAM引擎的表的.MYI 文件包含了表的索引、.MYD 文件的数据包含了表的数据。 优点:更新代价比聚集索引要小。非聚集索引的叶子节点是不存放数据的。 缺点:非聚集索引也依赖于有序的数据、可能会二次查询(回表,当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询) 。
覆盖索引 1 2 3 覆盖索引:需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。 在InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
索引创建原则 1 2 3 4 5 6 单列原则:单列索引即由一列属性组成的索引。 联合索引:联合索引即由多列属性组成索引。 最左前缀原则:假设创建的联合索引由三个字段组成,在创建联合索引时,把查询最频繁的那个字段作为最左字段。 可能由于版本原因,创建的联合索引,相当于在联合索引的每个字段上都创建了相同的索引。
选择合适的字段 1 2 3 4 5 6 7 不为NULL 的字段:对于数据为NULL 的字段,数据库较难优化。如果避免不了为NULL ,建议使用0 ,1 ,true ,false 这样语义清晰的短字符作为替代。 被频繁查询的字段:创建索引的字段应该是查询操作非常频繁的字段。 被作为条件查询的字段:被作为WHERE 条件查询的字段,应该被考虑建立索引。 被经常频繁用于连接的字段:可以考虑建立索引,提高多表连接查询的效率。
不合适创建索引的字段 1 2 3 4 5 6 7 8 9 被频繁更新的字段应该慎重建立索引 不被经常查询的字段没有必要建立索引 尽可能的考虑建立联合索引而不是单列索引 注意避免冗余索引 字符串类型的字段上使用前缀索引代替普通索引:前缀索引仅限于字符串类型,较普通索引会占用更小的空间。
使用索引的查询性能 1 2 大多数情况下,索引查询都是比全表扫描要快的。 但是如果数据库的数据量不大,那么使用索引也不一定能够带来很大提升。
事务 什么是事务 1 2 3 4 5 事务是由N 步数据库操作组成的逻辑执行单元,要么都执行,要么都不执行。 最经典的例子就是转账了。假如小明要给小红转账1000 元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000 元,将小红的余额增加1000 元。 万一在这两个操作之间突然出现错误,导致小明余额减少而小红的余额没有增加,这样就不对了。 事务就是保证这两个关键操作要么都成功,要么要失败。
事务的特性 1 2 3 4 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性保证操作要么都执行,要么都不执行。 一致性(Consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的。 隔离性(Isolation ):并发访问数据库时,一个用户的事务不被其他事务所干扰。 持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
并发事务带来的问题 1 2 3 4 5 6 7 8 9 10 11 12 13 在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。 并发虽然是必须的,但可能会导致以下的问题。 丢失修改:某一个事务的(回滚、提交),导致另一个事务修改的数据丢失。 脏读:某一个事务,读取了另一个事务(未提交)的数据。 不可重复读:在一个事务内两次读到的数据不一致。 幻读:与不可重复读类似。它发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据。在随后的查询中,第一个事务就会发现多了一些原来不存在的记录,就好像发生了幻觉一样,所以称为幻读。 不可重复读的重点是修改,如多次读取一条记录发现其中某些列的值被修改; 幻读的重点在于新增或者删除,如多次读取一条记录发现记录数量增多或减少。
事务的隔离级别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 READ -UNCOMMITTED (读取未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。READ -COMMITTED (读取已提交):允许读取并发事务已经提交的数据,可以防止脏读,但是幻读或不可重复读仍有可能发生。REPEATABLE -READ (可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以防止脏读和不可重复读,但幻读仍有可能发生。SERIALIZABLE (可串行化):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,事务之间就完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。 MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE -READ (可重复读)。 可以通过 SELECT @@tx_isolation; 命令查看 注意:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 REPEATABLE -READ (可重复读)事务隔离级别下使用的是 Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server ) 是不同的。 所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE -READ (可重复读) 已经可以完全保证事务的隔离性要求,即达到了 SQL 标准的 SERIALIZABLE (可串行化) 隔离级别。 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ -COMMITTED (读取已提交),但是 InnoDB 存储引擎默认使用 REPEAaTABLE-READ (可重读) 并不会有任何性能损失。 InnoDB 存储引擎在分布式事务的情况下一般会用到 SERIALIZABLE (可串行化) 隔离级别。
锁 MyISAM 和 InnoDB 使用的锁 1 2 MyISAM 支持表级锁(table -level locking)。 InnoDB 支持行级锁(row -level locking)和表级锁,默认为行级锁。
锁分类(锁的粒度) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Mysql为了解决并发、数据安全的问题,使用了锁机制。 可以按照锁的粒度把数据库锁分为表级锁和行级锁。 表级锁:Mysql中锁定粒度最大的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。 其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。 行级锁:Mysql中锁定粒度最小的一种锁,只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。 其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。 InnoDB支持的行级锁,包括如下几种。 Record Lock : 对索引项加锁,锁定符合条件的行。其他事务不能修改和删除加锁项; Gap Lock : 对索引项之间的“间隙”加锁,锁定记录的范围(对第一条记录前的间隙或最后一条将记录后的间隙加锁)。其他事务不能在锁范围内插入数据。 Next-key Lock : 锁定索引项本身和索引范围。即 Record Lock 和 Gap Lock 的结合。可解决幻读问题。 使用行级索具有粒度小、并发度高等特点,但是表级锁有时候也是非常必要的: 事务更新大表中的大部分数据,直接使用表级锁效率更高; 事务比较复杂,使用行级锁很可能引起死锁导致回滚。
锁分类(是否可写) 1 2 3 4 5 表级锁和行级锁可以进一步划分为共享锁(s)和排他锁(X)。 共享锁(Share Locks):又称为读锁。如果事务T对数据对象A 加上S锁,其他事务只能再对A 加S锁,而不能加X锁,直到T释放A 上的S锁。 排它锁(Exclusive lock):又称为写锁。如果事务T对数据A 加上排他锁后,其他事务不能再对A 加任何类型的锁。获取排他锁的事务既能读数据,又能修改数据。
池化设计思想 数据库连接池 1 2 3 4 5 6 7 8 9 10 11 12 13 池化设计应该不是⼀个新名词。如java线程池、jdbc连接池、redis连接池等是这类设计的代表实现。 这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消耗,如创建线程的开销,获取远程连接的开销等。 除了初始化资源,池化设计还包括如下这些特征:池⼦的初始值、池⼦的活跃值、池⼦的最⼤值等, 这些特征可以直接映射到java线程池和数据库连接池的成员属性中。 数据库连接本质就是⼀个 socket 的连接。数据库服务端还要维护⼀些缓存和⽤户权限信息,所以占⽤了⼀些内存。 数据库连接池维护的数据库连接的缓存,将来需要对数据库的请求时可以重⽤这些连接。 为每个⽤户打开和维护数据库连接,尤其是对动态数据库驱动的⽹站应⽤程序的请求,既昂贵⼜浪费资源。 在连接池中,创建连接后,将其放置在池中,并再次使⽤它,因此不必建⽴新的连接。 如果使⽤了所有连接,则会建⽴⼀个新连接并将其添加到池中。连接池还减少了⽤户必须等待建⽴与数据库的连接的时间。
参考资料 https://snailclimb.gitee.io/javaguide/