1、mysql的char和varchar有什么区别

char是固定长度,varchar长度可变:
char(n) 和 varchar(n) 中括号中 n 代表字符的个数,并不代表字节个数,比如 CHAR(30) 就可以存储 30 个字符。
存储时,前者不管实际存储数据的长度,直接按 char 规定的长度分配存储空间;而后者会根据实际存储的数据分配最终的存储空间
相同点:

  • char(n),varchar(n)中的n都代表字符的个数
  • 超过char,varchar最大长度n的限制后,字符串会被截断。
    不同点:
  • char不论实际存储的字符数都会占用n个字符的空间,而varchar只会占用实际字符应该占用的字节空间加1(实际长度length,0<=length<255)或加2(length>255)。因为varchar保存数据时除了要保存字符串之外还会加一个字节来记录长度(如果列声明长度大于255则使用两个字节来保存长度)。
  • 能存储的最大空间限制不一样:char的存储上限为255字节。
  • char在存储时会截断尾部的空格,而varchar不会。

char是适合存储很短的、一般固定长度的字符串。例如,char非常适合存储密码的MD5值,因为这是一个定长的值。对于非常短的列,char比varchar在存储空间上也更有效率。

2、mysql有哪些存储引擎?都有哪些区别?

MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得额外的速度或者功能,从而改善你的应用的整体功能。这些不同的技术以及配套的相关功能在 MySQL中被称作存储引擎(也称作表类型)
在mysql客户端中,使用show engines;命令可以查看MySQL支持的引擎。

2.1、InnoDB

InnoDB是一个健壮的事务型存储引擎,这种存储引擎已经被很多互联网公司使用,为用户操作非常大的数据存储提供了一个强大的解决方案。InnoDB就是一般作为默认的存储引擎。InnoDB还引入了行级锁定和外键约束,在以下场合下,使用InnoDB是最理想的选择:

  • 更新密集的表。InnoDB存储引擎特别适合处理多重并发的更新请求。
  • 事务。InnoDB存储引擎是支持事务的标准MySQL存储引擎。
  • 自动灾难恢复。与其它存储引擎不同,InnoDB表能够自动从灾难中恢复。
  • 外键约束。MySQL支持外键的存储引擎只有InnoDB。
  • 支持自动增加列AUTO_INCREMENT属性。

一般来说,如果需要事务支持,并且有较高的并发读取频率,InnoDB是不错的选择。

2.2、MyISAM

MyISAM在磁盘上存储成3个文件,其中文件名和表名都相同:

  • .frm(存储表定义)
  • MYD(MYData,存储数据)
  • MYI(MYIndex,存储索引)

特点:

  • 并发性与锁级别(表级锁,读写互斥),因此MyISAM对读写混合操作的并发性支持并不会很好。
  • 表损害修复,Myisam支持因任何意外关闭而损害的myisam表进行检查和修复操作。修复中也可能造成数据的丢失。

    • check table tableName 进行检查,
    • repair table tableName进行修复。
    • myisamchk —help 这个工具也可以用来修复。(需要将mysql服务停止,如果是运行中执行该命令,可能会对表造成更大得损坏)

      3、mysql机读顺序

      机读从from开始读
      image.png
      image.png

      4、JOIN

      Mysql 不支持full outer,使用left+right+union解决
      image.png

      5、什么是索引

      索引是一种数据结构,是帮助MYSQL高效获取数据的数据结构,也可以说是排好序的快速查找数据结构。
      数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。
      索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上
      优势:
  • 提高数据检索的效率,降低了数据库的IO成本

  • 降低了数据排序的成本,降低了CPU的消耗

