-->

后端面试38讲_07_06丨数据库原理为什么PrepareStatement性能更好更安全

你好,我是李智慧。

做应用开发的同学呢,常常觉得数据库有DBI运维,自己会写circle就可以了。

数据库库原理不需要学习。

其实啊就算是写circle,也需要了解数据库原理。

比如我们都知道啊,circle的查询条件尽量包含索引字段,那这是为什么呢?这样做有什么好处呢?你也许会说使用索引进行查询速度快,但为什么速度快呢?还有啊我们在java程序中访问数据库的时候,有两种提交circle的方式。

一种呢是通过statement直接提交circle.另一种呢先通过prepare statement编译circle,然后设置可变参数,再提交执行。

文稿中有这两种方式实现的代码,看代码似乎statement更简单了。

是编程。

实践中呢主要是用第二种,也就是prepare statement使用my betis的OM框架的时候,这些框架内部也是用第二种方式提交cirl.那么为什么要舍简单而求复杂呢?要想回答上面这些问题啊,就需要了解数据库的原理,包括数据库的架构原理和数据库文件的存储原理。

那我们先看看数据库架构原理与收口执行过程吧。

关系数据库系统RDBMS有很多种。

但是这些关系数据库的架构呢,基本上差不多,包括支持类似库语法的哈,多个大数据仓库、hive等等,也基本上都是相似的架构。

一个SELLL提交到数据库,需要经过连接器,然后将circl语句提交给语法分析器,生成一个抽象语法数ASTAST经过语义分析与优化器进行语义优化,使计算过程和需要获取的中间数据尽可能的少。

然后得到数据库的执行计划执行计划提交给具体的执行引擎进行计算,将计算结果通过连接器再返回给应用程序。

应用程序提交SELQL到数据库。

执行呢首先需要建立与数据库的连接,数据库连接器,或用每个连接请求分配一块专用的内存空间,用于绘画上下文管理。

首此建立连接呢对数据库而言相对比较重,需要花费一定的时间。

因此呢应用程序启动的时候,通常会初始化建立一些数据库连接,放在连接池里。

这样啊当处理外部请求执行circle操作的时候,就不需要花费时间建立连接呢。

但是这些连接一旦建立呢,不管是否有cirl执行,都会消耗一定的数据库内内存源源以对。

对于一个规规互联网应用,用群群说如果启启动很很多用用程序实力,这些程序每个都会和数据库建立若干个连接。

即便呢不提交circle到数据库执行,也会对数据库产生很大的压力。

一方呢应用程序需要对数据库连接进行管理,一方面通过连接词对连接进行管理,空闲的连接会被及时的释放。

另一方面呢微服务架构可以大大的减轻数据库的连接。

比如对于用户数据库来说,所有的应用都需要连接到用户数据库,这个连接数据比较大。

而如果划分一个用户微服务,并且独立的部署一个比较小的集群。

所么呢就只有这几个用户微服务实例需要连接到用户的数据库。

熊精灵连接数量大大的减少了连接器。

收到了cirl le以以后呢,会ccircle交语语分析析器进行处理。

语法分析的工作呢比较简单机械,就是根据circle语法规则生成对应的抽象语法树AST,但果circle语句中存在语法错误,那么在生成语法树的时候啊就会报错。

因为语法错误是构建抽象语法树的时候发现的,所以能够知道错误发生在哪里。

上面的例子中,虽然语法分析器不知道WHHEE一个语语法写写错误,因为呢这个HHE可能是表明的别别名,但是语法析析器构建语法法,到了usa d等于一,这个时候就出错了。

所以返回的报错信息呢可以提示在AD等语一附附近语法错误。

语法分析器生成的抽象语法数呢并不仅仅可以用来做语法校验,它也是下一步处理的基础。

语义分析与优化器呢会对抽象语法术进一步的做语义优化,也就是在保证circle语义不变的前提下进行语义等价转换,使最后的计算量和中间数据尽量的小收口。

语义分析与优化器,就是要让各种复杂嵌套的circle进行语义等价转换,得到有限的几种关系代数的计算结构,并且利用索引等信息进一步的进行优化。

可以说啊各个数据库最黑科技的部分就是优化这里了。

语义分析与优化器最后会输出一个执行计划,由执行引擎完成数据库的查询或者更新。

执行引擎是可以替换的,只要能够执行这个执行计划就可以了。

所以mysql有多种执行引擎可以选择缺省的呢是ino DB,此外还有my asim memory等等。

我们可以在创建表的时候呢,就指定重储引擎。

大数据仓库have也是这样的架构have输出的执行计划,可以在hadoop上执行。

