lealone / Lealone

比 MySQL 和 MongoDB 快10倍的 OLTP 关系数据库和文档数据库
Other
2.44k stars 514 forks source link

根据ID获取不到数据 #200

Closed cbqqkcel closed 12 months ago

cbqqkcel commented 1 year ago

image id是没有空字符串的。 image 根据ID却查询不到数据

create sequence if not exists PurchaseInBill start with 100000;
create table if not exists PurchaseInBill
(
    id          varchar primary key,
    warehouseId bigint,
    supplierId  bigint,
    supplier    varchar,
    orderId     varchar   not null,
    totalQty    decimal(11, 3)    not null,
    totalAmount decimal(11, 3),
    status      tinyint   not null,
    inAt        timestamp,
    remark      varchar,
    creatorId   bigint,
    creator     varchar,
    version     integer   not null default 0,
    tenantId    integer   not null default 0,
    createAt    timestamp not null,
    updateAt    timestamp not null,
    isDelete    boolean   not null default false
);
cbqqkcel commented 1 year ago

这个是刚更新完seq bug 后压力测试下插入的数据。之前是没有这个问题的。

codefollower commented 1 year ago

你的 insert 语句是什么样子? id 是 varchar,跟 sequence 没有关系。

codefollower commented 1 year ago

也可能是昨天提交的代码,我只是跑通了所有单元测试,性能测试还没有做。

cbqqkcel commented 1 year ago
INSERT INTO `PurchaseInBill`(`id`, `orderId`, `supplierId`, `supplier`, `warehouseId`, `status`, `tenantId`, `totalQty`,
                             `totalAmount`, `version`, `createAt`, `updateAt`)
VALUES ('POIS100784', 'PO0101300', 1, '默认', 1, 1, 0, 4300.0000000000, 860000.00000000000000000000, 0, now(), now())
@Override
    public boolean create(PurchaseInBill header) {
        header.setId("POIS" + nextId());
        header.setStatus(InBillStatus.CREATE);
        if (header.getWarehouseId() == null) {
            header.setWarehouseId(WarehouseUtil.picker().getId());
        }
        $.setTotal(header);
        super.create(header);
        return lineService.create(header.getLines());
    }

create是加了事务的方法

nextId()

public class IdUtil {
    public static Long getSequenceId(Class<?> clazz) {
        var row = Db.selectOneBySql(StrUtil.format("select {}.NEXTVAL as value", clazz.getSimpleName()));
        return row.getLong("value");
    }
}
cbqqkcel commented 1 year ago

是不是索引坏了。查询比较慢的时候我重启服务了。

cbqqkcel commented 1 year ago

image

这个问题也比较奇葩

create sequence if not exists PurchaseInBillLine;
create table if not exists PurchaseInBillLine
(
    id         bigint           default (NEXT VALUE FOR PurchaseInBillLine) primary key,
    headerId   varchar not null,
    remark     varchar,
    skuId      bigint  not null,
    qty        bigint  not null,
    price      bigint,
    areaId     integer,
    locationId integer,
    batchNo    varchar(32),
    tenantId   integer not null default 0,
    createAt   timestamp,
    updateAt   timestamp,
    isDelete   boolean not null default false,
    foreign key (headerId) references PurchaseInBill (id) on delete cascade
);

这个我主键是不为空,自动生成的。这么能查询出Id为空的数据出来了

codefollower commented 1 year ago

你把历史数据清一下再重新跑跑,昨天提交的代码修复了一些问题。

我现在压测下面的代码:

