数据库优化-事务篇
说明
背景: 日常开发中, 性能的瓶颈往往在于 IO, 尤其是数据库. 了解如何优化数据库, 提高数据库的性能和请求响应速度是有必要的.因此, 我打算梳理和总结数据库优化的相关内容.
本篇为事务篇
, 讲解数据库事务相关的优化. 梳理事务相关的知识点, 并结合实际场景进行讲解.
文章中的实例都是以 PG 数据库为例的, 其它数据库语法可能稍有不同.
准备数据:
create table public.test (
id serial,
val varchar(20) null,
num int,
version int default 0 not null
);
insert
into
test (val, num)
values('iPhone13', 200);
insert
into
test (val, num)
values('MacBook', 300);
insert
into
test (val, num)
values('华为P20', 100);
为什么要有数据库事务
数据库事务是数据库中极为重要的一部分.
一个经典的例子就是银行转账. 由于关于事务的讲解在网上已有很多较为优秀的文章了, 本文就不再赘述.
可以参考:
注意: 无论那种事务的隔离级别, 更新的时候都会取最新的已提交的数据. 即为当前读
数据竞争
在多个数据库 session 并发执行 sql 时, 会遇到数据竞争 data race.
数据竞争: 多个数据库事务尝试在同一时间更新同一行的数据. 为了保持数据的可靠性, 在更新同一条数据时, 需要获取写锁. 而写锁会阻止其它 session 对数据的修改. 想修改这条数据的其它 session 必须等待.
数据竞争是无法避免的, 只能尽可能地降低其对性能的影响.
下图是一个非常简单的例子.
如何降低数据竞争对性能的影响
常见的数据库(例如: Oracle,MySQL,Postgresql) 采用 锁
+ MVCC
的方案实现尽可能地降低数据竞争.
从而提高数据库并发访问, 维护数据一致性(data consistency)和实现不同的事务的隔离级别.
锁机制
数据库的锁机制, 比较复杂, 属于数据库内部的实现, 我们业务开发人员一般不会去主动申请锁的.
拥有这么多种类的锁, 主要目的是: 提高并发, 减少数据竞争.
以 PG 数据库为例, 锁大致的分类如下:
不同颗粒度的锁
- 表锁
- 行锁
- 页锁 (完全由数据库管理)
不同种形式的锁
- 共享锁 share 有时也被称为读锁
- 排它锁 exclusive 有时也被称为写锁
具体的锁, 见 PG 文档: explicit-locking
一条 sql 语句, 可以获取多个锁, 例如
begin;
-- 行的排它锁, 避免其它事务修改同一行数据
-- 表的共享锁, 避免有alert等操作修改表结构
update
test
set
num = num -