尝试运行我的维护计划时,我收到以下错误:
执行查询“”失败,出现以下错误:“无法重新组织表“”上的索引“”(分区 1),因为禁用了页级锁定。”
我们目前在此索引上启用了行级锁定。我可以启用页面级别锁定,但我不确定影响是什么。
我的问题是:这两种锁定方案有什么区别,它们在现实世界(生产中)的后果是什么?
前言
我们的应用程序运行多个DELETE并行执行查询的线程。查询影响孤立的数据,即不应该存在并发DELETE发生在来自不同线程的相同行上的可能性。但是,根据文档,MySQL 对DELETE语句使用所谓的“next-key”锁,它同时锁定匹配的键和一些间隙。这会导致死锁,我们找到的唯一解决方案是使用READ COMMITTED隔离级别。
问题
DELETE使用JOIN大表执行复杂语句时会出现问题。在特定情况下,我们有一个带有警告的表,只有两行,但查询需要从两个单独的INNER JOINed 表中删除属于某些特定实体的所有警告。查询如下:
DELETE pw
FROM proc_warnings pw
INNER JOIN day_position dp
ON dp.transaction_id = pw.transaction_id
INNER JOIN ivehicle_days vd
ON vd.id = dp.ivehicle_day_id
WHERE vd.ivehicle_id=? AND dp.dirty_data=1
Run Code Online (Sandbox Code Playgroud)
当 day_position 表足够大时(在我的测试用例中有 1448 行),那么即使使用READ COMMITTED隔离模式,任何事务也会阻塞整个 proc_warnings表。
该问题始终在此示例数据上重现 - http://yadi.sk/d/QDuwBtpW1BxB9在 MySQL 5.1(在 5.1.59 上检查)和 MySQL 5.5(在 MySQL 5.5.24 上检查)中。
编辑:链接的示例数据还包含查询表的架构和索引,为方便起见,在此处复制:
CREATE TABLE `proc_warnings` (
`id` int(11) NOT NULL AUTO_INCREMENT, …Run Code Online (Sandbox Code Playgroud) 当一条记录被锁定时,我们能知道哪个记录被锁定了吗?
如何获取记录 rowid 或其他信息?
我可以通过这个 sql 得到一些信息
SELECT c.ROW_WAIT_OBJ#,c.ROW_WAIT_FILE#,c.ROW_WAIT_BLOCK#,c.ROW_WAIT_ROW#
FROM v$locked_object a, dba_objects b, v$session c
WHERE a.object_id = b.object_id
AND a.SESSION_ID = c.sid(+)
Run Code Online (Sandbox Code Playgroud)
我在 web 中找到了一种使用函数获取 rowid 的方法 DBMS_ROWID.ROWID_CREATE()
但它似乎不起作用。
我正在运行以下(虚拟)查询
CREATE TABLE large_temp_table AS
SELECT a.*, b.*, c.*
FROM a
LEFT JOIN b ON a.foo = b.foo
LEFT JOIN c ON a.bar = c.bar
Run Code Online (Sandbox Code Playgroud)
假设查询运行需要 10 分钟。在运行时尝试更新表 a、b 或 c 中的值将等待上述查询首先完成。我想避免这个锁(不关心数据一致性)。我怎样才能做到这一点?
使用:MySQL 5.1.41 和 InnoDB 表
ps 设置事务隔离级别读取未提交;不会改变行为
更新 在执行查询时,显示 ENGINE INNODB STATUS 的输出如下(我特意在这里做了一个非常慢的查询)
=====================================
120323 15:26:29 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 8 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 1470, signal count 1468
Mutex spin waits 0, rounds 7525, …Run Code Online (Sandbox Code Playgroud) 我需要在 document_revisions 表中保留一个唯一的(每行)修订号,其中修订号的范围仅限于文档,因此它不是整个表唯一的,仅适用于相关文档。
我最初想出了类似的东西:
current_rev = SELECT MAX(rev) FROM document_revisions WHERE document_id = 123;
INSERT INTO document_revisions(rev) VALUES(current_rev + 1);
Run Code Online (Sandbox Code Playgroud)
但是有一个竞争条件!
我试图用 解决它pg_advisory_lock,但文档有点稀缺,我不完全理解它,我不想错误地锁定某些东西。
以下是可以接受的,还是我做错了,或者有更好的解决方案?
SELECT pg_advisory_lock(123);
current_rev = SELECT MAX(rev) FROM document_revisions WHERE document_id = 123;
INSERT INTO document_revisions(rev) VALUES(current_rev + 1);
SELECT pg_advisory_unlock(123);
Run Code Online (Sandbox Code Playgroud)
我不应该为给定的操作 (key2) 锁定文档行 (key1) 吗?所以这将是正确的解决方案:
SELECT pg_advisory_lock(id, 1) FROM documents WHERE id = 123;
current_rev = SELECT MAX(rev) FROM document_revisions WHERE document_id = 123;
INSERT INTO document_revisions(rev) VALUES(current_rev + 1);
SELECT pg_advisory_unlock(id, 1) FROM …Run Code Online (Sandbox Code Playgroud) 我正在运行这样的并发 Postgres 查询:
UPDATE foo SET bar = bar + 1 WHERE baz = 1234
Run Code Online (Sandbox Code Playgroud)
每个查询都会影响固定的 K 行数,我找不到强制执行更新行顺序的方法,最终导致死锁。目前我通过手动执行订单来解决这个问题,但这意味着我必须执行比平时更多的查询,同时还将搜索复杂度从 O(log N + K) 提高到 O(K log N)。
有没有办法提高性能而不会最终容易陷入死锁?我怀疑如果Postgres 以扫描它们的相同顺序更新行,用(baz)索引替换(baz, id)索引可能会起作用,这是一种值得追求的方法吗?
我们最近将生产实例从 SQL 2008 R2 迁移到全新的 SQL 2014 服务器。这是我们在使用 Service Broker 时发现的一个有趣场景。考虑一个Broker Enabled = true带有MyService和的数据库MyQueue。此队列上禁用了毒物消息处理。队列中至少有 2 个与消息的活动对话。
在一个进程 (SPID 100) 中执行:
BEGIN TRANSACTION;
DECLARE @conversation_group_id UNIQUEIDENTIFIER;
RECEIVE TOP (1) @conversation_group_id = conversation_handle FROM MyQueue;
Run Code Online (Sandbox Code Playgroud)
请注意,我们将事务保持打开状态。假设它是一个 .NET 程序,它在某些外部资源上等待了很长时间。通过sys.dm_tran_locks我们看到该 SPID 已被授予对队列的 IX 锁。
| type | resource_id | mode | status | spid |
| OBJECT | 277576027 | IX | GRANT | 100 |
Run Code Online (Sandbox Code Playgroud)
在单独的进程 (SPID 101) 中执行五次:
BEGIN TRANSACTION;
DECLARE @conversation_group_id …Run Code Online (Sandbox Code Playgroud) 我不确定是否有一个命名模式,或者是否没有,因为这是一个糟糕的主意。但我需要我的服务在主动/主动负载平衡环境中运行。这只是应用程序服务器。数据库将位于单独的服务器上。我有一个服务,需要为表中的每条记录运行一个过程。此过程可能需要一两分钟,并且每 n 分钟重复一次(可配置,通常为 15 分钟)。
有一个需要此处理的 1000 条记录表,以及针对同一数据集运行的两个服务,我希望每个服务“签出”要处理的记录。我需要确保一次只有一个服务/线程在处理每条记录。
我有同事过去使用过“锁表”。将一条记录写入此表以在逻辑上锁定另一个表中的记录(顺便说一句,该其他表非常静态,并且添加了非常偶然的新记录),然后删除以释放锁定。
我想知道如果新表有一个列指示它何时被锁定,并且它当前被锁定,而不是不断插入删除,是否会更好。
有没有人有这种事情的提示?是否存在长期(ish)期限逻辑锁定的既定模式?关于如何确保一次只有一项服务获取锁的任何提示?(我的同事使用 TABLOCKX 锁定整个表。)
我有一个包含大约 1500 万条记录的表。现在我需要向表中添加一个索引。
添加索引需要一些时间来更新表中的每个条目。
我很困惑添加索引是否会导致停机。
如果是,那么我怎样才能克服停机时间?
我正面临着一些重复发生的死锁,其中一个是 Keylock 并且包含一个带有 XLOCK 提示的 SELECT 查询,该查询成为死锁的受害者。另一个语句是对作为第一个查询视图的一部分的一个表的 INSERT。
看法:
create view dbo.viewE
as
select * from dbo.E
where myValue > 13000
Run Code Online (Sandbox Code Playgroud)
选择查询:
select * from dbo.viewE with (XLOCK) where A > GETUTCDATE()
Run Code Online (Sandbox Code Playgroud)
插入语句:
INSERT INTO [dbo].[E] (myValue,A) VALUES (10,GetDate())
Run Code Online (Sandbox Code Playgroud)
基础表 dbo.E 在大约 20 列中包含大约 300 万行,其中一些是 ntext。
取出查询并使用两个事务手动模拟它,该行为是可重现的。如果从选择中删除 XLOCK,则行为会发生变化。
死锁图:
<deadlock-list>
<deadlock victim="process222222221">
<process-list>
<process id="process222222221" taskpriority="0" logused="0" waitresource="KEY: 5:72057604035644444 (ccdf51accc0c)" waittime="2522" ownerId="27202256401" transactionname="SELECT" lasttranstarted="2015-09-14T16:32:36.160" XDES="0x2f1ec5ca0" lockMode="RangeX-X" schedulerid="15" kpid="12936" status="suspended" spid="359" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2015-09-14T16:32:36.160" lastbatchcompleted="2015-09-14T16:32:36.160" clientapp="x" hostname="x" hostpid="14536" …Run Code Online (Sandbox Code Playgroud) locking ×10
sql-server ×4
postgresql ×3
deadlock ×2
mysql ×2
concurrency ×1
ctas ×1
index ×1
index-tuning ×1
innodb ×1
maintenance ×1
mysql-5.5 ×1
oracle ×1
update ×1