Java面试基础篇-MySQL

MySQL存储引擎

查看引擎命令:show engines;
查看MySQL当前默认的存储引擎:show variables like ‘%storage_engine%’;
在这里插入图片描述

MyISAM和InnoDB区别

MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,而且提供了大量的特性,包括全文索 引、压缩、空间函数等,但MyISAM不支持事务和行级锁,所以一般用于有大量查询少量插入的场景来使用,而且myisam不支持外键,并且索引和数据是分开存储的。不 过,5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为 InnoDB。
两者的对比:

  1. 是否支持行级锁 : MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row- level locking)和表级锁,默认为行级锁。
  2. 是否支持事务和崩溃后的安全恢复: MyISAM 强调的是性能,每次查询具有原子性,其执行速度 比InnoDB类型更快,但是不提供事务支持。但是InnoDB 提供事务支持事务,外部键等高级数据 库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities) 的事务安全(transaction-safe (ACID compliant))型表。
  3. 是否支持外键: MyISAM不支持,而InnoDB支持。
  4. 是否支持MVCC :仅 InnoDB 支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在
    READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作;MVCC可以使用 乐观 (optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。

索引

为何默认不用hash索引而用BTree索引

  1. Hash 索引仅仅能满足"=",“IN"和”<=>"查询,不能使用范围查询。
  2. Hash 索引无法被用来避免数据的排序操作。
  3. Hash 索引不能利用部分索引键查询。
  4. Hash 索引在任何时候都不能避免表扫描。
  5. Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。

为什么Mysql用B+树做索引而不用B-树或红黑树

二叉查找树结构由于树的深度过大而造成磁盘I/O读写过于频繁,进而导致查询效率低下。
B树又可以写成B-树/B-Tree。

m 阶的定义:一个节点能拥有的最大子节点数来表示这颗树的阶数
举个例子:
如果一个节点最多有 n 个key,那么这个节点最多就会有 n+1 个子节点,这棵树就叫做 n+1(m=n+1)阶树

索引大小:B+树只有叶节点存放数据,其余节点用来索引,而B-树是每个索引节点都会有Data域。所以从Mysql(Inoodb)的角度来看,B树是用来充当索引的,一般来说索引非常大。

磁盘IO次数:B-树/B+树 的特点就是每层节点数目非常多,层数很少,目的就是为了就少磁盘IO次数,但是B-树的每个节点都有data域(指针),这无疑增大了节点大小,说白了增加了磁盘IO次数(磁盘IO一次读出的数据量大小是固定的,单个数据变大,每次读出的就少,IO次数增多,一次IO多耗时),而B+树除了叶子节点其它节点并不存储数据,节点小,磁盘IO次数就少。

顺序访问:B+树所有的Data域在叶子节点,一般来说都会进行一个优化,就是将所有的叶子节点用指针串起来。这样遍历叶子节点就能获得全部数据,这样就能进行区间访问啦。在数据库中基于范围的查询是非常频繁的,而B树不支持这样的遍历操作。

B树相对于红黑树的区别
AVL 数和红黑树基本都是存储在内存中才会使用的数据结构。在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况。

数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

B+树索引

查看表索引
show index from t_org

请添加图片描述

1、Table 表名

2、Non_unique 如果索引不能包括重复值则为0,如果可以则为1。也就是平时所说的唯一索引。

3、Key_name 索引名称,如果名字相同则表明是同一个索引,而并不是重复,比如上图中的第二、三条数据,索引名称都是index_fID_lastTime,其实是一个联合索引。

4、Seq_in_index 索引中的列序列号,从1开始。上图中的二、三条数据,Seq_in_index一个是1一个是2,就是表明在联合索引中的顺序,我们就能推断出联合索引中索引的前后顺序。

5、Column_name 索引的列名。

6、Collation 列以什么方式存储在索引中,大概意思就是字符序。在MySQLSHOW INDEX语法中,有值’A’(升序)或NULL(无分类)。默认的类型是utf8_general_ci,这样的大小写不敏感,比如下面两个sql会出现相同的查询结果:

  select * from Table where content = 'Yes'
  select * from Table where content = 'yes'

这样可能不符合你的要求,你需要大小写敏感的情况,你可以修改字段字符集类型,如下sql

alter table bbs_posts modify column content varchar(5000) NOT NULL collate utf8_bin;

这样修改以后就OK了。

7、Cardinality 基数的意思,表示索引中唯一值的数目的估计值。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机会就越大。我们知道某个字段的重复值越少越适合建索引,所以我们一般都是根据Cardinality来判断索引是否具有高选择性,如果这个值非常小,那就需要重新评估这个字段是否适合建立索引。因为MySQL数据库中有各种不同的存储引擎,而每种存储引擎对于B+树索引的实现又各不相同。所以对Cardinality统计时放在存储引擎层进行的,至于它是如何统计更新的在这里就不再做更深入的介绍了。

8、Sub_part 前置索引的意思,如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。如上图所示,除了index_content那行显示4外,其他的都是NULL,表明index_content是一个长度为4的前置索引。对于BLOB,TEXT,或者很长的VARCHAR类型的列,必须使用前缀索引,因为MySQL不允许索引这些列的完整长度,这会让索引变得大且慢。选择长度的诀窍在于要选择足够长的前缀以保证较高的选择性,同时又不能太长以便节约空间。下面是计算前置索引长度的一般方法:

  select count(distinct left(content,3))/count(*) from bbs_posts as sel3

  select count(distinct left(content,4))/count(*) from bbs_posts as sel4

  select count(distinct left(content,5))/count(*) from bbs_posts as sel5

 最后算出来那个长度的基数接近完整列的选择行就OK了,完整列 select count(distinct content)/count(*) from bbs_posts

9、Packed 指示关键字如何被压缩。如果没有被压缩,则为NULL。压缩一般包括压缩传输协议、压缩列解决方案和压缩表解决方案。

10、Null 如果列含有NULL,则含有YES。比如上图中的lastOperateTime其中就包含null,我们知道建立索引的列是不允许为Null的,单列索引不存Null值,复合索引不存全为Null的值,如果列允许为Null,可能会得到“不符合预期”的结果集。我这里是为了更好的给大家展示故意构造了一些数据。

11、Index_type 索引类型,Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE。

1). FULLTEXT
即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。
2). HASH
由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。    HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。
3). BTREE    BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。这是MySQL里默认和最常用的索引类型。
4). RTREE
RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。相对于BTREE,RTREE的优势在于范围查找。