好了,了解了数据库架构与circle执行过程之后啊,让我们再回到开头的问题。

应用程序为什么应该使用prepare statement执行circle呢?这样做主要有两个好处,一个好处呢是prepare statement和预先提交。

对占位符的circle到数据库进行预处理,提前生成执行计划。

当改定占位符参数真正执行circle的时候啊,执行引擎可以直行,执行效率更好一点了,我们在好处就更加重要了。

Prepare statement可以防止circle注入。

因为呢一开始构造prepare statement的时,就已经提交了查询的circle,并且被一据库预先生产的执行计划。

后面的黑客不管提交什么样的字符串,都只能提交给这个执行计划去执行,不可能再生成一个新的circle了,也就不会被攻击了。

我在文稿中给了一个例子,你可以看一下,让我们再回到文章开头提到的另一个问题。

数据库通过索引进行查询,能够加快查询速度。

那么为什么索引能够加快查询速度呢?数据库索引使用b加树,我们先看一下b加树这种数据结构。

B加树是一种n叉排序,数数的每个节点包含n个数据,这些数据按顺序排好。

两个数据之间呢是一个指向子节点的指针。

而子节点的数据呢则在这两个数据大小之间,b加树的节点存储在磁盘上。

如果每个节点存储一千多个数据,那这样数的深度最多呢,只要四层就可以存储数亿的数据。

如果将树的根结点缓存在内存中呢,那么最多只需要三次磁盘访问,就可以检索到需要的索引数据。

B加速只是加快了索引的检索速度。

如何通过索引加快数据库记录的查询速度呢?数据库索引有两种,一种是据处索引,据处索引的数据库记录和索引重储在一起。

文稿中有一张图,这张图中的叶子节点索引一和记录行RE重储在一起,查找到索引呢就查找到了数据库记录。

像mysql数据库的组件,就是据处索引组件ID和所在的记录行重储在一起。

Mysql的数据库文件实际上是以组界作为中间节点行记录,作为叶子节点的一颗b加速。

另一种数据库索引呢是非矩处索引,非据处索引,在叶子节点记录的就不是数据行记录了,而是据处索引,也就是组件通过b加速在叶子节点找到非据处索引。

A和索引。

I在一起存储的呢就是组件一再根据组件,一通过组件索引就可以找到对应的记录。

Re最终通过非居处索引找到组件索引,再通过组件索引找到行记录的过程,也被称作回表。

所以通过索引啊,我们可以快速的查询到需要的记录。

而如果需要查询的字段上没有索引呢,就只能扫描整张表了,查询速度就会慢。

很多数据库除了索引的b加速文件啊,还有一些比较重要的文件,比如事务日志文件,数据库可以支持事务,一个事务对多条记录进行更新,要么全部更新,要么全部不更新,不能部分更新。

否则像转账这样的操作就会出现快速的数据不一致,就可能会造成巨大的经济损失。

数据库实现事务呢主要就是依靠事务日志文件。

在进行事务操作的时候啊,事务日志文件会记录更新前的数据记录,然后再更新数据库中的记录。

如果全部记录都更新成功呢,那么事物正常结束。

如果过程中某条记录更新失败,那么整个事物全部回滚已经更新的记录,根据事物日志中记录的数据进行恢复,这样呢全部数据都恢复到事物提交前的状态,仍然保持数据的一致性。

此外啊,像mysql数据库,还有一种叫做bean log的日志文件记录,全部的数据更新操作记录。

这样只要有了bean log,就可以完整的复现数据库的历史变更,还可以实现数据库的主重复制,构建高性能高可用的数据库系统。

我将会在架构模块呢进一步为你讲述我们做应用开发需要了解RDBMS的架构原理,但是关系数据库系统非常庞大复杂。

对于一般的应用开发者而言,全面掌握关系数据库的各种实现细节代价,实在是太高昂,也没有太大必要。

实际呢我们只需要掌握数据库的架构原理和执行过程,数据文件的存储原理和索引的实现方式,然及数据库事务与数据复制的基本原理就可以了。

然后在开发工作中呢,针对各种数据库问题去思考背后的原理是什么呀?应该如何处理呢?通过这样不断的思考学习,不但能够让使用数据库方面的能力不断提高,也能对数据库软件的设计理念有更深刻的认识自己的软件设计与架构的能力呢也会得到加强。

此以可以提高数据库的查询性能,那么是不是应该尽量多的使用索引呢?如果不是为什么,此外,你还了解哪些改善数据库访问性能的技巧方法,欢迎你在评论区写下你的思考。

也欢迎把这篇文章分享给你的朋友,或者同事我们一起交流。

进不。