错误模拟:
Session A: 定义一个存储过程,并打开一个游标,让游标保持60s
createprocedure "informix".a ()
define i int;
define j char(30);
foreach cur1 for select a,b into i,j from x
system 'sleep 60';
end foreach;
end procedure ;
execute procedure a();
Session B:在A进行的同时,修改表结构
Alter table x add ( c char(10) );
242: Could not open database table(informix.modinfo).
106: ISAM error: non-exclusive access.
Session B 会得到如下报错。这个报错并不是因为上面的游标锁住了该表,我们可以检查syslocks或者在sessionB执行:
lock table x in exclusive mode;
Table locked.
究其原因,应该是由于使用了游标读取,虽然游标并不加锁,但是会保证表的partion信息不被更新。
我们也可以这么模拟:
Session A: 执行一个大表的扫描,如表modinfo,该表有1000000以上的数据量
dbaccess shentest <<! 1>/dev/null 2>&1
select * from modinfo;
!
Session B:在A进行的同时,修改表结构
Alter table modinfo add ( c char(10) );
同样会得到上面的错误。
解决问题方法:
针对该问题,网上说可以增加IFX_DIRTY_WAIT环境变量,我也进行了尝试,确实可以解决问题,但会产生一些新问题,说明如下:
Session A: 对大表进行select
dbacces sshentest <<! 1>/dev/null 2>&1
select* from modinfo;
!
Session B: 设置 exportIFX_DIRTY_WAIT=30,执行:
Alter table modinfo add ( c char(10) );
这时session B会处于等待状态,如果A在30s内完成,B可以正常执行,否则仍报“non-exclusive access”错误。
带来的问题如下:
当session A正在执行,而Session B也在等待时,我们执行sessionC,而无论SessionC是否设置IFX_DIRTY_WAIT,都会出现:
Session C:
dbaccess shentest <<!
Select * from modinfo;
!
[informix@dbmaster~]$ sh y.sh
Databaseselected.
243: Could not position within a table(informix.modinfo).
106: ISAM error: non-exclusive access.
Databaseclosed.
Session C立刻会报这个错误。而如果只有session A和sessionC执行时(两个select),各自不干扰,可同时执行。
究其原因,应该是sessionB虽然在等待sessionA执行完毕,但已经独占了该表的partition信息,致使其他连接无法读取该表。为了避免该问题,建议对表结构的修改采用反复尝试的办法,而不是采取设置该环境变量的方法。尝试方式可以加入死循环,先检查,在执行,在检查,在执行。
常用案例: 修改表分区,detach过期分区:
drop procedure defrag;
create procedure defrag(tabname varchar(20),part varchar(20))
define i int;
define procname char(32);
define sql varchar(255);
DEFINE sql_errI NTEGER;
DEFINE isam_err INTEGER;
DEFINE err_txt CHAR(200);
let procname='defrag';
for i in (1 to 1000 step 1)
on exception SET sql_err, isam_err, err_txt
insert into proclog values(procname,current year to second,sql_err, isam_err, err_txt);
end exception with resume;
if exists (select b.tabname from sysfragments a, systables b
where a.tabid=b.tabid and b.tabname=tabname and a.partition=part )
then
let sql='alter fragment on table '||tabname||' detach '||part||' '||tabname||part;
execute immediate sql;
end if;
if exists (select tabname from systables where tabname=tabname||part)
then
let sql='drop table '||' '||tabname||part;
execute immediate sql;
end if;
end for;
end procedure;
execute procedure defrag('minfo','pt1105');