劣势:

  • 虽然提高了查询速度,但是会降低更新表的速度

    5.1、索引的分类

    一张表索引最好不好超过五个

  • 单值索引:即一个索引只包含单个列,一个表可以有多个单列索引

  • 唯一索引:索引列的值必须唯一,但允许有空值
  • 复合索引:即一个索引包含多个列

    5.2、哪些情况需要创建索引

  • 主键自动建立唯一索引

  • 频繁作为查询条件的字段
  • 查询中与其他表关联的字段,外键关系建立索引
  • 单键/组合索引的选择问题,高并发下倾向创建组合索引
  • 查询中排序的字段,排序字段通过索引访问大幅提高排序速度
  • 查询中统计或分组字段

    5.3、哪些情况不需要创建索引

  • 表记录太少

  • 经常增删改的表
  • 数据重复且分布均匀的表字段,只应该为最经常查询和最经常排序的数据列建立索引(如果某个数据类包含太多的重复数据,建立索引没有太大意义)
  • 频繁更新的字段不适合创建索引(会加重IO负担)
  • where条件里用不到的字段不创建索引

    6、explain

    image.png
    主要两个字段
    type:这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行,查找数据行记录的大概范围。依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL一般来说,得保证查询达到range级别,最好达到ref
    extra:

  • Using filesort

mysql 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。此时mysql会根据联接类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。这种情况下一般也是要考虑使用索引来优化的。

  • Using temporary

在许多查询的执行过程中,MySQL可能会借助临时表来完成一些功能,比如去重、排序之类的,比如我们在执行许多包含DISTINCT、GROUP BY、UNION等子句的查询过程中,如果不能有效利用索引来完成查询,MySQL很有可能寻求通过建立内部的临时表来执行查询

  • Using index

表示相应的select操作用使用覆盖索引,避免访问了表的数据行。如果同时出现using where,表名索引被用来执行索引键值的查找;如果没有同时出现using where,表名索引用来读取数据而非执行查询动作。

覆盖索引(Covering Index),或者叫索引覆盖,也就是平时所说的不需要回表操作。

  • 就是select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。
  • 索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据,当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含(覆盖)满足查询结果的数据就叫做覆盖索引。
  • 判断标准使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为using index,MySQL查询优化器在执行查询前会决定是否有索引覆盖查询

    7、事务

  • 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  • 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
  • 隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

    7.1、并发事务产生的问题

    7.1.1、脏读

    读取到了未提交的数据(如果事务这时候回滚了,那么第二个事务就读到了脏数据)

    7.1.2、不可重复读

    同一个事务中,对于同一数据,执行完全相同的select语句时可能看到不一样的结果。

    7.1.3、幻读

    当用户读取某一范围的数据行时,另一个事务又在该范围内插入或删除了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行;

    7.1.4、更新丢失

    数据库第一类更新丢失问题:事务回滚覆盖另外一个事务的更新
    数据库第二类更新丢失问题:事务提交覆盖另外一个事务的更新,可以通过乐观锁或者悲观锁解决

    7.1.5、不可重复读何幻读的区别

    不可重复读

  • 重点在修改

  • 数据变化
  • 锁行

幻读

  • 重点在新增和删除
  • 行数变化
  • 锁表

    7.2、事务的隔离级别

    mysql默认的事务隔离级别为可重复读repeatable-read
    image.png
    数据库事务隔离越严格,并发副作用越小,但付出的代价也越大,

  • 事务隔离级别为读提交时,写数据只会锁住相应的行

  • 隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
  • 事务隔离级别为串行化时,读写数据都会锁住整张表
  • 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

    7.2.1、MVCC

    image.png

    8、B+Tree树,B-Tree树

    B-Tree
    B-Tree是为磁盘等外存储设备设计的一种平衡查找树。
    系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。
    InnoDB 存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB 存储引擎中默认每个页的大小为16KB,可通过参数 innodb_page_size 将页的大小设置为 4K、8K、16K,在 MySQL 中可通过如下命令查看页的大小:show variables like ‘innodb_page_size’;
    而系统一个磁盘块的存储空间往往没有这么大,因此 InnoDB 每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小 16KB。InnoDB 在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘I/O次数,提高查询效率。
    B-Tree 结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述 B-Tree,首先定义一条记录为一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data 为一行记录中除主键外的数据。对于不同的记录,key值互不相同。
    image.png
    每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。
    模拟查找关键字29的过程:
  1. 根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
  2. 比较关键字29在区间(17,35),找到磁盘块1的指针P2。
  3. 根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
  4. 比较关键字29在区间(26,30),找到磁盘块3的指针P2。
  5. 根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
  6. 在磁盘块8中的关键字列表中找到关键字29。
    分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。
    B+Tree 是在 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,InnoDB 存储引擎就是用 B+Tree 实现其索引结构。

