Java复习笔记(9)——数据库

一:存储引擎

数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。

1. InnoDB(B+树)

InnoDB 底层存储结构为B+树, B树的每个节点对应innodb的一个page,page大小是固定的,一般设为 16k。其中非叶子节点只有键值,叶子节点包含完整数据。
Java复习笔记(9)——数据库_第1张图片

  • 经常更新的表,适合处理多重并发的更新请求。
  • 支持事务。
  • 可以从灾难中恢复(通过 bin-log 日志等)。
  • 外键约束。只有他支持外键。
  • 支持自动增加列属性 auto_increment。

2. TokuDB(Fractal Tree-节点带数据)

TokuDB 底层存储结构为 Fractal Tree,Fractal Tree 的结构与 B+树有些类似, 在 Fractal Tree中,每一个 child 指针除了需要指向一个 child 节点外,还会带有一个 Message Buffer ,这个Message Buffer 是一个 FIFO 的队列,用来缓存更新操作。

例如,一次插入操作只需要落在某节点的 Message Buffer 就可以马上返回了,并不需要搜索到叶子节点。这些缓存的更新会在查询时或后台异步合并应用到对应的节点中。
Java复习笔记(9)——数据库_第2张图片
TokuDB 在线添加索引,不影响读写操作, 非常快的写入性能, Fractal-tree 在事务实现上有优势。 他主要适用于访问频率不高的数据或历史数据归档。

3. MyISAM

MyISAM是 MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。

ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。ISAM 是一种静态索引结构。缺点是它不 支持事务处理。

MyISAM和InnoDB

Java复习笔记(9)——数据库_第3张图片

4. Memory

Memory(也叫 HEAP)堆内存:使用存在内存中的内容来创建表。每个 MEMORY 表只实际对应一个磁盘文件。MEMORY 类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH 索引。但是一旦服务关闭,表中的数据就会丢失掉。

Memory 同时支持散列索引和 B 树索引,B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引对于“相等的比较”快但是对于“范围的比较”慢很多。

二:索引

参考博客:MySQL索引背后的数据结构及算法原理,Mysql建立、删除索引及使用
索引(Index)是帮助 MySQL 高效获取数据的数据结构。常见的查询算法,顺序查找,二分查找,二叉排序树查找,哈希散列法,分块查找,平衡多路搜索树 B 树(B-tree)

特点

  • 可以加快数据库的检索速度
  • 降低数据库插入、修改、删除等维护的难度
  • 只能创建在表,无法创建在视图上
  • 可以直接或间接创建索引
  • 可以在优化隐藏器中使用索引
  • 使用查询处理器执行sql语句,在一个表上,一次只能用一个索引

优点

  • 创建唯一索引,可保证数据记录的唯一性
  • 加快检索数据的速度
  • 加快表与表之间的连接,特别是在实现数据的参考完整性方面
  • 加快查询时的分组和排序的时间
  • 在查询中使用优化隐藏器,提高系统的性能

缺点

  • 创建和维护索引需要耗费时间,随着数据量的增加而增加
  • 索引需要占用物理空间,聚簇索引的空间更大
  • 对表中的数据进行修改时,索引也需要维护,增加了数据维护的时间

注意事项

Java复习笔记(9)——数据库_第4张图片
Java复习笔记(9)——数据库_第5张图片

适用场景

  • 选择唯一性索引:值是唯一的,可以更快速的通过该索引来确定某条记录。
  • 为经常需要排序、分组和联合操作的(出现在order by、group by、distinct后面)字段建立索引
  • 为常作为查询条件的字段建立索引。
  • 为常作为表连接的字段建立索引
  • 对于数据很少被更新的表,如果用户经常查询其中几个字段,可以加上索引,使用索引覆盖,将对表的扫描转换为对索引的扫描

原则

  • 限制索引的数目:越多的索引,会使更新表变得很浪费时间。

  • 尽量使用数据量少的索引:如果索引的值很长,那么查询的速度会受到影响。

  • 尽量使用前缀来索引:如果索引字段的值很长,最好使用值的前缀来索引。

  • 删除不再使用或者很少使用的索引

  • 最左前缀匹配原则,非常重要的原则。

  • 尽量选择区分度高的列作为索引:区分度的公式是表示字段不重复的比例

  • 索引列不能参与计算,保持列“干净”:带函数的查询不参与索引。

  • 尽量的扩展索引,不要新建索引。

