为何获取数据库首个空闲Device Type Id的第二种代码更优?
两种获取空闲Device Type Id实现的优势对比
需求背景
在预定义的ID范围(如10-20)内,从数据库中获取首个未被使用的Device Type Id。例如现有ID列表为[1,2,4,10,11,12,14],范围10-20时需返回13。
两种实现代码
第一种实现
firstFreeId = Enumerable.Range(_idRangeFrom.Value + 1, _idRangeTo.Value).First(i => !_dbContext.DeviceTypes.Any(dt => dt.DeviceTypeId == i));
第二种实现
var deviceTypeIds = _dbContext.DeviceTypes.Where(e => e.DeviceTypeId > _idRangeFrom.Value && e.DeviceTypeId < _idRangeTo.Value).Select(e => e.DeviceTypeId); firstFreeId = Enumerable.Range(_idRangeFrom.Value + 1, _idRangeTo.Value).First(i => !deviceTypeIds.Any(dtId => dtId == i));
(注:_idRangeFrom和_idRangeTo为预定义范围,_dbContext.DeviceTypes是EF对应数据库的DeviceTypes表上下文)
第二种实现的核心优势
数据库查询次数大幅减少
第一种实现会对Enumerable.Range生成的每个ID发起单独的数据库查询(Any(dt => dt.DeviceTypeId == i)),如果范围跨度大,会产生数十甚至上百次数据库请求。而第二种实现仅执行一次范围查询,把目标区间内已存在的所有ID一次性拉取到内存,后续判断全在内存中完成,仅需1次数据库请求,极大降低了网络连接和数据库交互的开销。降低数据库负载
多次小查询会频繁占用数据库连接,增加CPU与IO消耗;单次范围查询能更好地利用数据库查询优化器,尤其当DeviceTypeId有索引时,范围扫描的效率远高于多次单值匹配查询。缩短数据库交互窗口
虽然两种实现都存在并发场景下的竞态风险(比如查询到空闲ID后被其他线程抢先占用),但第二种实现的数据库操作时间更短,一定程度上降低了这类问题的发生概率。内存判断效率更高
把目标区间内的已用ID加载到内存后,后续的Any判断是纯内存操作,相比第一种每次都要走网络请求到数据库,响应速度提升明显,尤其是当范围内已用ID数量不多时,优势更突出。
内容的提问来源于stack exchange,提问作者Standej
相关产品推荐
相关产品推荐