private void executeInNewThread(CountDownLatch latch) {
        new Thread(() -> {
            try {
                Connection conn = getConnection();
                Statement stmt = conn.createStatement();
                stmt.executeUpdate("set lock_timeout 1000000");
                conn.setAutoCommit(false);
                for (int row = 0; row < 1000; row++) {
                    ResultSet rs = stmt.executeQuery("SELECT seq_locked.NEXTVAL");
                    rs.next();
                    int v = rs.getInt(1);
                    rs.close();
                    stmt.execute(
                            "insert into SequenceLockedExceptionTest values('f" + v + "', " + row + ")");
                    conn.commit();
                }
                latch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

发现一个问题是压测久了变慢,数据是能查的。

codefollower commented 1 year ago

通过 create sequence 创建的 sequence,是共享 sequence,是严格支持单调递增的,如果多个事务并发执行 sequence.NEXTVAL,实际上是串行执行,也就是即便事务回滚了,sequence 也会自动回滚到上一个值。

共享 sequence 比较慢,如果只是为了得到一个自增的主键,在建表时使用 id long auto_increment primary key 会快很多。

cbqqkcel commented 1 year ago

测试了一下暂时没有问题 我这个主表ID是字符串要加前缀的POSI的

cbqqkcel commented 1 year ago

id long auto_increment primary key, 创建的能指定seq名称,和起始值吗 不能指定名称好像就不好在创建表的时候设置起始值,要查询一下才自动SEQ名称是啥然后修改

cbqqkcel commented 1 year ago

共享的能指定不回滚到上一个值吗

cbqqkcel commented 1 year ago

测试了一下暂时没有问题 我这个主表ID是字符串要加前缀的POSI的

一般的ID都有生成策略的,如果能在SEQ指定策略就方便多了。 比如 前缀+日期+自增+后缀啥的。 不够这个需求很难通用。一般的项目都是自己键一张表维护ID。

codefollower commented 1 year ago

auto_increment 目前没有其他配置项。 共享 sequence 不能指定不回滚。

如果主键是字符串,想用 sequence 来组装,你可以直接在 insert 语句里面这样写: insert into test(pk) values('POSI'||seq.NEXTVAL), 不用在一个事务里用另一条 select 语句查出 sequence 的值,这样也很快。

cbqqkcel commented 1 year ago

这样就要写 SQL语句插入了,一般的框架都封装好了insert方法,回头看看框架支持不。否则为了这点性能写SQL又太麻烦了

codefollower commented 1 year ago

正常来讲即便串行执行也满足大多数中小企业的需求的,我现在压测久了变慢,可能哪里还是有点问题,比如 GC,最开始也是很快的。

codefollower commented 1 year ago

通过 create sequence 语句来关联某个表,让它变成某个表的私有 sequence 是很容易做到的,我改进一下。 通过 auto_increment 创建的 sequence 就是带有 BELONGS_TO_TABLE 关键字的。

cbqqkcel commented 1 year ago

image findByHeaderId = select * from PurchaseOrderLine where headerId = 'PO0101419' 这个是还未提交事务。 这个过程如下 insert header = PurchaseOrder insert lines = PurchaseOrderLines 查询 lines 然后出现了qty为空的情况。数据库的创建语句设置了 qty 不为空的。 也是在压力测试下出现的问题。

不知道是数据库的问题还框架的问题。

codefollower commented 1 year ago

我记错了 auto_increment 后面支持 start 和 increment, 语法像这样 id long auto_increment(1000) 或 id long auto_increment(1000,1)

cbqqkcel commented 1 year ago

43条数据,出错的时候总是有一条空数据

codefollower commented 1 year ago

可能还是数据库的问题,昨天的改动很核心,正在跑 tpcc,也遇到一些问题。

cbqqkcel commented 12 months ago

又出现了,这次不是多线程出现的,昨天好好的,今天可能是重启比较多然后就出现了。今天没有新增数据。 image image

codefollower commented 12 months ago

是正常停止数据库还是直接杀死?如果没有新增数据按道理不会有修改操作了,最多就是重启时执行一下老的 redo log。正常停止数据库会自动执行一次 checkpoint,重启时也不会有 redo log。

cbqqkcel commented 12 months ago

大部分情况是正常重启的。主要25日-27日的数据都有问题。就几条

cbqqkcel commented 12 months ago

我刚才测试了一下新增了一条数据后,然后正常停止 会打印 Stopping TcpServer accepter TcpServer stopped 之后再启动数据就这样了

cbqqkcel commented 12 months ago

不重启就不会有问题,哪怕我在重启之前执行 backup to "xxx.zip" 然后正常重启,还是会出现问题。

cbqqkcel commented 12 months ago

image redo_log有许多文件

cbqqkcel commented 12 months ago

感觉触发了一个特点的BUG开关,现在100%出现这个问题。之前还是多线程情况下才会有的。

codefollower commented 12 months ago

我马上排查一下问题

codefollower commented 12 months ago

image redo_log有许多文件

这个是已经归档的 redo log,会自动删除的,上一级目录的 redo log 才有用。

codefollower commented 12 months ago

我前几天改了一下 gc 算法,现在压测也每次都出问题,可能改出 bug 了。