分类

  • 组合索引:实质上是将多个字段(最多 16 个字段)建到一个索引里,列值的组合必须唯一
  • 单个索引
    • 聚集索引:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中,只能拥有一个聚集索引。
      Java复习笔记(9)——数据库_第6张图片
    • 非聚集索引:
      • INDEX(普通索引):允许出现相同的索引内容
      • UNIQUE(唯一索引):不可以出现相同的值,可以有 NULL 值
      • PROMARY KEY(主键索引):不允许出现相同的值,不可以有 NULL 值
      • fultext index(全文索引):可以针对值中的某个单词,但效率确实不敢恭维
  • 空间索引:依据空间对象的位置和形状或空间对象之间的某种空间关系按一定的顺序排列的一种数据结构 ,其中包含空间对象的概要信息,如对象的标识、外接矩形及指向空间对象实体的指针。它将空间对象按范围划分,每个结点都对应一个区域和一个磁盘页,非叶结点的磁盘页中数据库从左到右原则

创建

参考我之前的博客:学习数据库(4)——SQL语言

实现:B+树

参考博客:从B树、B+树、B*树谈到R 树

B树

为了磁盘或其它存储设备而设计的一种多叉平衡查找树。指针、关键字、关键字代表的文件地址构成B树的一个节点,这些节点存储在一个磁盘块

  • 非叶子结点有 M/2 ~M个儿树,M>2
  • 根节点有0、1、2个子树
  • 每个节点至少存放M/2-1,最多存放M-1个关键字(至少2个)
  • 非叶子节点的关键字个数=指向儿子的指针个数-1
  • 非叶子结点的关键字按顺序排列
  • 非叶子结点之间按顺序排列,P[1]指向关键字<K[1]的子树,P[M]指向关键字>K[M]的子树
    Java复习笔记(9)——数据库_第7张图片

B+树

是 B 树的一种变形,它更适合实际应用中操作系统的文件索引和数据库索引

  • 非叶子结点有 M/2 ~M个儿树,每个子树对应一个关键字
  • 根节点有0个或2个子树
  • 所有的叶子节点包含了全部的关键字以及其指向文件的指针
    • 叶子结点本身依关键字的大小自小而大的顺序链接
    • 相邻的叶子节点顺序链接
  • 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字
    Java复习笔记(9)——数据库_第8张图片
    B+树与 B-树的区别
  • 内部节点中,关键字的个数与其子树的个数相同,不像 B 树的子树个数总比关键字个数多 1 个
  • 所有指向文件的关键字及其指针都在叶子节点中,不像 B 树,有的指向文件的关键字是在内部节点中。换句话说,B+树中,内部节点仅起到索引的作用
  • B+在搜索过程中,如果查询和内部节点的关键字一致,那么搜索过程不停止,而是继续向下搜索这个分支,B+为了找到这个关键字的指针。

MyISAM索引实现

参考博客:【Java】Java常见面试题(三)数据库常见面试题
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址
Java复习笔记(9)——数据库_第9张图片

InnoDB索引实现

InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引
Java复习笔记(9)——数据库_第10张图片
因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有)

如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

三:数据库范式

范式是具有最小冗余的表结构。
Java复习笔记(9)——数据库_第11张图片

第一范式(列都是不可再分)

第一范式的目标是确保每列的原子性:如果每列都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式(1NF)
Java复习笔记(9)——数据库_第12张图片

第二范式(每个表只描述一件事情)

首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。 第二范式要求每个表只描述一件事情。
Java复习笔记(9)——数据库_第13张图片

第三范式(不存在对非主键列的传递依赖)

第三范式定义是,满足第二范式,并且表中的列不存在对非主键列的传递依赖。
Java复习笔记(9)——数据库_第14张图片
除了主键订单编号外,顾客姓名依赖于非主键顾客编号。

四:事务、存储过程、触发器、并发控制

参考我之前的博客:学习数据库(6)——数据库的控制

Mysql的并发和连接数

  • 设置datasource连接池的moveAbandoned=true
  • 在getNumActive()的数量快到达getMaxActive()时,系统回收无效的Connection(以removeAbandoned(时间)为标准)
  • 设置dataSource.maxWait(时间),则Connection超过这段时间还没有被使用,就释放连接