从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:
image.png
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为103)。也就是说一个深度为3的B+Tree索引可以维护 10亿 条记录。

实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2-4层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。

InnoDB 是聚簇索引,MyISAM 是非聚簇索,MyISAM 和 InnoDB 存储引擎都使用 B+Tree的数据结构,它相对与 B-Tree结构:
(1)非叶子节点只存储键值信息
(2)所有叶子节点之间都有一个链指针,以加快相邻数据的检索效率。
(3)数据记录都存放在叶子节点中

9、Mysql查询流程

客户端请求 —-> 连接器(验证用户身份,给予权限) —-> 查询缓存(存在缓存则直接返回,不存在则执行后续操作) —-> 分析器(对SQL进行词法分析和语法分析操作) —-> 优化器(主要对执行的sql优化选择最优的执行方案方法) —-> 执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口) —-> 去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果)
image.png

10、JDBC操作数据库流程

第一步:Class.forName()加载数据库连接驱动;
第二步:DriverManager.getConnection()获取数据连接对象;
第三步:根据SQL 获取 sql 会话对象,有 2 种方式Statement、PreparedStatement ;
第四步:执行SQL 处理结果集,执行 SQL 前如果有参数值就设置参数值 setXXX();
第五步:关闭结果集、关闭会话、关闭连接。

11、mysql锁

image.png

锁的分类:
从对数据操作的类型分类:

  1. 读锁(共享锁):针对同一份数据,多个读操作可以同时进行,不会互相影响
  2. 写锁(排他锁):当前写操作没有完成前,它会阻断其他写锁和读锁
  3. 意向锁:
    1. 如果事务A获取了某一行的排它锁,实际此表存在两种锁,表中某一行的排他锁和表上的意向排他锁。
    2. 如果事务B试图在该表级别上加锁时,则受到上一个意向锁的阻塞,它在锁定该表前不必检查各个页或行锁,而只需检查表上的意向锁。

image.png
从对数据操作的粒度分类:

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低(MyISAM 和 MEMORY 存储引擎采用的是表级锁);
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高(InnoDB 存储引擎既支持行级锁也支持表级锁,但默认情况下是采用行级锁);
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

image.png

12、数据库设计规范

12.1、基础规范

1、表存储引擎必须使用InnoDB,表字符集默认使用utf8,必要时候使用utf8mb4

  • 通用,无乱码风险,汉字3字节,英文1字节
  • utf8mb4是utf8的超集,有存储4字节例如表情符号时,使用它

2、禁止使用存储过程,视图,触发器,Event

  • 对数据库性能影响较大,互联网业务,能让站点层和服务层干的事情,不要交到数据库层
  • 调试,排错,迁移都比较困难,扩展性较差

3、禁止在数据库中存储大文件,例如照片,可以将大文件存储在对象存储系统,数据库中存储路径
4、禁止在线上环境做数据库压力测试
5、测试,开发,线上数据库环境必须隔离

12.2、命名规范

1、库名,表名,列名必须用小写,采用下划线分隔

  • abc,Abc,ABC都是给自己埋坑

2、库名,表名,列名必须见名知义,长度不要超过32字符

  • tmp,wushan谁知道这些库是干嘛的

