写点什么

数据库浅析

作者:andy
  • 2022-10-28
    北京
  • 本文字数:4114 字

    阅读完需:约 13 分钟

数据库架构原理


数据库架构


一、连接器

连接器为每个连接请求分配一块内存空间用于会话上下文管理

缺点:

数据库建立连接的过程,花费较多的计算机资源,比较重

解决思路:

应用启动时初始化一定数量的连接放到连接池中,外部请求 SQL 操作不需要重新建立连接,而是直接向连接池获取连接

通过 TCP 建立连接,连接数据库需要耗费资源,花费一定的时间,通常需要几百毫秒,为了提高效率,故而初始化时,建立连接池,应用程序通过与连接池通信,取得数据库连接

二、语法分析器

语法分析器,针对 SQL,校验 SQL 关键字是否正确以及 SQL 语句是否是正确的书写顺序,进而建立语法树,如果 SQL 错误,无法建立语法树,故而会报出错误

书写顺序

select distinct 字段

from 表 1

[连接类型] join 表 2

on 关联条件

where 限定条件

group by 分组

having 第二级分组

order by 排序字段

limit 起始位置条目索引(偏移量),条目数

实例:

select s_grade from staff where s_city not in (select p_city from proj where s_empname=p_pname)



三、语义分析与优化器

针对复杂嵌套的 SQL 进行等价转化,得到几种关系代数计算结构,利用索引等信息进行优化

关系代数有选择、投影、交、积、连接等有限的结构

数据库中最有技术含量的地方就在语义分析与优化器,毕竟 SQL 会写得非常复杂

select f.id from orders f where f.user_id = (select id from users);

select f.id from orders f join users u on f.user_id = u.id;


四、执行引擎

使用 explain 查看执行计划



执行顺序

from 表 1

on 关联条件

[连接类型] join 表 2

where 限定条件

group by 分组

having 分组后的筛选

select distinct 字段

order by 排序字段

limit 起始位置条目索引,条目数

PrepareStatement

预先提交带占位符的 SQL 到数据库进行预处理,提前生成执行计划,当给定占位符参数,执行引擎才真正执行 SQL

PrepareStatement 可以防止 SQL 注入,因为语法结构已经固定,不会因为输入参数的不同导致语法结构改变

