delete后加 limit是个好习惯么

本文探讨在MySQL中使用DELETE语句时加LIMIT关键字的优势,包括防止误删、减少锁资源占用及避免CPU满载,同时分析了不同场景下LIMIT数量的选择,如一次性删除、循环删除及并行删除的不同效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小伙伴想精准查找自己想看的MySQL文章?喏 → MySQL专栏目录 | 点击这里

在业务场景要求高的数据库中,对于单条删除和更新操作,在delete和update后面加limit 1绝对是个好习惯。比如,在删除执行中,第一条就命中了删除行,如果SQL中有limit 1;这时就return了,否则还会执行完全表扫描才return。效率不言而喻。

那么,在日常执行delete时,我们是否需要养成加 limit 的习惯呢?是不是一个好习惯呢?

在日常的SQL编写中,你写delete语句时是否用到过以下SQL?

delete from t where sex = 1 limit 100; 

你或许没有用过,在一般场景下,我们对 delete 后是否需要加 limit 的问题很陌生,也不知有多大区别,今天带你来了解一下,记得mark!

写在前面,如果是清空表数据建议直接用truncate,效率上truncate远高于delete,应为truncate不走事务,不会锁表,也不会生产大量日志写入日志文件;truncate table table_name 后立刻释放磁盘空间,并重置auto_increment的值。delete删除不释放磁盘空间,但后续insert会覆盖在之前删除的数据上。详细了解请跳转另一篇博文《delete、truncate、drop的区别有哪些,该如何选择》

下面只讨论delete场景,首先,delete后面是支持limit关键字的,但仅支持单个参数,也就是[limit row_count],用于告知服务器在控制命令被返回到客户端前被删除的行的最大值。

delete limit语法如下:

(值得注意的是,当需要用到order by排序时,必须order by + limit联用,否则order by 就会被优化器优化掉,被认为无意义。)

delete [low_priority] [quick] [ignore] from tbl_name
  [where ...]
    [order by ...]
      [limit row_count]

加limit的的优点:

以下面的这条SQL为例:

delete from t where sex = 1; 

 

  • 1. 降低写错SQL的代价,就算删错了,比如limit 500,那也就丢了500条数据,并不致命,通过binlog也可以很快恢复数据。
  • 2. 避免了长事务,delete执行时MySQL会将所有涉及的行加写锁和Gap锁(间隙锁),所有DML语句执行相关行会被锁住,如果删除数量大,会直接影响相关业务无法使用。
  • 3. delete数据量大时,不加limit容易把cpu打满,导致越删越慢。

针对上述第二点,前提是sex上加了索引,大家都知道,加锁都是基于索引的,如果sex字段没索引,就会扫描到主键索引上,那么就算sex = 1 的只有一条记录,也会锁表。



对于delete limit 的使用,MySQL大佬丁奇有一道题:

如果你要删除一个表里面的前 10000 行数据,有以下三种方法可以做到:
第一种,直接执行 delete from T limit 10000;
第二种,在一个连接中循环执行 20 次 delete from T limit 500;
第三种,在 20 个连接中同时执行 delete from T limit 500。

你先考虑一下,再看看几位老铁的回答:

--------------------------------------------

Tony Du:

  • 方案一,事务相对较长,则占用锁的时间较长,会导致其他客户端等待资源时间较长。
  • 方案二,串行化执行,将相对长的事务分成多次相对短的事务,则每次事务占用锁的时间相对较短,其他客户端在等待相应资源的时间也较短。这样的操作,同时也意味着将资源分片使用(每次执行使用不同片段的资源),可以提高并发性。
  • 方案三,人为自己制造锁竞争,加剧并发量。
  • 方案二相对比较好,具体还要结合实际业务场景。

--------------------------------------------
肉山:
不考虑数据表的访问并发量,单纯从这个三个方案来对比的话。

  • 第一个方案,一次占用的锁时间较长,可能会导致其他客户端一直在等待资源。
  • 第二个方案,分成多次占用锁,串行执行,不占有锁的间隙其他客户端可以工作,类似于现在多任务操作系统的时间分片调度,大家分片使用资源,不直接影响使用。
  • 第三个方案,自己制造了锁竞争,加剧并发。

至于选哪一种方案要结合实际场景,综合考虑各个因素吧,比如表的大小,并发量,业务对此表的依赖程度等。
-------------------------------------------
~嗡嗡:

  • 1. 直接delete 10000可能使得执行事务时间过长
  • 2. 效率慢点每次循环都是新的短事务,并且不会锁同一条记录,重复执行DELETE知道影响行为0即可
  • 3. 效率虽高,但容易锁住同一条记录,发生死锁的可能性比较高

-------------------------------------------

怎么删除表的前 10000 行。比较多的朋友都选择了第二种方式,即:在一个连接中循环执行 20 次 delete from T limit 500。确实是这样的,第二种方式是相对较好的。

第一种方式(即:直接执行 delete from T limit 10000)里面,单个语句占用时间长,锁的时间也比较长;而且大事务还会导致主从延迟。