12、 Comment Index_comment 注释的意思。

聚集索引:B+树是左小右大的顺序存储结构,节点只包含id索引列,而叶子节点包含索引列和数据,这种数据和索引在一起存储的索引方式叫做聚簇索引,一张表只能有一个聚簇索引。
假设没有定义主键,InnoDB会选择一个唯一的非空索引代替,如果没有的话则会隐式定义一个主键作为聚簇索引。
非聚簇索引:非聚簇索引(二级索引)保存的是主键id值,这一点和myisam保存的是数据地址是不同的。
在这里插入图片描述

联合索引 (a,b)

  • 联合索引指对表上多列进行索引。
  • 联合索引是键值都是有序的。
select * from t where a = xxx and b = xxxx; 
select * from t where a = xxx; (a,b)(a)都是有序的,都可以使用到联合索引。 
select * from t where b = xxx;因为b不是排序的所以无法使用到索引。
  • 联合索引的第二个好处是已经对第二个键值进行了排序处理。
    下列语句是可以用到索引的: select * from t where a = xxx and order by b;

覆盖索引

即从辅助索引中就可以查询得到的记录,而不需要查询聚集索引中的记录。
使用覆盖索引的好处是辅助索引不包含整行记录的所有信息,故其远小于聚集索引,因此减少大量IO。
在通常条件下,诸如(a,b)的联合索引,一般是不可用选择b列中的查询条件的,但是对于统计操作,并且是覆盖索引,可以使用。