PreparedStatement updateUser = con.prepareStatement("UPDATE Users SET

stateus = ? WHERE userID = 233");

updateUser.setInt(1, 2);

updateUser.executeUpdate();

扩展:

防止 SQL 注入的手段,有两种手段,第一种,SQL 预处理的方式,第二种,增加中间件,防止用户请求落到数据库层面

架构师思维

知道该怎么做,还必须知道为什么要这么做,这么做和不这么做的后果是什么


数据库事务


事务特性 ACID

原子性(Atomicity):事务要么全部完成,要么全部取消

隔离性(Isolation):两个事务 T1 和 T2 同时运行,事务 T1 和事务 T2 最终的结果是相同的,不管 T1 和 T2 谁先结束,隔离性依靠锁实现

持久性(Durability):事务提交,数据持久保存到数据库中

一致性(Consistency):只有合法的数据(依照关系约束和函数结束),才能写入数据库



事务日志

(1)事务操作时,事务日志文件记录更新前的数据记录

(2)更新数据库记录

(3)如果全部更新成功,则事务提交

(4)如果某条记录更新失败,进行事务回滚,数据恢复到事务提交之前的状态,保证数据一致性




目的

保证数据的一致性,当多个线程并发访问同一个数据时,只允许一个线程进行访问处理,进而保证数据的完整性和一致性

数据库中事务的隔离级别,通过锁实现

分类

(1)按照锁定对象的粒度划分

行锁

按照行对数据进行锁定

优点:锁定粒度小,发生锁冲突概率低,实现的并发度高

缺点:锁开销比较大,加锁比较慢,容易死锁

页锁

按照页对数据进行锁定

锁定粒度、并发度、开销,处于行锁和表锁之间

表锁

对数据表进行锁定

优点:锁的开销小,加锁比较快

缺点:锁定粒度大,发生锁冲突概率高,实现的并发度低

注意:

锁会占用内存空间,锁空间的大小是有限的,当某个层级的锁数量超过了这个层级的阈值时,就会进行锁升级,锁升级就是用更大粒度的锁替代多个更小粒度的锁



(2)按照数据库管理的角度划分

共享锁

共享锁也叫读锁或 S 锁,共享锁锁定的资源可以被其他用户读取,但不能修改,而自身事务是可以修改数据的

锁定指令

LOCK TABLE product_comment READ;

解锁指令

UNLOCK TABLE;

行数据加上共享锁

SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 LOCK IN SHARE MODE;

排他锁

排它锁也叫独占锁、写锁或 X 锁。排它锁锁定的数据只允许进行锁定操作的事务使用,其他事务无法对已锁定的数据进行查询或修改

数据表添加排他锁

LOCK TABLE product_comment WRITE;

释放排他锁

UNLOCK TABLE;

行数据加上排他锁

SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 FOR UPDATE;

当程序对数据进行更新操作时,即 INSERT、DELETE 或者 UPDATE,数据库自动使用排它锁,防止其他事务对该数据行进行操作

意向锁(Intent Lock)

给更大一级别的空间示意里面是否已经上过锁

意向共享锁、意向排他锁

(3)从程序员的角度对进行划分

即看待数据并发的不同思维方式

乐观锁(Optimistic Locking)

对同一数据的并发操作不会总是发生,不用每次针对数据采用数据库自身的锁机制,而是通过程序实现

实现方式

版本号机制

在表中设计一个版本字段 version,第一次读的时候,会获取 version 字段的取值。然后对数据进行更新或删除操作时,会执行 UPDATE ... SET version=version+1 WHERE version=version

时间戳机制

在更新提交的时候,将当前数据的时间戳和更新之前取得的时间戳进行比较,如果两者一致则更新成功,否则就是版本冲突

乐观锁就是程序员自己控制数据并发操作的权限,基本是通过给数据行增加一个戳(版本号或者时间戳),从而证明当前拿到的数据是否最新

悲观锁(Pessimistic Locking)

认为同一个数据可能会被其他事务并发修改,故需要通过数据库自身的锁机制实现,保证数据操作的排他性



乐观锁与悲观锁的比较

1、乐观锁适用于读操作较多的场景,锁机制通过程序实现,不存在死锁,阻止不了程序以外的数据库操作

2、悲观锁适合写操作较多的场景,写操作具备排他性,可以在数据库层面阻止其他事务对该数据的操作权限


推荐阅读(关系型数据库)


数据库设计(入门级)

(1)《数据库设计的那些事》(慕课网-在线课程),关键点:数据的范式、SQL 语句用法

(2)MySQL 官方文档

(3)《MySQL 必知必会》

理论基础

(1)《数据库系统概念》(翻译不够好)

核心知识:数据库查询语言、模式设计、数据仓库、数据库应用开发、基于对象的数据库和 XML、数据存储和查询、事务管理、数据挖掘与信息检索以及数据库系统体系结构等方面的内容,对性能评测标准、性能调整、标准化以及空间与地理数据、事务处理监控等高级应用主题进行了广泛讨论

(2)《Oracle Database 9i/10g/11g 编程艺术》

(3)《高性能 MySQL》

(4)《MySQL 技术内幕:InnoDB 存储引擎》,核心:MySql 内部原理

(5)《MySQL Internals Manual》(MySql 官方文档)

(6)《数据库的索引设计与优化》(数据库的索引设计和优化)


主从读写分离


问题

数据库单机部署,4 核 8G 运行 MySql5.7,大概支撑 500TPS 和 10000QPS,一旦进行营销活动,导致数据库查询和事务压力增加

主从读写分离

客观事实

大部分系统的访问模型是读多写少,读写请求量的差距可能达到几个数量级

解决方案

如何抵抗更高的查询请求,需要把读写分开,这样还可以针对读进行伸缩扩展

两个关键技术

(1)数据的拷贝,即主从复制;

(2)在主从分离的情况下,如何屏蔽主从分离带来的访问数据库方式的变化,让开发如同在使用单一数据库一样

主从复制

关键在于 binlog 日志文件,binlog 记录 MySql 所有变化并以二进制形式保存在磁盘上二进制日志文件

过程



优点

(1)写入操作依赖主库,读出操作依赖从库,即使写入操作导致行锁或者表锁,也不会影响从库的读数据操作

(2)读流量较大时,通过部署多个从库共同承担读流量,即“一主多从”,用以抵御大量的并发读操作

(3)从库作为一个备份,避免主库故障导致数据丢失

缺陷

(1)部署的复杂性

(2)容量并没有变化

(3)主从复制的延迟

在互联网的项目中,一般会优先考虑性能而不是数据的强一致性,故可以适当容忍延迟



核心解决思想:尽量不去从库读取数据

第一种方案是数据的冗余

消息队列不仅包含信息 ID,还包含所有的处理信息,避免从库读取数据

第二种方案是使用缓存

进行写操作时,将数据信息存储到缓存中,其他业务处理时,从缓存中获取数据

第三种方案是查询主库

读写混合操作较为紧密时,则查询数据从主库获取,这样保证数据的一致性,实际上,管理平台系统由于用户少,数据操作往往读写同时进行,为了保证有效性,可以采用这种查询主库的方式,尤其是读写操作交叉的业务环节里

如何访问数据库

传统访问方式

单台服务只需要一个地址,一主多从模式,则需要一个主库地址和多个从库地址,并且需要区分写入操作和查询操作

新兴访问方式

为了降低实现的复杂度,业界涌现了很多数据库中间件来解决数据库的访问问题,这些中间件可以分为两类

第一类

以淘宝的 TDDL( Taobao Distributed Data Layer)为代表,以代码形式内嵌运行在应用程序内部,可以看成是数据源的代理,它的配置管理多个数据源,每一个数据源对应一个数据库

第二类

单独部署的代理层方案,如早期阿里巴巴开源的 Cobar,基于 Cobar 开发出来的 Mycat,360 开源的 Atlas,美团开源的基于 Atlas 开发的 DBProxy 等,业务代码如同在使用单一数据库一样使用它



聚簇索引与非聚簇索引


聚簇索引

(1)数据库记录与索引存储在一起

(2)MySql 主键就是聚簇索引,主键 ID 和记录行存储在一个 B+树

(3)由于数据在物理上只会保存一份,包含实际数据的聚簇索引只能有一个



非聚簇索引

(1)非聚簇索引的叶子节点记录存储的不是行记录,而是聚簇索引,即主键

(2)通过非聚簇索引找到聚簇索引主键,再由主键找到数据记录,这个过程叫做回表



添加必要索引优化 SQL 查询性能

没有索引,查询数据需要遍历所有记录,时间复杂度为 O(n)

合理使用索引

1、不要盲目添加索引,尤其生产环境

(1)添加索引 Alter 操作会消耗较长的时间

(2)Alter 操作期间,数据库的增删改查操作全部阻塞

(3)插入一条新纪录,索引越多,维护的 B+树越多,性能越差

(4)合理使用索引,主要针对的是二级索引(即非聚簇索引,因为聚簇索引是必须存在的,也就是必须有主键)

2、删除不用的索引,避免过多消耗空间

(1)使用更小的数据类型创建索引

(2)int 4 字节 bigint 8 字节,Timestamp 4 字节 Datetime 8 字节


用户头像

andy

关注

还未添加个人签名 2019-11-21 加入

还未添加个人简介

评论

发布
暂无评论
数据库浅析_andy_InfoQ写作社区