五:基于 Redis 分布式锁

  1. 获取锁的时候调用 setnx,如果返回 0,则该锁正在被别人使用,返回 1 则成功获取锁。可以设置一个获取的超时时间,若超过这个时间则放弃获取锁。
  2. 释放锁的时候,通过 UUID(锁的 value值为一个随机生成的) 判断是不是该锁,若是该锁,则执行 delete 进行锁释放。可以使用 expire 命令为锁添加一个超时时间,超过该时间则自动释放锁。

六:分库分表

参考博客:分表与分库使用场景以及设计方式

分表

对于访问极为频繁且数据量巨大的单表来说,我们首先要做的就是减少单表的记录条数,以便减少数据查询所需要的时间,提高数据库的吞吐

用户 id 直接 mod 分成表的数目大小,将大表拆成小表
Java复习笔记(9)——数据库_第15张图片

分库

对数据库进行拆分,从而提高数据库写入能力
Java复习笔记(9)——数据库_第16张图片

分库分表

  1. 中间变量 = user_id % (分库数量 * 每个库的表数量)

  2. 库 = 取整数 (中间变量 / 每个库的表数量)

  3. 表 = 中间变量 % 每个库的表数量

还有一种就是分库分表的垂直切分和水平划分

垂直切分(按照功能模块)

将表按照功能模块、关系密切程度划分出来,部署到不同的库上。例如,我们会建立定义数据库 workDB、商品数据库 payDB、用户数据库 userDB、日志数据库 logDB 等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。
Java复习笔记(9)——数据库_第17张图片

缺点
  • 单机的ACID被打破,数据到了多机之后,原来在单机通过事务进行处理的逻辑会受到很大影响。
  • join操作困难,数据可能已经在两个表中了
  • 无法靠外键约束

水平切分(按照规则划分存储)

当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如 userID 散列,进行划分,然后存储到多个结构相同的表,和不同的库上。

Java复习笔记(9)——数据库_第18张图片

缺点
  • 单机的ACID被打破
  • join操作困难
  • 无法靠外键约束
  • 依靠单库的自增序列生成的唯一ID受到影响
  • 对于逻辑上单个表的查询需要跨库

改进

  • ACID:只能放弃原来的单机事务或者引入分布式事务
  • join
    • 在应用层把原来的数据库中的join操作分为多次数据库操作
    • 数据冗余,在每个表中多添加一些被分走的数据的信息,可能会重复,但是可以在一个库中进行 join
    • 借助外部系统
  • 外键:如果需要对分库后的单库做外键约束,就需要每个单库的数据是内聚的,否则只能依靠应用层判断容错方式
  • 递增ID:需要从连续性和唯一性两方面考虑问题,如果只从唯一性考虑,可以参考UUID的生成方式或者根据自身业务确定ID,如果考虑连续性,则需要一个独立的系统完成工作(把所有ID集中在一个地方,对每个ID序列独立管理,每台机器使用ID时都从ID生成器获取)

七:分布式事务

分布式事务是指会涉及到操作多个数据库的事务,在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。

XA 就是 X/Open DTP 定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。 XA 接口函数由数据库厂商提供。

1. 二阶段提交

在计算机网络以及数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm)。

通常,二阶段提交也被称为是一种协议(Protocol))。在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时,为了保持事务的 ACID 特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果,并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。

因此,二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

(1)准备阶段

事务协调者(事务管理器)给每个参与者(资源管理器)发送 Prepare 消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事务,写本地的 redo 和 undo 日志,但不提交,到达一种“万事俱备,只欠东风”的状态。

(2)提交阶段

如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)

缺点

  • 同步阻塞问题:执行过程中,所有参与节点都是事务阻塞型的。
  • 单点故障:由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。
  • 数据不一致:在二阶段提交的阶段二中,当协调者向参与者发送 commit 请求之后,发生了局部网络异常或者在发送 commit 请求过程中协调者发生了故障,导致只有一部分参与者接受到了commit 请求。于是整个分布式系统便出现了数据不一致的现象(脑裂现象)
  • 数据状态不确定:协调者再发出 commit 消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

2. 三阶段提交( Three-phase commit )

也叫三阶段提交协议( Three-phase commit protocol),是二阶段提交(2PC)的改进版本。与两阶段提交不同的是,三阶段提交有两个改动点。

  • 引入超时机制。同时在协调者和参与者中都引入超时机制。
  • 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。也就是说,除了引入超时机制之外,3PC 把 2PC 的准备阶段再次一分为二,这样三阶段提交就有 CanCommit、PreCommit、DoCommit 三个阶段。