优化器选择不使用索引的情况

  • 范围扫描
    索引不能覆盖到我们要查找的信息
  • JOIN链接操作
    连表条件没有索引
  • 模糊搜索:左模糊或全模糊都会导致索引失效,比如’%a’和’%a%‘。但是右模糊是可以利用索引的,比如’a%’ 。
  • 隐式类型转换
  • 联合索引不符合最左前缀匹配原则
    (a,b,c) 相当于创建了 (a) (a,b) (a,b,c) 三个索引
  • 如果查询中有某个列的范围查询,则其右边所有的列都无法使用索引优化查找。例如查询Where last_name = ‘Smath’ and irst_name like ‘%J’ and dob=‘1970-02-01’.这个查询只能使用索引的前两列,因为like是一个范围条件。

查询优化

  1. 索引下推
    索引条件下推 (ICP) 是针对 MySQL 使用索引从表中检索行的情况的优化。如果没有 ICP,存储引擎将遍历索引以查找基表中的行,并将它们返回给 MySQL 服务器,该服务器会评估行的 WHERE 条件。启用ICP后,如果只能使用索引中的列来评估 WHERE 部分条件,则MySQL服务器会将这部分 WHERE 条件向下推送到存储引擎。然后,存储引擎使用索引条目评估推送的索引条件,并且仅当满足此条件时,才会从表中读取行。ICP可以减少存储引擎访问基表的次数,减少MySQL服务器访问存储引擎的次数。

对于 InnoDB 表,ICP 仅用于二级索引。ICP 的目标是减少整行读取次数,从而减少 I/O 操作。对于 InnoDB 聚集索引,完整记录已读入 InnoDB 缓冲区。在这种情况下,使用 ICP 不会减少 I/O。

  1. ORDER BY优化
SELECT * FROM t1
  ORDER BY key_part1, key_part2;

假设 上 (key_part1, key_part2) 有一个索引,以下查询可以使用该索引来解析该 ORDER BY 部分。
但是,查询使用 SELECT * ,它选择的列可能多于 key_part1 和 key_part2 。在这种情况下,扫描整个索引并查找表行以查找不在索引中的列可能比扫描表并对结果进行排序更昂贵。如果是这样,则优化器可能不使用索引。如果 SELECT * 仅选择索引列,则使用索引并避免排序。

全文索引

全文检索是将存储与数据库中的整本书或者整篇文章中的任意内容信息查找出来的技术。可以根据需要获得本文中有关章、节、段、句等信息。

倒排索引

倒排索引在辅助表中存储了单词和单词自身在一个或者多个文档中所在位置之间的映射。这通常利用关联数组来实现:
inverted file index,其表现形式为{单词,单词所在文档的ID}
full inverted index,表现形式为{单词,(单词所在文档ID,单词在文档中具体位置)}

InnoDB全文检索

InnoDB从1.2.x版本开始支持全文检索的技术,采用full inverted index方式

哈希索引

哈希索引限制

  • 哈希索引只保存哈希码和指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过访问内存中的行速度非常快(因为是MEMORY引擎),所以对性能影响并不大
  • 哈希索引数据并不是按照索引值顺序存储的,所以无法用于排序。
  • 哈希索引不支持部分索引列查找,因为哈希索引始终是使用索引列的全部内容来计算哈希码。
    如在数据列(A,B)上建立哈希索引,如果查询只有数据列A,则无法使用该哈希索引
  • 哈希索引只支持等值比较查询,包括=、IN()、<=>,不支持范围查询,如where price > 100

事务