3、库备份必须以bak为前缀,以日期为后缀
4、从库必须以-s为后缀
5、备库必须以-ss为后缀

12.3、表设计规范

1、单实例表个数必须控制在2000个以内
2、单表分表个数必须控制在1024个以内
3、表必须有主键,推荐使用UNSIGNED整数为主键

  • 潜在坑:删除无主键的表,如果是row模式的主从架构,从库会挂住

4、禁止使用外键,如果要保证完整性,应由应用程式实现

  • 外键使得表之间相互耦合,影响update/delete等SQL性能,有可能造成死锁,高并发情况下容易成为数据库瓶颈

5、建议将大字段,访问频度低的字段拆分到单独的表中存储,分离冷热数据

12.4、列设计规范

1、根据业务区分使用tinyint/int/bigint,分别会占用1/4/8字节
2、根据业务区分使用char/varchar

  • 字段长度固定,或者长度近似的业务场景,适合使用char,能够减少碎片,查询性能高
  • 字段长度相差较大,或者更新较少的业务场景,适合使用varchar,能够减少空间

3、根据业务区分使用datetime/timestamp

  • 前者占用5个字节,后者占用4个字节,存储年使用YEAR,存储日期使用DATE,存储时间使用datetime

4、必须把字段定义为NOT NULL并设默认值

  • NULL的列使用索引,索引统计,值都更加复杂,MySQL更难优化
  • NULL需要更多的存储空间
  • NULL只能采用IS NULL或者IS NOT NULL,而在=/!=/in/not in时有大坑

5、使用INT UNSIGNED存储IPv4,不要用char(15)
6、使用varchar(20)存储手机号,不要使用整数

  • 牵扯到国家代号,可能出现+/-/()等字符,例如+86
  • 手机号不会用来做数学运算
  • varchar可以模糊查询,例如like ‘138%’

7、使用TINYINT来代替ENUM

  • ENUM增加新值要进行DDL操作

    12.5、索引规范

    1、唯一索引使用uniq[字段名]来命名
    2、非唯一索引使用idx
    [字段名]来命名
    3、单张表索引数量建议控制在5个以内

  • 互联网高并发业务,太多索引会影响写性能

  • 生成执行计划时,如果索引太多,会降低性能,并可能导致MySQL选择不到最优索引
  • 异常复杂的查询需求,可以选择ES等更为适合的方式存储

4、组合索引字段数不建议超过5个

  • 如果5个字段还不能极大缩小row范围,八成是设计有问题

5、不建议在频繁更新的字段上建立索引
6、非必要不要进行JOIN查询,如果要进行JOIN查询,被JOIN的字段必须类型相同,并建立索引

  • 踩过因为JOIN字段类型不一致,而导致全表扫描的坑么?

7、理解组合索引最左前缀原则,避免重复建设索引,如果建立了(a,b,c),相当于建立了(a), (a,b), (a,b,c)

12.6、SQL规范

1、禁止使用select *,只获取必要字段

  • select *会增加cpu/io/内存/带宽的消耗
  • 指定字段能有效利用索引覆盖
  • 指定字段查询,在表结构变更时,能保证对应用程序无影响

2、insert必须指定字段,禁止使用insert into T values()

  • 指定字段插入,在表结构变更时,能保证对应用程序无影响

3、隐式类型转换会使索引失效,导致全表扫描
4、禁止在where条件列使用函数或者表达式

  • 导致不能命中索引,全表扫描

5、禁止负向查询以及%开头的模糊查询

  • 导致不能命中索引,全表扫描