(1)CanCommit 阶段

协调者向参与者发送 commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。

(2)PreCommit 阶段

协调者根据参与者的反应情况来决定是否可以继续进行

  • 假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会执行事务的预执行
  • 假如有任何一个参与者向协调者发送了 No 响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。

(3)doCommit 阶段

该阶段进行真正的事务提交

  1. 协调者发送提交请求
  2. 参与者提交事务
  3. 参与者响应反馈( 事务提交完之后,向协调者发送 Ack 响应。)
  4. 协调者确定完成事务。

八:柔性事务

在电商领域等互联网场景下,传统的事务在数据库性能和处理能力上都暴露出了瓶颈。

在分布式领域基于 CAP 理论以及 BASE 理论,有人就提出了 柔性事务 的概念。

  • CPA理论:指的是在一个分布式系统中, Consistency(一致性)、 Availability
    (可用性)、Partition tolerance(分区容错性),三者不可得兼。
    • 一致性:在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
    • 可用性:在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
    • 分区容忍性:以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。
  • BASE 理论:它是在 CAP 理论的基础之上的延伸。包括 基本可用(Basically Available)、柔性状态(Soft State)、最终一致性(Eventual Consistency)。

两阶段型

就是分布式事务两阶段提交,对应技术上的 XA、JTA/JTS。这是分布式环境下事务的典型模式。

补偿型

TCC 型事务(Try/Confirm/Cancel)可以归为补偿型。WS-BusinessActivity 提供了一种基于补偿的 long-running 的事务处理模型。

服务器 A 发起事务,服务器 B 参与事务,服务器 A 的事务如果执行顺利,那么事务 A 就先行提交,如果事务 B 也执行顺利,则事务 B 也提交,整个事务就算完成。但是如果事务 B 执行失败,事务 B 本身回滚,这时事务 A 已经被提交,所以需要执行一个补偿操作,将已经提交的事务 A 执行的操作作反操作,恢复到未执行前事务 A 的状态。

这样的 SAGA 事务模型,是牺牲了一定的隔离性和一致性的,但是提高了 long-running事务的可用性。
Java复习笔记(9)——数据库_第19张图片

异步确保型

通过将一系列同步的事务操作变为基于消息执行的异步操作, 避免了分布式事务中的同步
阻塞操作的影响。
Java复习笔记(9)——数据库_第20张图片

最大努力通知型(多次尝试)

这是分布式事务中要求最低的一种, 也可以通过消息中间件实现, 与前面异步确保型操作不同的一点是, 在消息由 MQ Server 投递到消费者之后, 允许在达到最大重试次数之后正常结束事务。

九:SQL优化

参考博客
Java复习笔记(9)——数据库_第21张图片
Java复习笔记(9)——数据库_第22张图片
Java复习笔记(9)——数据库_第23张图片
Java复习笔记(9)——数据库_第24张图片
Java复习笔记(9)——数据库_第25张图片
Java复习笔记(9)——数据库_第26张图片

十:数据库连接池

连接池的工作原理主要由三部分组成

连接池的建立

一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java 中提供了很多容器类可以方便的构建连接池,例如 Vector、Stack 等。

连接池的管理

连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:

  • 当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;

  • 如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数

    • 如果没达到就重新创建一个连接给请求的客户
    • 如果达到就按设定的最大等待时间进行等待
    • 如果超出最大等待时间,则抛出异常给客户。
  • 当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值

    • 如果超过就从连接池中删除该连接
    • 否则保留为其他客户服务。

该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销

连接池的关闭

当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反
Java复习笔记(9)——数据库_第27张图片

十一:explain

参考博客:EXPLAIN 命令详解

需要知道SQL 的执行计划,比如是全表扫描,还是索引扫描,这些都需要通过EXPLAIN 去完成。EXPLAIN 命令是查看优化器如何决定执行查询的主要方法。

explain 命令详解

