DBD::DB2:为什么`$dbh->disconnect`会导致自增列中出现间隙?

huangapple go评论72阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年1月8日 21:51:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75048269.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定