6、禁止大表JOIN和子查询
7、同一个字段上的OR必须改写为IN,IN的值必须少于50个
8、应用程序必须捕获SQL异常

  • 方便定位线上问题
  • 本规范适用于并发量大,数据量大的典型互联网业务,可直接参考。

    13、Mysql优化

    image.png
    image.png
    image.png
    image.png
    image.png
    image.png

    14、Mysql主从复制

    Slave从Master获取binlog二进制日志文件,然后再将日志文件解析成相应的SQL语句在从服务器上重新执行一遍主服务器的操作,通过这种方式来保证数据的一致性。由于主从复制的过程是异步复制的,因此Slave和Master之间的数据有可能存在延迟的现象,只能保证数据最终的一致性。在master和slave之间实现整个复制过程主要由三个线程来完成:
  1. Slave SQL thread线程:创建用于读取relay log中继日志并执行日志中包含的更新,位于slave端
  2. Slave I/O thread线程:读取 master 服务器Binlog Dump线程发送的内容并保存到slave服务器的relay log中继日志中,位于slave端:
  3. Binlog dump thread线程(也称为IO线程):将bin-log二进制日志中的内容发送到slave服务器,位于master端

注意:如果一台主服务器配两台从服务器那主服务器上就会有两个Binlog dump 线程,而每个从服务器上各自有两个线程;

14.1、主从复制流程

  1. master服务器在执行SQL语句之后,记录在binlog二进制文件中;
  2. slave端的IO线程连接上master端,并请求从指定bin log日志文件的指定pos节点位置(或者从最开始的日志)开始复制之后的日志内容。
  3. master端在接收到来自slave端的IO线程请求后,通知负责复制进程的IO线程,根据slave端IO线程的请求信息,读取指定binlog日志指定pos节点位置之后的日志信息,然后返回给slave端的IO线程。该返回信息中除了binlog日志所包含的信息之外,还包括本次返回的信息在master端的binlog文件名以及在该binlog日志中的pos节点位置。
  4. slave端的IO线程在接收到master端IO返回的信息后,将接收到的binlog日志内容依次写入到slave端的relay log文件的最末端,并将读取到的master端的binlog文件名和pos节点位置记录到master-info文件中(该文件存slave端),以便在下一次同步的候能够告诉master从哪个位置开始进行数据同步;
  5. slave端的SQL线程在检测到relay log文件中新增内容后,就马上解析该relay log文件中的内容,然后还原成在master端真实执行的那些SQL语句,再按顺序依次执行这些SQL语句,从而到达master端和slave端的数据一致性;

    14.2、主从复制好处

  6. 读写分离,通过动态增加从服务器来提高数据库的性能,在主服务器上执行写入和更新,在从服务器上执行读功能。

  7. 提高数据安全,因为数据已复制到从服务器,从服务器可以终止复制进程,所以,可以在从服务器上备份而不破坏主服务器相应数据。
  8. 在主服务器上生成实时数据,而在从服务器上分析这些数据,从而提高主服务器的性能。

    15、分库分表

    读写分离解决的是数据库读写操作的压力,但是没有分散数据库的存储压力,利用分库分表可以解决数据库的储存瓶颈,并提升数据库的查询效率。

    15.1、垂直拆分

  9. 垂直分表:将一个表按照字段分成多个表,每个表存储其中一部分字段。一般会将常用的字段放到一个表中,将不常用的字段放到另一个表中。

    1. 避免IO竞争减少锁表的概率。因为大的字段效率更低,第一,大字段占用的空间更大,单页内存储的行数变少,会使得IO操作增多;第二数据量大,需要的读取时间长。
    2. 可以更好地提升热门数据的查询效率。
  10. 垂直分库:按照业务模块的不同,将表拆分到不同的数据库中,适合业务之间的耦合度非常低、业务逻辑清晰的系统。
    1. 降低业务中的耦合,方便对不同的业务进行分级管理
    2. 可以提升IO、数据库连接数、解决单机硬件存储资源的瓶颈问题