在这里插入图片描述

  • id:包含一组数字,表示查询中执行 select 子句或操作表的顺序
  • select_type:表示查询中每个 select 子句的类型(简单 OR 复杂)
    • SIMPLE:查询中不包含子查询或者 UNION
    • 查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
    • 在 SELCT 或 WHER 列表中包含了子查询,该子查询被标记为:SUBQUERY
    • 在 FROM 列表中包含的子查询被标记为:DERIVED(衍生)用来表示包含在 from子句中的子查询的 select,mysql 会递归执行并将结果放到一个临时表中。服务器内部称为"派生表",因为该临时表是从子查询中派生出来的
    • 若第二个 SELCT 出现在 UNION 之后,则被标记为 UNION;若 UNION 包含在
      FROM 子句的子查询中,外层 SELCT 将被标记为:DERIVED
    • 从 UNION 表获取结果的 SELCT 被标记为:UNION RESULT
    • SUBQUERY 和 UNION 还可以被标记为 DEPNDENT 和 UNCAHEABLE。
      • DEPNDENT 意味着 select 依赖于外层查询中发现的数据。
      • UNCACHEABLE 意味着 select 中的某些特性阻止结果被缓存于一个 item_cache 中。
  • type:表示 MySQL 在表中找到所需行的方式,又称“访问类型”(全局遍历啦,还是索引查询等)。ALL、index、range, ref、eq_ref、const、system、NULL
  • .posible_keys:指出 MySQL 能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(表中的某一列上有多个索引都和这个有关系,索引建立在这个字段上了,那么都列出来)
  • key:显示 MySQL 在查询中实际使用的索引,若没有使用索引,显示为 NULL
  • key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
  • ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
  • rows 表示 MySQL 根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
  • Extra 包含不适合在其他列中显示但十分重要的额外信息
  • table Select 查询的表的名字

十二:其他

1. limit 语句如何优化

子查询优化法

原始 sql 语句:

select * 
from Member 
limit 100000,100;

可以先找出第一条数据,然后大于等于这条数据的 id 就是要获取的数据

select * 
from Member 
where MemberID >= 
	(select MemberID from Member limit 100000,1) 
limit 100;
缺点

数据必须是连续的,可以说不能有 wher 条件,wher 条件会筛选数据,导致数据失去连续性

使用 id 限定优化

原始 sql 语句:

select * 
from orders_history 
wher type=2 
limit 100000,100;

这种方式假设数据表的 id 是连续递增的,则我们根据查询的页数和查询的记录数可以算出查询的 id 的范围,可以使用 id 和 betwen and 来查询:

select * 
from orders_history 
where type=2 and id betwen 100000 and 100100 
limit 100;

反向查找优化法

总记录数:100,000
每页记录数: 100
总页数:100,000 / 100= 1000
中间页数:1000 / 2 = 500

原始 sql 语句:查找第601页(600*100=60000)

select * 
from abc
wher type=2 
limit 60000,100;

当偏移超过一半记录数的时候,先用排序,这样偏移就反转了。
正向查找: (当前页 – 1) * 页长度
反向查找: 总记录 – 当前页 * 页长度

select * 
from abc
wher type=2 
order by name desc
limit 39900,100;
缺点:

order by 优化比较麻烦,要增加索引,索引影响数据的修改效率,并且要知道总记录数,偏移大于数据的一半

2. char varchar text 区别

char:

char 最大长度是 25 字符,注意是字符数和字符集没关系。可以有默认值,尾部有空
格会被截断。

varchar:

varchar 的最大长度 65535 是指能存储的字节数,其实最多只能存储 65532 个字节,还有 3 个字节用于存储长度。注意是字节数这个和字符集有关系。一个汉字符用 utf8 占用 3字节,用 gbk 占用 2 字节。可以有默认值,尾部有空格不会截断。

text:

text 和 varchar 基本相同。text 会忽略指定的大小这和 varchar 有所不同,text 不能有
默认值。尾部有空格不会被截断。text 使用额外的 2 个字节来存储数据的大小,varchar 根据存储数据的大小选择用几个字节来存储。text 的 65535 字节全部用来存储数据,varchar则会占用 1-3 个字节去存储数据大小。

3. drop delete truncate 区别

  • delete 和 truncate 只删除表的数据不删除表的结构
  • 速度,一般来说: drop> truncate >delete
  • delete 语句是 dml,这个操作会放到 rollback segment 中,事务提交之后才生效,如果有相应的 trigger,执行的时候将被触发. truncate,drop 是 ddl, 操作立即生效,原数据不放到 rollback segment 中,不能回滚. 操作不触发 trigger
  • drop 直接删掉表
  • truncate 删除表中数据,再插入时自增长 id 又从 1 开始
  • delete 删除表中数据,可以加 where 字句。

你可能感兴趣的:(JAVA,数据库)