PostgreSQL并发问题
事务的隔离级别
在不考虑隔离性的前提下,事务的并发可能会出现的问题:
- 脏读:读到了其他事务未提交的数据(这种情况必须避免)。
- 不可重复读:同一事务中,多次查询同一数据,结果不一致,因为其他事务修改造成的(一些业务中不可重复读不是问题)。
- 幻读:同一事务中,多次查询同一数据,因为其他事务对数据进行了增删吗,导致出现了一些问题(一些业务中幻读不是问题)。
针对这些并发问题,关系型数据库有一些事务的隔离级别,一般用4种。
- READ UNCOMMITTED:读未提交(啥用没有,并且PostgreSQL没有,提供了只是为了完整性);
- READ COMMITTED:读已提交,可以解决脏读(PostgreSQL默认隔离级别);
- REPEATABLE READ:可重复读,可以解决脏读和不可重复读(MySQL默认隔离级别,PostgreSQL也提供了,但是设置为可重复读,效果还是串行化);
- SERIALIZABLE:串行化,问题都解决了,使用了锁,效率慢。
PostgreSQL在老版本中,只有两个隔离级别,读已提交和串行化,在PostgreSQL中就不存在脏读问题。
MVCC
如果一个数据库,频繁的进行读写操作,为了保证安全,采用锁的机制。但如果采用锁机制,如果一些事务在写数据,另外一个事务就无法读数据,会造成读写之间相互阻塞。 大多数的数据库都会采用 多版本并发控制MVCC 来解决这个问题。
比如你要查询一行数据,但是这行数据正在被修改,事务还没提交,如果此时对这行数据加锁,会导致其他的读操作阻塞,需要等待。如果采用PostgreSQL,他的内部会针对这一行数据保存多个版本,如果数据正在被写入,包就保存之前的数据版本。让读操作去查询之前的版本,不需要阻塞。等写操作的事务提交了,读操作才能查看到最新的数据。 这几个及时可以确保 读写操作没有冲突 ,这个就是MVCC的主要特点。
写写操作,和MVCC没关系,那个就是加锁的方式。
注意:这里的MVCC是基于读已提交的,如果是串行化,那就读不到了。
在操作之前,先了解一下PostgreSQL中,每条数据都会自带两个字段:
- xmin:给当前事务分配的数据版本。如果有其他事务做了写操作,并且提交事务了,就给xmin分配新的版本。
- xmax:当前事务没有存在新版本,xmax就是0。如果有其他事务做了写操作,未提交事务,将写操作的版本放到xmax中。事务提交后,xmax会分配到xmin中,然后xmax归0。
基于上图的操作查看一波效果:
- 事务A:
1 | -- 左,事务A |
- 事务B:
1 | -- 右,事务B |