pingcap / tidb

TiDB - the open-source, cloud-native, distributed SQL database designed for modern applications.
https://pingcap.com
Apache License 2.0
37.29k stars 5.85k forks source link

P2-[4.0 bug hunting]-[AutoIncrement]-Restarting consumes too many values #17758

Closed sre-bot closed 5 months ago

sre-bot commented 4 years ago

Bug Hunter issue tidb-challenge-program/bug-hunting-issue#66


Bug Report

Please answer these questions before submitting your issue. Thanks!

1. What did you do?

This is a regression:

DROP TABLE IF EXISTS t0;
CREATE TABLE t0 (
 id int not null primary key auto_increment,
 pad varbinary(255) not null
) auto_increment=1;
SHOW CREATE TABLE t0\G
INSERT INTO t0 VALUES (NULL, 'a');
SHOW CREATE TABLE t0\G
-- restart tidb-server
INSERT INTO t0 VALUES (NULL, 'a');
SELECT * FROM t0;

2. What did you expect to see?

TiDB 2.1.2 (the version I had on hand):

mysql> select * from t0;
+-------+-----+
| id    | pad |
+-------+-----+
|     1 | a   |
| 30001 | a   |
+-------+-----+
2 rows in set (0.00 sec)

3. What did you see instead?

TiDB 4.0:

mysql> SELECT * FROM t0;
+---------+-----+
| id      | pad |
+---------+-----+
|       1 | a   |
| 2000001 | a   |
+---------+-----+
2 rows in set (0.00 sec)

Because integers default to signed, that is 1/1000th of the keyspace consumed. It looks like the next restart only consumes 30000 values, which is better. But I haven't tested with multiple tidb-servers:

mysql> show create table t0\G
*************************** 1. row ***************************
       Table: t0
Create Table: CREATE TABLE `t0` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pad` varbinary(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=2030001
1 row in set (0.00 sec)

4. What version of TiDB are you using? (tidb-server -V or run select tidb_version(); on TiDB)

mysql> select tidb_version()\G
*************************** 1. row ***************************
tidb_version(): Release Version: v4.0.0-beta.2-352-g5ede18f10
Git Commit Hash: 5ede18f10eedfe2e3690d7728dec3ffa4b0af2d5
Git Branch: master
UTC Build Time: 2020-04-24 03:45:17
GoVersion: go1.13
Race Enabled: false
TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306
Check Table Before Drop: false
1 row in set (0.01 sec)
zimulala commented 4 years ago

PTAL @AilinKid

AilinKid commented 4 years ago

hi~ there is a interesting logic here TiDB has dynamic cache size computation, according to the last cache consume-time. When table created, the default last allocation time is now() although there is no cache allocated before. Once you insert for the first time, the cache finds itself size was 0, so the next cache size will recomputed with formula below:

func NextStep(curStep int64, consumeDur time.Duration) int64 {
    failpoint.Inject("mockAutoIDChange", func(val failpoint.Value) {
        if val.(bool) {
            failpoint.Return(step)
        }
    })

    consumeRate := defaultConsumeTime.Seconds() / consumeDur.Seconds()
    res := int64(float64(curStep) * consumeRate)
    if res < minStep {
        return minStep
    } else if res > maxStep {
        return maxStep
    }
    return res
}

So if you insert after the table created for a while, the next cache size will be 30000, otherwise if you put the create table + insert SQL into server simutaniously, the time interval will be short, so you will get 200000 instead.

jebter commented 5 months ago

The issue has not been updated for too long, so I will close it. If there are any updates you can reopen it.