这很简单,但我对 PG 所做的(v9.0)感到困惑。我们从一个简单的表开始:
CREATE TABLE test (id INT PRIMARY KEY);
Run Code Online (Sandbox Code Playgroud)
和几行:
INSERT INTO TEST VALUES (1);
INSERT INTO TEST VALUES (2);
Run Code Online (Sandbox Code Playgroud)
使用我最喜欢的 JDBC 查询工具 (ExecuteQuery),我将两个会话窗口连接到该表所在的数据库。它们都是事务性的(即 auto-commit=false)。我们称它们为 S1 和 S2。
每个相同的代码位:
1:DELETE FROM test WHERE id=1;
2:INSERT INTO test VALUES (1);
3:COMMIT;
Run Code Online (Sandbox Code Playgroud)
现在,以慢动作运行它,在窗口中一次执行一个。
S1-1 runs (1 row deleted)
S2-1 runs (but is blocked since S1 has a write lock)
S1-2 runs (1 row inserted)
S1-3 runs, releasing the write lock
S2-1 runs, now that it can get the lock. But reports …
Run Code Online (Sandbox Code Playgroud) 我和我的一位同事讨论了使用可序列化隔离级别的含义。他说它锁定了整个表,但我不同意告诉他它可能可以但它尝试应用范围锁并且它不应用真正的序列化,如此处所述:可序列化隔离级别。
我在文档中找不到“锁定整个表”的任何内容:SET TRANSACTION ISOLATION LEVEL。
该文档说明了一系列关于范围锁的内容,因此理论上您可以通过简单地拥有一个范围锁来锁定整个表,该范围锁可以锁定表中可能值的整个范围,但它不会锁定表。
我在这里完全错了吗?它实际上是否锁定了整个表(或多个表)?
我在教科书(Avi Silberschatz、Henry F. Korth 和 S. Sudarshan $6e$ 的《数据库系统概念教科书》)中遇到了以下行。686:
Thomas 的写入规则允许不可冲突序列化但仍然正确的调度。那些允许的非冲突可序列化调度满足视图可序列化调度的定义(参见示例框)。
我从上面几行中了解到,遵循 Thomas 的写入规则的时间戳协议生成的每个时间表都是视图可序列化的。
现在让我们采用以下小时间表:$S:R_1(X)、W_2(X)、W_1(X)$。
这个时间表 $S$ 在时间戳协议下是允许的,该协议遵循 Thomas 的写入规则。
并且序列化顺序是 $R_1(X), W_1(X).$
但我无法证明它是视图可序列化的。
其实我认为它是非视图可序列化的,因为,
考虑串行顺序为 $T_1, T_2$
现在 $X$ 的最终值由 $T_2$ 写入。所以不等价。
下一个替代序列是 $T_2, T_1$
在这里,$R_1(X)$ 将读取由 $T_1$ 写入的 $X$ 的值,而不是在两个事务开始之前存在的原始值。所以这也不是视图等价的。
这里出了什么问题?请帮我解决这个问题。
我想我们的PostgreSQL 9.4的事务级数据库迁移READ COMMITTED
要么REPEATABLE READ
或SERIALIZABLE
。在任何一种情况下,我都会遇到一组新的格式错误:
(for both)
ERROR: could not serialize access due to concurrent update
(just for SERIALIZABLE)
ERROR: could not serialize access due to read/write dependencies among transactions
Run Code Online (Sandbox Code Playgroud)
在阅读了 SSI 上的 wiki 页面和文档后,我彻底了解了可能导致这些错误的错误条件、如何处理它们,甚至是避免它们的最佳实践。
但是,我看不到从 PostgreSQL 可以提供的任何调试输出或任何调试信息中确定导致它们的数据依赖性的方法。有没有办法从数据库中获取这些信息,要么在回滚时执行额外的查询,要么通过某种日志机制?
有了这些信息,我就可以进行应用程序级别的更改(锁定、不同的查询等),从而消除一些数据竞争以避免过多的回滚。
我在 x86_64-unknown-linux-gnu 上的 PostgreSQL 9.4.3 中的表和触发器,由 gcc (Debian 4.9.2-10) 4.9.2,64 位编译:
CREATE TABLE measurements (
measurement_id SERIAL PRIMARY KEY NOT NULL,
measurement_size_in_bytes INTEGER NOT NULL
);
CREATE TABLE file_headers (
header_id SERIAL PRIMARY KEY NOT NULL,
measurement_id INTEGER NOT NULL,
file_header_index_start INTEGER,
file_header_index_end INTEGER
);
CREATE TRIGGER measurement_ids AFTER INSERT
ON measurements FOR EACH ROW
EXECUTE PROCEDURE ins_function('SELECT measurement_id FROM measurements
ORDER BY measurement_id desc limit 1;', 1, 666 );
Run Code Online (Sandbox Code Playgroud)
在那里我假设 SELECT 的数据类型自 SERIAL 以来是 INTEGER 但它显然是错误的,因为我从这个启动触发器的命令中收到错误消息:
INSERT INTO measurements …
Run Code Online (Sandbox Code Playgroud) 我很难弄清楚如何准确实现“如果未找到则插入”功能。考虑以下。
我们有一个名为artist
2 列的表,(name, id)
其中name
是唯一id
键,是串行主键。这是一个人为的例子,但它说明了我的问题:
SESSION A SESSION B
1. SELECT id FROM artist
WHERE name = 'Bob';
2. INSERT INTO artist (name)
VALUES ('Bob')
3. INSERT INTO artist (name)
VALUES ('Bob')
4. code that users 'Bob'
(e.g., a FK to Bob's ID)
5. ??? Bob already exists, but we
can't find it
4. COMMIT
Run Code Online (Sandbox Code Playgroud)
会话 B 开始尝试找到一个artist
叫 Bob 的人,但失败了。但是,会话 A 然后创建了 Bob。会话 B 尝试插入名为 Bob 的艺术家,但由于违反主键而失败。但这是我不明白的一点——如果我将操作 3 更改 …
我正在研究冲突序列化类中的顺序保留的概念,并且遇到了顺序保留冲突可序列化(简称 OCSR)。以下是我查到的OCSR的定义:
\n\n\n\n\n如果h是等价于串行历史hs 的冲突,其中 t,t'\xe2\x88\x88h:如果t完全出现在h中的t'之前,则历史 h 是可序列化的保留冲突顺序的冲突,那么hs中也同样成立。
\n
以下是 OCSR 中的计划示例之一:
\n\n\n\n\nw3(y) c3 w1(x)r2(x) c2 w1(y) c1
\n
但我不明白为什么这个时间表在 OCSR 中。因为根据我的理解,这是冲突图 t3 --- > t1 ---- > t2
\n\n它显示了一个串行时间表,其中 t1 在 t2 之前。但在原始的交错时间表中,t2 完全出现在 t1 之前。那么给定的示例如何说是在 OCSR 中呢?\n谁能帮我更好地理解这一点吗?
\n可序列化隔离模式可用于避免更新插入相等 id 时的竞争条件。因此,create table u(uid int primary key, name text);
如果我们运行两个相似的事务 T1 和 T2:
begin isolation level serializable;
select * from u where uid = 1;
Run Code Online (Sandbox Code Playgroud)
然后继续 T1 和 T2:
insert into u (uid, name) values (1, 'A');
Run Code Online (Sandbox Code Playgroud)
commit;
只有第一个成功,而另一个抛出序列化失败之后。
这是该模式的一个非常巧妙的功能,可以处理复杂交易中的独特密钥违规,而不是诉诸特定的“黑客” insert ... on conflict
。然而,即使 uid 不同,例如uid = 2
和uid = 3
,事务 T1 和 T2仍然无法提交。
怎么可能?据说他们创建了不同的谓词 SIReadlocks 并select
使用索引扫描。窍门在哪里?
我有一个触发器错误,导致在 x86_64-unknown-linux-gnu 上的 PostgreSQL 9.4.3 中出现错误,由 gcc (Debian 4.9.2-10) 4.9.2, 64 位编译。每次出错期间,SERIAL 的主键都会增加。修复错误后,表格测量结果为
measurement_id | measurement_size_in_bytes | time
----------------+---------------------------+-------------------------------
1 | 77777 | 2015-07-14 18:29:56.858703+03
2 | 888 | 2015-07-14 18:29:56.882552+03
3 | 888 | 2015-07-14 18:30:15.505957+03
4 | 888 | 2015-07-14 18:41:01.878106+03
39 | 77777 | 2015-07-15 12:11:21.21391+03
40 | 77777 | 2015-07-15 12:11:59.551973+03
41 | 77777 | 2015-07-15 12:12:05.48982+03
42 | 77777 | 2015-07-15 12:13:02.402053+03
43 | 77777 | 2015-07-15 12:13:02.419412+03
44 | 888 | 2015-07-15 …
Run Code Online (Sandbox Code Playgroud) 如果这是一个愚蠢的问题,请道歉。我们正在尝试将我们的多语句表值函数 (MSTVF) 转换为内联表值函数 (ITVF),以避免强制序列化查询。
我有以下 ITVF 函数(淡化了),但执行计划仍然说它的并行度为 1。所讨论的实际函数具有三个由 UNION ALL 分隔的基本 SELECT 语句。
我错过了什么吗?我怎样才能避免在这里强制序列化?
CREATE FUNCTION dbo.Test (@i int)
RETURNS TABLE
AS
RETURN
SELECT @i as [i];
GO
SELECT * FROM dbo.Test (2);
GO
Run Code Online (Sandbox Code Playgroud)
https://www.brentozar.com/pastetheplan/?id=Hyn8o50D7
该实例具有以下设置:
serialization ×10
postgresql ×6
concurrency ×2
sql-server ×2
datatypes ×1
errors ×1
locking ×1
parallelism ×1
plpgsql ×1
protocol ×1
t-sql ×1
timestamp ×1
transaction ×1
trigger ×1
upsert ×1