垂直拆分(分库、分表)的缺点:

  • 主键出现冗余,需要管理冗余列
  • 事务的处理变得复杂
  • 仍然存在单表数据量过大的问题

    15.2、水平拆分

  1. 水平分表:在同一个数据库内,把同一个表的数据按照一定规则拆分到多个表中。
    1. 解决了单表数据量过大的问题
    2. 避免IO竞争并减少锁表的概率
  2. 水平分库:把同一个表的数据按照一定规则拆分到不同的数据库中,不同的数据库可以放到不同的服务器上。
    1. 解决了单库大数据量的瓶颈问题
    2. IO冲突减少,锁的竞争减少,某个数据库出现问题不影响其他数据库,提高了系统的稳定性和可用性

水平拆分(分表、分库)的缺点:

  • 分片事务一致性难以解决
  • 跨节点JOIN性能差,逻辑会变得复杂
  • 数据扩展难度大,不易维护

    15.3、分库分表产生的问题

  • 事务

  • 跨节点 Join
  • 跨节点count,order by,group by,分页和聚合函数
  • 全局唯一ID的问题
    • UUID
    • 数据库自增ID
    • Redis生成ID
    • Twitter的snowflake算法
    • 美团的Leaf分布式ID生成系统

      16、分区

      分区就是将表的数据按照特定规则存放在不同的区域,也就是将表的数据文件分割成多个小块,在查询数据的时候,只要知道数据数据存储在哪些区域,然后直接在对应的区域进行查询,不需要对表数据进行全部的查询,提高查询的性能。同时,如果表数据特别大,一个磁盘磁盘放不下时,我们也可以将数据分配到不同的磁盘去,解决存储瓶颈的问题,利用多个磁盘,也能够提高磁盘的IO效率,提高数据库的性能。在使用分区表时,需要注意分区字段必须放在主键或者唯一索引中、每个表最大分区数为1024;常见的分区类型有:Range分区、List分区、Hash分区、Key分区
  1. Range分区:按照连续的区间范围进行分区
  2. List分区:按照给定的集合中的值进行选择分区。
  3. Hash分区:基于用户定义的表达式的返回值进行分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式。
  4. Key分区:类似于按照HASH分区,区别在于Key分区只支持计算一列或多列,且key分区的哈希函数是由 MySQL 服务器提供。

    16.1、分区的有点

  5. 可伸缩性:

    1. 将分区分在不同磁盘,可以解决单磁盘容量瓶颈问题,存储更多的数据,也能解决单磁盘的IO瓶颈问题。
  6. 提升数据库的性能:
    1. 减少数据库检索时需要遍历的数据量,在查询时只需要在数据对应的分区进行查询。
    2. 避免Innodb的单个索引的互斥访问限制
    3. 对于聚合函数,例如sum()和count(),可以在每个分区进行并行处理,最终只需要统计所有分区得到的结果
  7. 方便对数据进行运维管理:
    1. 方便管理,对于失去保存意义的数据,通过删除对应的分区,达到快速删除的作用。比如删除某一时间的历史数据,直接执行truncate,或者直接drop整个分区,这比detele删除效率更高;
    2. 在某些场景下,单个分区表的备份很恢复会更有效率。

      17、sql注入攻击

      由于后台的SQL语句是拼接而来的。其中的参数是由用户提交的,如果用户在提交参数时,在其中掺杂了一些SQL关键字或者特殊符号(比如,or # —),就可能会导致SQL语句的语意发生变化。从而执行一些意外的操作(在不知道密码的情况下也能登陆,甚至在不知道用户名和密码的情况下也能登陆),这就是SQL注入攻击。
      解决办法
  • 代码层防止sql注入攻击的最佳方案就是sql预编译PreparedStatement
  • 确认每种数据的类型,比如是数字,数据库则必须使用int类型来存储
  • 规定数据长度,能在一定程度上防止sql注入
  • 严格限制数据库权限,能最大程度减少sql注入的危害
  • 避免直接响应一些sql异常信息,sql发生异常后,自定义异常进行响应
  • 过滤参数中含有的一些数据库关键词