事物的四大特性(ACID)

  • 原子性(Atomicity): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部 完成,要么完全不起作用;
  • 一致性(Consistency): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是 相同的;
  • 隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务 之间数据库是独立的;
  • 持久性(Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据 库发生故障也不应该对其有任何影响。

并发事务带来哪些问题

  • 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是 不正确的。
  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修 改结果就被丢失,因此称为丢失修改。
    例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
  • 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不 一样的情况,因此称为不可重复读。
  • 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

不可重复读和幻读区别:
不可重复读:update.
幻读:delete,insert.
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者 删除比如多次读取一条记录发现记录增多或减少了。

事务隔离级别

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导 致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读 或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及 幻读。

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。

锁机制与InnoDB锁算法

MyISAM和InnoDB存储引擎使用的锁:
MyISAM采用表级锁(table-level locking)。
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

表级锁和行级锁对比:

  • 表级锁:
    MySQL中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也 比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低, MyISAM和 InnoDB引擎都支持表级锁。
  • 行级锁:
    MySQL中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减 少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死 锁。

InnoDB存储引擎的锁的算法有三种:

  • Record lock:单个行记录上的锁
  • Gap lock:间隙锁,锁定一个范围,不包括记录本身
  • Next-key lock:record+gap 锁定一个范围,包含记录本身
    Next-locking keying为了解决Phantom Problem幻读问题
    当查询的索引含有唯一属性时,将next-key lock降级为record key

MVCC(多版本并发控制)

在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。 在可重读Repeatable reads事务隔离级别下:

  • SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
  • INSERT时,保存当前事务版本号为行的创建版本号
  • DELETE时,保存当前事务版本号为行的删除版本号
  • UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行

通过MVCC读取出来的数据其实是历史数据,而不是最新数据,这在一些对于数据时效特别敏感的业务中,很可能出问题,这也是MVCC的短板之处,有办法解决吗? 当然有.

MCVV这种读取历史数据的方式称为快照读(snapshot read),而读取数据库当前版本数据的方式,叫当前读(current read).

快照读
我们平时只用使用select就是快照读,这样可以减少加锁所带来的开销.

select * from table ....

当前读
对于会对数据修改的操作(update、insert、delete)都是采用当前读的模式。在执行这几个操作时会读取最新的记录,即使是别的事务提交的数据也可以查询到。假设要update一条记录,但是在另一个事务中已经delete掉这条数据并且commit了,如果update就会产生冲突,所以在update的时候需要知道最新的数据。读取的是最新的数据,需要加锁。以下第一个语句需要加共享锁,其它都需要加排它锁。

select * from table where ? lock in share mode; 
select * from table where ? for update; 
insert; 
update; 
delete;

在可重复度的隔离级别下,情况就完全不同了.事务1在update后,对该数据加锁,事务B无法插入新的数据,这样事务A在update前后数据保持一致,避免了幻读,可以明确的是,update锁的肯定不只是已查询到的几条数据,因为这样无法阻止insert。

Next-Key锁
锁定一个范围,包含记录本身。防止其他事务对当前数据进行操作,进而防止幻读的发生。

可重复读能防不可重复读,还能防幻读,它能防住所有的幻读吗?当然不是,比如如下的例子:

在这里插入图片描述

数据库状态的快照适用 SELECT于事务中的语句,不一定适用于 DML语句。如果您插入或修改某些行然后提交该事务,则从另一个并发 事务发出的 DELETE或者UPDATE语句REPEATABLE READ可能会影响那些刚刚提交的行,即使会话无法查询它们。

一致的非锁定读取

ACID靠什么保证的

  • A原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
  • C一致性一般由代码层面来保证
  • I隔离性由MVCC来保证
  • D持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redolog刷盘,宕机的时候可以从redo log恢复

主从同步原理

1.master提交完事务后,写入binlog
2.slave连接到master,获取binlog
3.master创建dump线程,推送binglog到slave
4.slave启动一个IO线程读取同步过来的master的binlog,记录到relay log中继日志中
5.slave再开启一个sql线程读取relay log事件并在slave执行,完成同步
6.slave记录自己的binglog
在这里插入图片描述

大表优化

限定数据的范围
务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以 控制在一个月的范围内;

读/写分离
经典的数据库拆分方案,主库负责写,从库负责读;

垂直分区
根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信 息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说 大家应该就更容易理解了。

水平分区
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了 分布式的目的。 水平拆分可以支撑非常大的数据量。

分库分表

数据库瓶颈

1、IO瓶颈
第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表。

第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库。

2、CPU瓶颈
第一种:SQL问题,如SQL中包含join,group by,order by,非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,在业务Service层进行业务计算。

第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 -> 水平分表。

分库分表

  1. 水平分库
    概念:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。
    特点:每个库的结构都一样;
    每个库的数据都不一样,没有交集;
    所有库的并集是全量数据
  2. 水平分表
    概念:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。
    特点:每个表的结构都一样;
    每个表的数据都不一样,没有交集;
    所有表的并集是全量数据;

数据分片规则

1.根据数值范围
按照时间区间或ID区间来切分。例如:按日期将不同月甚至是日的数据分散到不同的库中;将userId为1-9999的记录分到第一个库,10000~20000的分到第二个库,以此类推。某种意义上,某些系统中使用的"冷热数据分离",将一些使用较少的历史数据迁移到其他库中,业务功能上只提供热点数据的查询,也是类似的实践。

这样的优点在于:
单表大小可控
天然便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题。

缺点:
热点数据成为性能瓶颈。连续分片可能存在数据热点,例如按时间字段分片,有些分片存储最近时间段内的数据,可能会被频繁的读写,而有些分片存储的历史数据,则很少被查询
2.根据数值取模
一般采用hash取模mod的切分方式,例如:将 Customer 表根据 cusno 字段切分到4个库中,余数为0的放到第一个库,余数为1的放到第二个库,以此类推。这样同一个用户的数据会分散到同一个库中,如果查询条件带有cusno字段,则可明确定位到相应库去查询。

优点:
数据分片相对比较均匀,不容易出现热点和并发访问的瓶颈

缺点:
后期分片集群扩容时,需要迁移旧的数据(使用一致性hash算法能较好的避免这个问题)容易面临跨分片查询的复杂问题。比如上例中,如果频繁用到的查询条件中不带cusno时,将会导致无法定位数据库,从而需要同时向4个库发起查询,再在内存中合并数据,取最小集返回给应用,分库反而成为拖累。

跨节点关联查询 join 问题

  1. 全局表
    全局表,也可看做是"数据字典表",就是系统中所有模块都可能依赖的一些表,为了避免跨库join查询,可以将这类表在每个数据库中都保存一份。这些数据通常很少会进行修改,所以也不担心一致性的问题。
  2. 字段冗余
    一种典型的反范式设计,利用空间换时间,为了性能而避免join查询。例如:订单表保存userId时候,也将userName冗余保存一份,这样查询订单详情时就不需要再去查询"买家user表"了。
    但这种方法适用场景也有限,比较适用于依赖字段比较少的情况。而冗余字段的数据一致性也较难保证,就像上面订单表的例子,买家修改了userName后,是否需要在历史订单中同步更新呢?这也要结合实际业务场景进行考虑。
  3. 数据组装
    在系统层面,分两次查询,第一次查询的结果集中找出关联数据id,然后根据id发起第二次请求得到关联数据。最后将获得到的数据进行字段拼装。
  4. ER分片
    关系型数据库中,如果可以先确定表之间的关联关系,并将那些存在关联关系的表记录存放在同一个分片上,那么就能较好的避免跨分片join问题。在1:1或1:n的情况下,通常按照主表的ID主键切分。

分表后唯一ID

  1. UUID
    优点:生成足够简单,本地生成无网络消耗,具有唯一性缺点:无序的字符串,不具备趋势自增特性,没有具体的业务含义。如此长的字符串当MySQL主键并非明智选择。
  2. 基于数据库自增ID
    优点:实现简单,ID单调自增,数值类型查询速度快
    缺点:DB单点存在宕机风险,无法扛住高并发场景
  3. 基于数据库集群模式
    不同的Mysql实例设置不同的起始值和自增步长。
    优点:解决DB单点问题
    缺点:不利于后续扩容,而且实际上单个数据库自身压力还是大,依旧无法满足高并发场景。
  4. 基于数据库的号段模式
    号段模式是当下分布式ID生成器的主流实现方式之一,号段模式可以理解为从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存。等这批号段ID用完,再次向数据库申请新号段,对max_id字段做一次update操作,update max_id= max_id + step,update成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]。
  5. 基于Redis模式
    Redis 也同样可以实现,原理就是Redis 是单线程的,因此我们可以利用redis的incr命令实现ID的原子性自增。
  6. 基于雪花算法(Snowflake)模式
    SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。

开源产品

  1. 百度uid-generator
    GitHub地址:https://github.com/baidu/uid-generator
  2. 美团(Leaf)
    github地址:https://github.com/Meituan-Dianping/Leaf
  3. 滴滴(Tinyid)
    Github地址:https://github.com/didi/tinyid
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值