从数据库中随机抽取一条记录的方法

2 篇文章 0 订阅
1 篇文章 0 订阅

一、背景

        在Spring项目开发的过程中,遇到一个需求:需要从一个存有300w数据的表中随机抽取一系列具有特定字段值的数据;由于数据量大,采用普通的方式暴力搜索会产生巨大性能与时间开销,所以需要编写一个高性能的查询方法以满足需求。

二、设计思路

        由于Spring及Mybatis并未提供高性能随机查询方法,所以代码应改动到Mapper.xml文件,自定义相关方法;其次,无论如何,操作数据库的方法都较重,且项目随机性要求不是非常严格,可以考虑缓存一部分查到的数据以供下次调用,尽可能减少与数据库的交互次数。

        注:本文仅提供思路参考,代码为便于理解改动过,请谨慎使用!

三、具体实现

3.1 Controller层逻辑

        基本的业务逻辑如下:通过GET方法发起请求,随后getRandomContent方法调用服务实现层(原因后续阐述)的某个应用缓存的方法用于获取一个指定大小的内容列表,每次调用时从该列表中随机抽取一条记录返回;

    @ApiOperation(value = "获取随机数据")
    @RequestMapping(value = "/getRandomContent", method = RequestMethod.GET)
    public ApiResponse<?> getRandomContent() {
        // 这里建立一个List负责存储查询到的数据
        List<Content> contentList = contentService.selectContentList(50);
        if (contentList.isEmpty()) {
            return Response.fail("未查询到可用数据");
        } else {
            // 在 0~List大小 的范围内随机生成一个数作为下标
            int random = new Random().nextInt(contentList.size());
            // 取下标为 random 的数据
            Content randomContent = contentList.get(random);
            return Response.success(randomContent);
        }
    }

3.2 ServiceImpl层逻辑

        Service层需要做到的事如下:

  1. 初次搜索或缓存过期时:调用Dao层语句查询数据库,并将查询到的列表放入缓存;
  2. 若缓存中存有键为"'ValidContentList' + #limit"的数据时,不进入方法体,直接返回缓存内容。

        注:关于注解@Cacheable的学习笔记笔者仍在总结,建议需要的读者先自行查询。

    @Override
    @Cacheable(value = Consts.CACHE_GROUP_ONE, 
               key = "'ValidContentList' + #limit")
    public List<Content> selectValidContentList(Integer limit) {
        // 查询数据库操作
        List<Content> contents = contentMapper.selectContentList(limit);
        // 若程序执行到此处说明缓存未命中或已过期,做出log提示
        log.error("selectValidContentList未命中缓存");
        if (contents.isEmpty()) {
            throw new ResponseException(ResponseEnum.QUERY_EMPTY);
        }
        return contents;
    }

3.3 Mapper层逻辑

        直接在Mapper层操作数据库的灵活度大于其他层,Mabatis提供的数据库操作语句十分全面,可以满足大多数需求;本层需要实现的目标为从数据库指定表中随机抽取数条"status"字段为"VALID"的记录;

    <!-- 查询可用内容集合 -->
    <select id="selectContentList" resultMap="ContentResultMap">
        <!-- 从最小递增值与最大递增值间随机取数作为虚拟表id -->
        SET @minId = (SELECT MIN(id) FROM content WHERE status = #{status});
        SET @maxId = (SELECT MAX(id) FROM content WHERE status = #{status});
        SET @rId = TRUNCATE(@minId + (RAND() * (@maxId - @minId)));
        <!-- 进行连表查询 -->
        SELECT *
        FROM content AS c
        WHERE c.id >= @rId
          AND c.status = 'VALID'
            LIMIT #{limit}
    </select>

SQL代码解析:

  首先通过两次查询分别确定表内自动递增值的上下限;

  其次生成一个虚拟id:TRUNCATE(@minId + (RAND() * (@maxId - @minId)))的含义是随机取(下限~上限)之间的一个浮点数,并截断其小数部分避免溢出;

  最后将真实表的主键与虚拟id关联,取limit条符合条件的数据后返回。

 四、总结

        在数据量较大且并发较高的环境下,频繁查询数据库的操作可能导致数据库崩溃,为了达成相应目标,最好牺牲部分随机性,采用缓存等方式预先储存一部分数据集,之后缓存存活期每次调用可以直接从缓存中取出数据;

        在这里笔者附上一个表格,可以直观展示使用SQL语句优化及ehCache后的系统性能:

不同方式下数据查询时间
查询方式(查询50条数据)耗时
使用ORDER BY RAND()方法>13000ms
使用虚拟表连表查询但不使用缓存平均500ms
使用虚拟表连表查询且使用缓存(缓存存活期)平均2ms

        以上,希望能够帮助到各位。

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用以下SQL查询:SELECT AVG(price) FROM bookList ORDER BY RAND() LIMIT 10; 这将会告诉您从bookList表随机抽取10条记录的平均单价。 ### 回答2: 要从bookList表随机抽取10条记录,并计算图书的平均单价,首先我会使用数据库查询语言(如SQL)来实现这一功能。 假设表结构如下: bookList (书名, 单价) 我会编写以下SQL查询语句来随机抽取10条记录: SELECT TOP 10 单价 FROM bookList ORDER BY NEWID() 这个查询语句将返回bookList表随机10条记录的单价。我会将这些单价相加,并将结果除以10来计算平均单价。 接下来,我会使用编程语言(如Python)来执行以上查询语句,并进行计算。这是一个示例代码: import sqlite3 # 连接数据库 conn = sqlite3.connect('database.db') cursor = conn.cursor() # 执行查询语句 cursor.execute("SELECT TOP 10 单价 FROM bookList ORDER BY NEWID()") rows = cursor.fetchall() # 计算总价 total_price = sum(row[0] for row in rows) # 计算平均单价 average_price = total_price / 10 # 关闭数据库连接 conn.close() 最后,我会将平均单价作为结果输出。这个值代表了从随机抽取的10条记录图书的平均价格。 ### 回答3: 要计算图书的平均单价,首先需要从名为bookList的表随机选取10条记录。然后,我们需要将这10本图书的价格相加,并除以10得到平均单价。 具体步骤如下: 1. 随机选择10条记录:使用合适的方法从bookList表随机选取10条记录,确保选择的记录随机的且没有重复。 2. 计算图书价格总和:将这10本图书的价格相加,得到总价格。 3. 计算平均单价:将总价格除以10,得到平均单价。这个平均单价代表了图书的平均价格。 总之,通过以上步骤,我们就可以从bookList表随机抽取10条记录,并计算出图书的平均单价。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值