英文:
DBD::DB2: why does a `$dbh->disconnect` cause gaps in an autoincrement column?
问题
$dbh->disconnect
导致自增列中出现间隙的原因是什么?
use DBI;
my $db = 'MYDB2';
my $table = 'SCHEMA.TABLE';
my $user = 'user';
my $pass = 'passwd';
my $dsn = "dbi:DB2:$db";
my $dbh = DBI->connect( $dsn, $user, $pass );
$dbh->do( "DROP TABLE IF EXISTS $table" );
$dbh->do( "CREATE TABLE $table (ID INT NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY, NAME CHAR(3))" );
my $sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)");
$sth->execute( 'aaa' );
$sth->execute( 'bbb' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
$sth->finish;
$dbh->disconnect;
$dbh = DBI->connect( $dsn, $user, $pass );
$sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)");
$sth->execute( 'ccc' );
$sth->execute( 'ddd' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
'1', 'aaa'
'2', 'bbb'
2 rows
'1', 'aaa'
'2', 'bbb'
'21', 'ccc'
'22', 'ddd'
4 rows
没有使用 disconnect
时,自增列不会出现间隙:
use DBI;
my $db = 'MYDB2';
my $table = 'SCHEMA.TABLE';
my $user = 'user';
my $pass = 'passwd';
my $dsn = "dbi:DB2:$db";
my $dbh = DBI->connect( $dsn, $user, $pass );
$dbh->do( "DROP TABLE IF EXISTS $table" );
$dbh->do( "CREATE TABLE $table (ID INT NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY, NAME CHAR(3))" );
my $sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)");
$sth->execute( 'aaa' );
$sth->execute( 'bbb' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
$sth->finish;
#$dbh->disconnect;
$dbh = DBI->connect( $dsn, $user, $pass );
$sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)");
$sth->execute( 'ccc' );
$sth->execute( 'ddd' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
'1', 'aaa'
'2', 'bbb'
2 rows
'1', 'aaa'
'2', 'bbb'
'3', 'ccc'
'4', 'ddd'
4 rows
请注意,注释掉 $dbh->disconnect
后,自增列中不再出现间隙。
英文:
Why does the $dbh->disconnect
cause gaps in the auto-increment column?
use DBI;
my $db = 'MYDB2';
my $table = 'SCHEMA.TABLE';
my $user = 'user';
my $pass = 'passwd';
my $dsn = "dbi:DB2:$db";
my $dbh = DBI->connect( $dsn, $user, $pass );
$dbh->do( "DROP TABLE IF EXISTS $table" );
$dbh->do( "CREATE TABLE $table (ID INT NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY, NAME CHAR(3))" );
my $sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)" );
$sth->execute( 'aaa' );
$sth->execute( 'bbb' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
$sth->finish;
$dbh->disconnect;
$dbh = DBI->connect( $dsn, $user, $pass );
$sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)" );
$sth->execute( 'ccc' );
$sth->execute( 'ddd' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
'1', 'aaa'
'2', 'bbb'
2 rows
'1', 'aaa'
'2', 'bbb'
'21', 'ccc'
'22', 'ddd'
4 rows
Without disconnect
the auto-increment column is created without gaps:
use DBI;
my $db = 'MYDB2';
my $table = 'SCHEMA.TABLE';
my $user = 'user';
my $pass = 'passwd';
my $dsn = "dbi:DB2:$db";
my $dbh = DBI->connect( $dsn, $user, $pass );
$dbh->do( "DROP TABLE IF EXISTS $table" );
$dbh->do( "CREATE TABLE $table (ID INT NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY, NAME CHAR(3))" );
my $sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)" );
$sth->execute( 'aaa' );
$sth->execute( 'bbb' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
$sth->finish;
#$dbh->disconnect;
$dbh = DBI->connect( $dsn, $user, $pass );
$sth = $dbh->prepare( "INSERT INTO $table (NAME) VALUES(?)" );
$sth->execute( 'ccc' );
$sth->execute( 'ddd' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
'1', 'aaa'
'2', 'bbb'
2 rows
'1', 'aaa'
'2', 'bbb'
'3', 'ccc'
'4', 'ddd'
4 rows
答案1
得分: 5
这是按设计工作的,生成方式为 GENERATED ALWAYS 的 IDENTITY 列不保证连续,应该预期会有间隙。
这与 Perl 或 DBD:DB2 无关,这只是 Db2 实现 GENERATED ALWAYS 的身份列的方式。Db2 在每个连接中内部维护了一个小的缓存,用于这些列的身份值,当一个事务消耗了一个值后,或在崩溃或异常终止后,或者如果其他应用程序(其他连接)正在向相同的身份列插入值,或者增量/减量值不为 1,或者数据库停用后,都可能出现间隙。
虽然在指定身份列时可以使用 "NO CACHE" 选项,或者指定较低的缓存值,但出于性能/并发原因,这些选项都不理想。
您还可以通过在第一个连接发生之前安排数据库明确激活(db2 activate database $dbname
)来减少数据库停用(但无法完全消除它)。这将确保数据库在最后一个连接断开连接时不会自动停用,而身份列的预分配号码缓存在这种情况下不会丢失。
如果您想强制零间隙,您必须使用不同的技术,通常会对高频插入应用程序和/或可扩展性/并发性挑战产生性能影响。
英文:
This is working as designed, IDENTITY columns GENERATED ALWAYS are not guaranteed to be consecutive and gaps should be expected.
This is nothing to do with Perl or DBD:DB2, it is just how Db2 implements identity columns that are GENERATED ALWAYS. Db2 internally maintains a small cache of values for identity values for such columns per connection, and you can get gaps after ROLLBACK of a transaction that consumed a value, or after a crash or abnormal termination, or if other apps (other connections) are inserting values into the same identity column, or the increment/decrement value is not 1, or database deactivation.
Although you can use the "NO CACHE" option when specifying your identity column, or specify a lower cache value, these options are undesirable for performance / concurrency reasons.
You can also reduce database deactivation (but not eliminate it) by arranging to have the database explicitly activated before the first connection happens ( db2 activate database $dbname
) . This will ensure the database does not automatically deactivate itself when the last connection does the disconnect , and the cache of pre-allocated numbers for the identity column(s) will not be lost in this case.
If you want to enforce zero gaps, you have to use a different technique, which usually has performance implications for high-insert-frequency apps and/or scalability/concurrency challenges.
答案2
得分: 3
你可以使用身份列的CACHE选项来更改这个。基本上,出于性能原因,会缓存并使用多个标识,以避免同步。你可以减少间隔,但会增加性能风险。
英文:
You can change that my using the CACHE option for identity columns. Basically, for performance reasons, a number of identities is cached and used, so that syncing is avoided. You can reduce the gap but risk a reduction in performance.
答案3
得分: 1
原因是,很可能在您的应用程序会话断开连接时,数据库会被停用,因为它是唯一与数据库一起工作的应用程序。
当数据库被停用时,所有未使用的序列缓存值都会丢失。
您可以使用 ACTIVATE DATABASE
命令来避免隐式的数据库激活/停用。但这并不能帮助您避免在回滚和实例重启时出现间隙。
英文:
The reason is, that the database is deactivated highly likely upon your application session disconnection, since it's the only application working with the database.
When a database is deactivated, all unused sequence cache values are lost.
You may avoid implicit database activation / deactivation with the ACTIVATE DATABASE
command. But it doesn't help you to avoid gaps in case of rollbacks and the instance restart.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论