第三种方式(即:在 20 个连接中同时执行 delete from T limit 500),会人为造成锁冲突。

这个例子对我们实践的指导意义就是,在删除数据的时候尽量加 limit。这样不仅可以控制删除数据的条数,让操作更安全,还可以减小加锁的范围。所以,在 delete 后加 limit 是个值得养成的好习惯。

好了,本文就带你了解这些,如果有相关疑问和好想法,请在下方留言,方便和小伙伴儿们一起讨论。

 

一张照片背后的故事

这张照片拍摄于2009年
喜德县北山乡瓦伍村都来小学
一名女孩在上课时举手回答问题
破烂的衣衫和教室
反而更能衬托出她眼神中对学习的渴望
有着这样眼神的孩子
前方的路一定是充满光明的

<think>好的,我现在需要帮用户解决如何在 Nest.js 中实现一个字典接口的问题。首先,我得回想一下用户之前的问题历史。之前用户问过React中setState更新后的操作,现在转向了Nest.js,可能他们正在全栈开发,前端用React,后端用Nest.js。用户可能是想了解如何构建一个基础的CRUD接口,特别是字典类的数据管理。 接下来,我需要确定用户的需求层次。用户提到“实现一个字典接口”,可能指的是创建、读取、更新、删除(CRUD)字典项的功能。但可能需要更详细的信息,比如字典的结构,是否需要分页、验证、错误处理等。不过用户没有给出具体细节,所以需要做一个通用的指南,覆盖基本实现步骤。 首先,我应该从项目结构开始。Nest.js通常使用模块化结构,所以需要创建模块、控制器、服务、实体和DTO。考虑到用户可能对Nest.js有一定了解,但可能需要一步步的指导,所以分步骤讲解比较合适。 然后,数据库部分。Nest.js常用TypeORM,所以需要配置数据库连接,创建实体类。这里可能需要选择数据库类型,比如MySQL或PostgreSQL,但为了通用性,可以先用SQLite作为示例,方便用户快速测试。 接下来是实体定义。字典通常有键、值、描述等字段,所以需要定义这些字段的类型和装饰器。同时,添自动生成的主键ID和自动时间戳,这样用户可以看到完整的实体示例。 然后是DTO,用于数据验证。使用class-validator来添验证规则,比如字段是否必填,长度限制等。这能帮助用户了解如何确保输入数据的正确性。 服务层负责业务逻辑和数据库交互。需要实现CRUD方法,这里要注意异步操作,使用async/await。同时,处理可能的错误,比如查找不到字典项时抛出NotFoundException,这样用户就知道如何正确返回HTTP错误。 控制器层处理HTTP请求,使用不同的装饰器处理GET、POST、PUT、DELETE等方法。需要注入服务,并在路由处理中调用服务的方法。同时,参数校验和正确的状态码返回也是重点,比如POST返回201 Created,DELETE返回204 No Content。 模块部分需要将各个部分组合起来,导入TypeOrmModule,并配置实体。这样用户能理解模块化的好处,以及如何组织代码。 最后,可能需要添Swagger文档,方便用户测试接口。使用@nestjs/swagger可以自动生成API文档,这对前端开发人员很有帮助,特别是如果用户同时负责前后端的话。 测试部分也很重要,建议用户使用Postman或curl测试接口,并提供示例请求,这样用户可以直接复制粘贴进行测试,确保接口按预期工作。 另外,用户可能对高级功能感兴趣,比如分页查询、软删除、缓存、权限控制等,但作为基础教程,可能先覆盖基础部分,再在最后提及扩展点,供用户参考。 需要确保代码示例正确,符合Nest.js和TypeORM的最佳实践,比如使用Repository模式,依赖注入等。同时,注意代码格式和注释,方便用户理解每一步的作用。 最后,总结整个实现步骤,强调模块化、分层结构和最佳实践,帮助用户建立良好的开发习惯。可能还需要提醒用户根据实际需求调整,比如数据库配置、字段定义等,保持灵活性。</think>以下是在 Nest.js 中实现字典接口的完整指南,采用模块化分层架构,包含基础CRUD功能和最佳实践: --- ### 一、项目结构 ```bash src/ ├── dictionary/ │ ├── dto/ │ │ ├── create-dictionary.dto.ts # 创建DTO │ │ └── update-dictionary.dto.ts # 更新DTO │ ├── entities/ │ │ └── dictionary.entity.ts # 实体类 │ ├── dictionary.controller.ts # 控制器 │ ├── dictionary.service.ts # 服务层 │ └── dictionary.module.ts # 模块 ``` --- ### 二、实体定义(TypeORM) ```typescript // src/dictionary/entities/dictionary.entity.ts import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Dictionary { @PrimaryGeneratedColumn() id: number; @Column({ unique: true, comment: '字典键名' }) key: string; @Column({ type: 'json', comment: '字典值' }) value: string | object; @Column({ comment: '字典描述' }) description: string; @Column({ default: true, comment: '是否启用' }) isActive: boolean; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) createdAt: Date; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' }) updatedAt: Date; } ``` --- ### 三、DTO 数据验证 ```typescript // src/dictionary/dto/create-dictionary.dto.ts import { IsNotEmpty, IsString, IsObject } from 'class-validator'; export class CreateDictionaryDto { @IsNotEmpty({ message: '键名不能为空' }) @IsString() key: string; @IsNotEmpty({ message: '值不能为空' }) value: string | object; @IsString() description: string; } // src/dictionary/dto/update-dictionary.dto.ts import { PartialType } from '@nestjs/swagger'; import { CreateDictionaryDto } from './create-dictionary.dto'; export class UpdateDictionaryDto extends PartialType(CreateDictionaryDto) {} ``` --- ### 四、服务层实现 ```typescript // src/dictionary/dictionary.service.ts import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Dictionary } from './entities/dictionary.entity'; @Injectable() export class DictionaryService { constructor( @InjectRepository(Dictionary) private readonly dictRepository: Repository<Dictionary>, ) {} // 创建字典项 async create(createDto: CreateDictionaryDto): Promise<Dictionary> { const newDict = this.dictRepository.create(createDto); return await this.dictRepository.save(newDict); } // 查询所有字典项(带分页) async findAll(page = 1, limit = 10): Promise<[Dictionary[], number]> { return await this.dictRepository.findAndCount({ skip: (page - 1) * limit, take: limit, }); } // 按key查询 async findByKey(key: string): Promise<Dictionary> { const dict = await this.dictRepository.findOne({ where: { key } }); if (!dict) throw new NotFoundException(`字典项 ${key} 不存在`); return dict; } // 更新字典项 async update(id: number, updateDto: UpdateDictionaryDto): Promise<Dictionary> { await this.dictRepository.update(id, updateDto); const updatedDict = await this.dictRepository.findOneBy({ id }); if (!updatedDict) throw new NotFoundException(`字典项 #${id} 不存在`); return updatedDict; } // 删除字典项(软删除) async remove(id: number): Promise<void> { const result = await this.dictRepository.softDelete(id); if (result.affected === 0) { throw new NotFoundException(`字典项 #${id} 不存在`); } } } ``` --- ### 五、控制器实现 ```typescript // src/dictionary/dictionary.controller.ts import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; import { DictionaryService } from './dictionary.service'; import { CreateDictionaryDto, UpdateDictionaryDto } from './dto'; @Controller('dictionary') export class DictionaryController { constructor(private readonly dictService: DictionaryService) {} @Post() async create(@Body() createDto: CreateDictionaryDto) { return this.dictService.create(createDto); } @Get() async findAll( @Query('page') page: number = 1, @Query('limit') limit: number = 10 ) { return this.dictService.findAll(page, limit); } @Get(':key') async findByKey(@Param('key') key: string) { return this.dictService.findByKey(key); } @Put(':id') async update( @Param('id') id: string, @Body() updateDto: UpdateDictionaryDto ) { return this.dictService.update(+id, updateDto); } @Delete(':id') async remove(@Param('id') id: string) { return this.dictService.remove(+id); } } ``` --- ### 六、模块整合 ```typescript // src/dictionary/dictionary.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Dictionary } from './entities/dictionary.entity'; import { DictionaryService } from './dictionary.service'; import { DictionaryController } from './dictionary.controller'; @Module({ imports: [TypeOrmModule.forFeature([Dictionary])], controllers: [DictionaryController], providers: [DictionaryService], }) export class DictionaryModule {} ``` --- ### 七、API 文档集成(Swagger) ```typescript // main.ts import { NestFactory } from '@nestjs/core'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Swagger配置 const config = new DocumentBuilder() .setTitle('Dictionary API') .setDescription('字典管理接口文档') .setVersion('1.0') .addTag('dictionary') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api-docs', app, document); await app.listen(3000); } bootstrap(); ``` --- ### 八、测试接口 ```bash # 创建字典项 curl -X POST http://localhost:3000/dictionary \ -H "Content-Type: application/json" \ -d '{ "key": "GENDER", "value": {"1":"男","2":"女"}, "description": "性别字典" }' # 查询所有字典项(分页) curl http://localhost:3000/dictionary?page=1&limit=10 # 按key查询 curl http://localhost:3000/dictionary/GENDER # 更新字典项 curl -X PUT http://localhost:3000/dictionary/1 \ -H "Content-Type: application/json" \ -d '{"description": "更新后的性别字典"}' # 删除字典项 curl -X DELETE http://localhost:3000/dictionary/1 ``` --- ### 高级扩展建议 1. **缓存机制**:使用Redis缓存高频访问的字典数据 2. **权限控制**:集成JWT实现接口访问权限控制 3. **国际化**:添多语言支持字段 4. **版本控制**:使用`@Version`装饰器实现API版本管理 5. **操作日志**:添审计日志记录字典变更 6. **数据校验**:扩展自定义校验装饰器(如校验key格式) 这个实现方案遵循了Nest.js的最佳实践,采用分层架构设计,具备完整的类型检查和错误处理机制,可直接用于生产环境。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_陈哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值