数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。
InnoDB 底层存储结构为B+树, B树的每个节点对应innodb的一个page,page大小是固定的,一般设为 16k。其中非叶子节点只有键值,叶子节点包含完整数据。
TokuDB 底层存储结构为 Fractal Tree,Fractal Tree 的结构与 B+树有些类似, 在 Fractal Tree中,每一个 child 指针除了需要指向一个 child 节点外,还会带有一个 Message Buffer ,这个Message Buffer 是一个 FIFO 的队列,用来缓存更新操作。
例如,一次插入操作只需要落在某节点的 Message Buffer 就可以马上返回了,并不需要搜索到叶子节点。这些缓存的更新会在查询时或后台异步合并应用到对应的节点中。
TokuDB 在线添加索引,不影响读写操作, 非常快的写入性能, Fractal-tree 在事务实现上有优势。 他主要适用于访问频率不高的数据或历史数据归档。
MyISAM是 MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。
ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。ISAM 是一种静态索引结构。缺点是它不 支持事务处理。
Memory(也叫 HEAP)堆内存:使用存在内存中的内容来创建表。每个 MEMORY 表只实际对应一个磁盘文件。MEMORY 类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH 索引。但是一旦服务关闭,表中的数据就会丢失掉。
Memory 同时支持散列索引和 B 树索引,B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引对于“相等的比较”快但是对于“范围的比较”慢很多。
参考博客:MySQL索引背后的数据结构及算法原理,Mysql建立、删除索引及使用
索引(Index)是帮助 MySQL 高效获取数据的数据结构。常见的查询算法,顺序查找,二分查找,二叉排序树查找,哈希散列法,分块查找,平衡多路搜索树 B 树(B-tree)
限制索引的数目:越多的索引,会使更新表变得很浪费时间。
尽量使用数据量少的索引:如果索引的值很长,那么查询的速度会受到影响。
尽量使用前缀来索引:如果索引字段的值很长,最好使用值的前缀来索引。
删除不再使用或者很少使用的索引
最左前缀匹配原则,非常重要的原则。
尽量选择区分度高的列作为索引:区分度的公式是表示字段不重复的比例
索引列不能参与计算,保持列“干净”:带函数的查询不参与索引。
尽量的扩展索引,不要新建索引。
参考我之前的博客:学习数据库(4)——SQL语言
参考博客:从B树、B+树、B*树谈到R 树
为了磁盘或其它存储设备而设计的一种多叉平衡查找树。指针、关键字、关键字代表的文件地址构成B树的一个节点,这些节点存储在一个磁盘块
是 B 树的一种变形,它更适合实际应用中操作系统的文件索引和数据库索引
参考博客:【Java】Java常见面试题(三)数据库常见面试题
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。
InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引
因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有)
如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
第一范式的目标是确保每列的原子性:如果每列都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式(1NF)
首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。 第二范式要求每个表只描述一件事情。
第三范式定义是,满足第二范式,并且表中的列不存在对非主键列的传递依赖。
除了主键订单编号外,顾客姓名依赖于非主键顾客编号。
参考我之前的博客:学习数据库(6)——数据库的控制
Mysql的并发和连接数
参考博客:分表与分库使用场景以及设计方式
对于访问极为频繁且数据量巨大的单表来说,我们首先要做的就是减少单表的记录条数,以便减少数据查询所需要的时间,提高数据库的吞吐
中间变量 = user_id % (分库数量 * 每个库的表数量)
库 = 取整数 (中间变量 / 每个库的表数量)
表 = 中间变量 % 每个库的表数量
还有一种就是分库分表的垂直切分和水平划分
将表按照功能模块、关系密切程度划分出来,部署到不同的库上。例如,我们会建立定义数据库 workDB、商品数据库 payDB、用户数据库 userDB、日志数据库 logDB 等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。
当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如 userID 散列,进行划分,然后存储到多个结构相同的表,和不同的库上。
分布式事务是指会涉及到操作多个数据库的事务,在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。
XA 就是 X/Open DTP 定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。 XA 接口函数由数据库厂商提供。
在计算机网络以及数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm)。
通常,二阶段提交也被称为是一种协议(Protocol))。在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时,为了保持事务的 ACID 特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果,并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。
因此,二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。
事务协调者(事务管理器)给每个参与者(资源管理器)发送 Prepare 消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事务,写本地的 redo 和 undo 日志,但不提交,到达一种“万事俱备,只欠东风”的状态。
如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)
也叫三阶段提交协议( Three-phase commit protocol),是二阶段提交(2PC)的改进版本。与两阶段提交不同的是,三阶段提交有两个改动点。
协调者向参与者发送 commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。
协调者根据参与者的反应情况来决定是否可以继续进行
该阶段进行真正的事务提交
在电商领域等互联网场景下,传统的事务在数据库性能和处理能力上都暴露出了瓶颈。
在分布式领域基于 CAP 理论以及 BASE 理论,有人就提出了 柔性事务 的概念。
就是分布式事务两阶段提交,对应技术上的 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事务的可用性。
通过将一系列同步的事务操作变为基于消息执行的异步操作, 避免了分布式事务中的同步
阻塞操作的影响。
这是分布式事务中要求最低的一种, 也可以通过消息中间件实现, 与前面异步确保型操作不同的一点是, 在消息由 MQ Server 投递到消费者之后, 允许在达到最大重试次数之后正常结束事务。
连接池的工作原理主要由三部分组成
一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java 中提供了很多容器类可以方便的构建连接池,例如 Vector、Stack 等。
连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:
当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;
如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销
当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反
参考博客:EXPLAIN 命令详解
需要知道SQL 的执行计划,比如是全表扫描,还是索引扫描,这些都需要通过EXPLAIN 去完成。EXPLAIN 命令是查看优化器如何决定执行查询的主要方法。
原始 sql 语句:
select *
from Member
limit 100000,100;
可以先找出第一条数据,然后大于等于这条数据的 id 就是要获取的数据
select *
from Member
where MemberID >=
(select MemberID from Member limit 100000,1)
limit 100;
数据必须是连续的,可以说不能有 wher 条件,wher 条件会筛选数据,导致数据失去连续性
原始 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 优化比较麻烦,要增加索引,索引影响数据的修改效率,并且要知道总记录数,偏移大于数据的一半
char 最大长度是 25 字符,注意是字符数和字符集没关系。可以有默认值,尾部有空
格会被截断。
varchar 的最大长度 65535 是指能存储的字节数,其实最多只能存储 65532 个字节,还有 3 个字节用于存储长度。注意是字节数这个和字符集有关系。一个汉字符用 utf8 占用 3字节,用 gbk 占用 2 字节。可以有默认值,尾部有空格不会截断。
text 和 varchar 基本相同。text 会忽略指定的大小这和 varchar 有所不同,text 不能有
默认值。尾部有空格不会被截断。text 使用额外的 2 个字节来存储数据的大小,varchar 根据存储数据的大小选择用几个字节来存储。text 的 65535 字节全部用来存储数据,varchar则会占用 1-3 个字节去存储数据大小。