当前位置: 首页 > news >正文

芝法酱学习笔记(2.1)——sql性能优化1

一、前言

做程序员这行,相信大家都会接触到性能优化这个概念。并且多数程序员,为自己能参与到性能优化的工作而感到自豪。
在普通应用系统的业务开发中,程序的性能瓶颈往往在数据库查询的IO上,所以优化数据库查询则是重中之重的工作。
本章以sql性能优化为引,将逐步展开Java开发各中间件的学习。

二、最基础的优化

实际上多数的性能瓶颈,并不需要搞高大上的分库分表,也不需要做缓存。仅仅修正表的字段类型;合理设置表的索引;优化查询方式,减少不必要连表。就可以大幅提升数据库查询性能。
我们还是以销售单报表查询这个简单的业务背景,做本节内容的讲解。

2.1 一个真实的惨痛教训

小编毕业是做游戏开发的,Java开发属于半路跳船,2020年才开始涉足。所以我接触的Java相关的知识相对比较新。在我实际工作中,遇到了非常多匪夷所思的表设计,即什么字段都喜欢搞成varchar。比如订单的时间,商品的关联ID。这些不科学的设计,无疑是性能的巨大隐患,将会在你想不到的地方埋上大坑。
为什么会有这样的现象呢?很有可能是一些程序员在学校里没认真学习,出门后跟着一些老程序员(80后)写代码,形成了一些恶习。一些团队十分守旧,坚持用早期版本的mysql(5.7以前),并且对技术过于敬畏,不敢升级mysql驱动。这样把各字段定成varchar,就避免了大量变量转化的问题。毕竟在项目在开始阶段,性能问题并不是大家关注的,大家更关注是否能快速实现。
我上家公司的工作经历,就遇到了一个这样的坑。我们的业务是这样的,一个商品对应着多套价格,有一个商品表,有一个商品价格表(1对多)。而商品价格表中,商品的关联id使用了varchar。在商品报表页面,需要把商品表和商品价格表连表查询,展示商品的所有价格。我们的sql大致是这样写的:

SELECT it.id,it.name,ipc.price1, ipc.price2 ... FROM
item it JOIN item_price ipc ON it.id = ipc.item_id and it.enp_code = ipc.enp_code
WHERE it.enp_code = #{In.enp_code}
LIMIT 0,10

我们一家企业,商品数量最多也就小几万(多数不到1万),就这样一个看上去简单到不行的连表查询,每次页面刷新都得等1秒以上。
使用explain发现,连边并没有走索引,可我们明明在item_price建了关于enp_code和item_id的索引。
后来我们发现,因为item_price的表商品id使用了varchar,在sql实际执行的时候,发生了类型转换,相当于对索引列使用了函数,自然就不会走索引。于是数量级从万就变成了万乘万。
用下面这个抽象的的做法,反倒使效率提升了。

SELECT it.id,it.name,ipc.price1, ipc.price2 ... FROM
item it JOIN item_price ipc ON CAST(it.id,CHAR) = ipc.item_id and it.enp_code = ipc.enp_code
WHERE it.enp_code = #{In.enp_code}
LIMIT 0,10

最后在老板万般纠结下,拖了2个月,终于下决心把线上数据库的索引改了。唉~
有些时候,只要按照规范来设计表,写代码,就不会遇到这些问题。实际上不是每一个技术点都需要深究的。

2.2 一些其他遇到的存疑问题

2.2.1 join关联不使用主键

我上家公司由于做了按照企业分库,每个表都有个enp_code的字段作为企业的唯一标识。强制要求拼上 left.enp_code = right.enp_code,并且join表用另一个非主键的字段关联。

2.2.2 时间用char

有时,时间直接用char,而不用datetime

2.2.3 order by id

由于上家公司的id用的是自增id,那么理论上说,id越靠后是不是时间就越晚。于是公司的大聪明在写排序字段时,就用ORDER BY id DESC,看上去是不是很美妙。
而实际explain一下,这样做不走索引了,直接走主键索引,反倒更慢了。

2.2.4 联合索引最左匹配原则

如果明确知道两个或多个查询条件会同时出现,那不如创建联合索引。实际查询时,mysql往往只会选择一个索引。
还是拿销售单查询举例,如果建一个企业id索引,建一个订单时间索引,在查询时并不会两个索引同时使用。

三、实验环境搭建

下面,我们来建一个项目来实现各种不规范建表建索引带来的性能损失

3.1 数据库表展示

本次主要为了验证sql性能问题,对业务做大幅简化

3.1.1 企业表

在这里插入图片描述
索引:

  • uidx_code: code

3.1.2 业务员表

在这里插入图片描述
索引:

  • idx_enp_id: enp_id

3.1.3 门店表

在这里插入图片描述
索引:

  • idx_enp_id: enp_id

3.1.4 商品表

在这里插入图片描述
这个表索引估计建抽象点,方便验证
索引:

  • idx_rel_id: enp_id;rel_id

3.1.5 销售单头表

在这里插入图片描述
索引:

  • uidx_bill_no: enp_id;bill_no
  • idx_time_key: enp_id;bill_time_key
  • idx_time: enp_id;bill_time
  • idx_time_str: enp_id;bill_time_str
  • idx_sales: enp_id;sales_id;bill_time_key
  • idx_customer: enp_id;customer_id;bill_time_key

3.1.6 销售单

在这里插入图片描述
索引:

  • idx_bill_date_key:enp_id;bill_time_key
  • idx_header_id: header_id
  • idx_enp_id_bill_no: enp_id;bill_no

3.2 模拟生成数据优化

在执行init前,先临时关掉mysql的binlog,加速执行
位置在/etc/mysql/my.cnf

[mysqld]
skip-log-bin

注意,改了配置要重启mysql
在application.yml配置里,关掉sql的log,并在连接中加入如下参数

url: jdbc:mysql://192.168.0.64:3306/study2024-class008-busy001?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&useServerPrepStmts=false

3.3 模拟代码

本处,我们忽略掉一些工具类和生成类的代码,只展示核心内容。具体代码看最后的代码展示。

@Slf4j
@RequiredArgsConstructor
@Component
public class ConsignServiceImpl implements IConsignService {final ConsignMapper mConsignMapper;final IGenEnterpriseDbService mEnterpriseDbService;final IGenSalesManDbService mSalesManDbService;final IGenCustomerDbService mCustomerDbService;final IGenItemDbService mItemDbService;final IGenConsignDbService mConsignDbService;final IGenConsignHeaderDbService mConsignHeaderDbService;final BillNoUtil mBillNoUtil;final PlatformTransactionManager mTransactionManager;@Overridepublic void initTestData() {final int ENP_CNT = 10;final int SALES_CNT_MIN = 5;final int SALES_CNT_MAX = 20;final int CUSTOMER_CNT_MIN = 20;final int CUSTOMER_CNT_MAX = 50;final int ITEM_CNT_MIN = 2000;final int ITEM_CNT_MAX = 10000;final long ITEM_COST_MIN = 500;final long ITEM_COST_MAX = 19800;final int ITEM_PROFIT_MIN = 10;final int ITEM_PROFIT_MAX = 50;// 每个门店售卖某个商品的概率final int CUSTOMER_ITEM_SELECT_PERCENT = 30;// 每个售货员当天来特定门店的概率final int CUSTOMER_SALES_SELECT_PERCENT = 15;// 售货员来该门店,每样商品送货的概率,万分制// 这个概率远小于真实情况,只不过我为了导入快点。正常概率,我们认为一周送2次货,拍2800更合适final int ITEM_SALES_SELECT_PERCENT = 250;final int ITEM_SALES_CNT_MIN = 10;final int ITEM_SALES_CNT_MAX = 1000;final String DAY_BEGIN = "2018-01-01";final String DAY_END = "2024-12-31";// 清空测试数据mEnterpriseDbService.clearTest();mSalesManDbService.clearTest();mCustomerDbService.clearTest();mItemDbService.clearTest();mConsignDbService.clearTest();mConsignHeaderDbService.clearTest();// 生成企业List<GenEnterpriseEntity> entList = new ArrayList<>();for(int i = 1; i <= ENP_CNT; i++) {GenEnterpriseEntity enterpriseEntity = new GenEnterpriseEntity();enterpriseEntity.createInit();enterpriseEntity.setCode(String.format("enp_%03d", i));enterpriseEntity.setName(String.format("enp_%03d", i));enterpriseEntity.setTestData(true);entList.add(enterpriseEntity);}mEnterpriseDbService.saveBatch(entList);Map<Long,List<GenSalesManEntity>> salseManMap = new HashMap<>();Map<Long,List<GenCustomerEntity>> customerMap = new HashMap<>();Map<Long,List<GenItemEntity>> itemMap = new HashMap<>();Map<Long,List<GenItemEntity>> customerItemMap = new HashMap<>();// 循环每个企业,用局部变量,节省内存for(GenEnterpriseEntity enterpriseEntity : entList) {// 为每个企业生成售货员int salesCnt = RandomUtil.randomInt(SALES_CNT_MIN, SALES_CNT_MAX);List<GenSalesManEntity> salesManEntityList = new ArrayList<>();for(int i = 1; i <= salesCnt; i++) {GenSalesManEntity salesManEntity = new GenSalesManEntity();salesManEntity.createInit();salesManEntity.setName(String.format("%s_sales_%03d", enterpriseEntity.getName(),i));salesManEntity.setTestData(true);salesManEntity.setEnpId(enterpriseEntity.getId());salesManEntity.setEnpCode(enterpriseEntity.getCode());salesManEntityList.add(salesManEntity);}mSalesManDbService.saveBatch(salesManEntityList);salseManMap.put(enterpriseEntity.getId(), salesManEntityList);// 为每个企业生成门店int customerCnt = RandomUtil.randomInt(CUSTOMER_CNT_MIN, CUSTOMER_CNT_MAX);List<GenCustomerEntity> customerEntityList = new ArrayList<>();for(int i = 1; i <= customerCnt; i++) {GenCustomerEntity customerEntity = new GenCustomerEntity();customerEntity.createInit();customerEntity.setName(String.format("%s_customer_%03d", enterpriseEntity.getName(),i));customerEntity.setTestData(true);customerEntity.setEnpId(enterpriseEntity.getId());customerEntity.setEnpCode(enterpriseEntity.getCode());customerEntityList.add(customerEntity);}mCustomerDbService.saveBatch(customerEntityList);customerMap.put(enterpriseEntity.getId(), customerEntityList);// 为每个企业生成商品int itemCnt = RandomUtil.randomInt(ITEM_CNT_MIN, ITEM_CNT_MAX);List<GenItemEntity> itemEntityList = new ArrayList<>();for(int i=1;i<=itemCnt;i++){GenItemEntity itemEntity = new GenItemEntity();itemEntity.createInit();itemEntity.setRelId(String.valueOf(itemEntity.getId()));itemEntity.setName(String.format("%s_item_%03d", enterpriseEntity.getName(),i));long cost = RandomUtil.randomLong(ITEM_COST_MIN, ITEM_COST_MAX);itemEntity.setCost(cost);itemEntity.setTestData(true);itemEntity.setEnpId(enterpriseEntity.getId());itemEntity.setEnpCode(enterpriseEntity.getCode());itemEntityList.add(itemEntity);}mItemDbService.saveBatch(itemEntityList);itemMap.put(enterpriseEntity.getId(), itemEntityList);// 确定每个门店的商品列表for(GenCustomerEntity customerEntity : customerEntityList) {List<GenItemEntity> itemList = new ArrayList<>();for(GenItemEntity itemEntity : itemEntityList) {int rd =RandomUtil.randomInt(0,100);if(rd < CUSTOMER_ITEM_SELECT_PERCENT){itemList.add(itemEntity);}}customerItemMap.put(customerEntity.getId(), itemList);}}LocalDate startDate = LocalDate.parse(DAY_BEGIN);LocalDate endDate = LocalDate.parse(DAY_END);final ExecutorService executorService = Executors.newFixedThreadPool(20);while (!startDate.isAfter(endDate)) {log.info("==================>>导入日期"+startDate+"====================");// 遍历每个企业for(GenEnterpriseEntity enterpriseEntity : entList) {List<GenSalesManEntity> salesManEntityList = salseManMap.get(enterpriseEntity.getId());List<GenCustomerEntity> customerEntityList = customerMap.get(enterpriseEntity.getId());List<GenConsignHeaderEntity> consignHeaderEntityList = new ArrayList<>();List<GenConsignEntity> consignEntityList = new ArrayList<>();log.info("导入企业"+enterpriseEntity.getName());// 遍历旗下所有销售for(GenSalesManEntity salesManEntity : salesManEntityList) {// 遍历旗下所有门店for(GenCustomerEntity customerEntity : customerEntityList) {int rd = RandomUtil.randomInt(0,100);// 售货员不来该门店if(rd >= CUSTOMER_SALES_SELECT_PERCENT){continue;}//创建头表GenConsignHeaderEntity consignHeaderEntity = new GenConsignHeaderEntity();consignHeaderEntity.createInit();consignHeaderEntity.setEnpId(enterpriseEntity.getId());consignHeaderEntity.setEnpCode(enterpriseEntity.getCode());consignHeaderEntity.setBillNo(mBillNoUtil.getBillNo("csn"));int hour = RandomUtil.randomInt(0,24);int minute = RandomUtil.randomInt(0,59);int second = RandomUtil.randomInt(0,59);LocalDateTime billTime = startDate.atTime(hour, minute, second);long billTimeKey = CommonUtil.LocalDateTimeToSecond(billTime);String billTimeStr = LocalDateTimeUtil.formatNormal(billTime);consignHeaderEntity.setBillTime(billTime);consignHeaderEntity.setBillTimeKey(billTimeKey);consignHeaderEntity.setBillTimeStr(billTimeStr);consignHeaderEntity.setCustomerId(customerEntity.getId());consignHeaderEntity.setCustomerName(customerEntity.getName());consignHeaderEntity.setSalesId(salesManEntity.getId());consignHeaderEntity.setSalesName(salesManEntity.getName());consignHeaderEntity.setDescription(billTimeStr+" "+customerEntity.getName()+" "+salesManEntity.getName());consignHeaderEntity.setTestData(true);consignHeaderEntityList.add(consignHeaderEntity);List<GenItemEntity> itemEntityList = customerItemMap.get(customerEntity.getId());for(GenItemEntity itemEntity : itemEntityList) {rd = RandomUtil.randomInt(0,10000);if(rd >= ITEM_SALES_SELECT_PERCENT){continue;}int saleCnt = RandomUtil.randomInt(ITEM_SALES_CNT_MIN,ITEM_SALES_CNT_MAX);GenConsignEntity consignEntity = new GenConsignEntity();consignEntity.createInit();consignEntity.setEnpId(enterpriseEntity.getId());consignEntity.setEnpCode(enterpriseEntity.getCode());consignEntity.setCustomerId(customerEntity.getId());consignEntity.setCustomerName(customerEntity.getName());consignEntity.setItemId(itemEntity.getId());consignEntity.setItemStrId(String.valueOf(itemEntity.getId()));consignEntity.setItemName(itemEntity.getName());consignEntity.setItemCnt(saleCnt);consignEntity.setSalesId(salesManEntity.getId());consignEntity.setSalesName(salesManEntity.getName());consignEntity.setBillTime(billTime);consignEntity.setBillTimeKey(billTimeKey);consignEntity.setHeaderId(consignHeaderEntity.getId());consignEntity.setBillNo(consignHeaderEntity.getBillNo());int profitPercent = RandomUtil.randomInt(ITEM_PROFIT_MIN,ITEM_PROFIT_MAX);long cost = (long)(itemEntity.getCost() * (1 + profitPercent/100.0));consignEntity.setPrice(cost);consignEntityList.add(consignEntity);}}}// 多线程反而更慢,因为插入的顺序问题
//                executorService.submit(()->{
//                    TransactionTemplate template = new TransactionTemplate(mTransactionManager);
//                    template.execute(status ->{
//                        mConsignHeaderDbService.saveBatch(consignHeaderEntityList);
//                        mConsignDbService.saveBatch(consignEntityList);
//                        return true;
//                    });
//                });TransactionTemplate template = new TransactionTemplate(mTransactionManager);template.execute(status ->{mConsignHeaderDbService.saveBatch(consignHeaderEntityList);mConsignDbService.saveBatch(consignEntityList);return true;});log.info("本轮销售单已提交导入");}startDate = startDate.plusDays(1l);}}
}

四、测试代码

4.1 测试接口

public interface IConsignService {void initTestData();Page<ConsignMapperOut> pageGroupByItemId(ConsignPageRequest pConsignPageRequest);
}

impl文件相应函数

    @Overridepublic Page<ConsignMapperOut> pageGroupByItemId(ConsignPageRequest pConsignPageRequest) {Page<ConsignMapperOut> pageCfg = new Page<>(pConsignPageRequest.getCurrent(),pConsignPageRequest.getSize());ConsignMapperIn consignMapperIn = pConsignPageRequest.toMapperIn();long curTime = System.currentTimeMillis();Page<ConsignMapperOut> consignMapperOutPage = mConsignMapper.page(pageCfg, consignMapperIn);long endTime = System.currentTimeMillis();log.info("cost time "+(endTime - curTime)/1000.0 + "s");return consignMapperOutPage;}

4.2 request类

request类

@Data
public class ConsignPageRequest extends BasePageRequest {List<Long> itemIdList;List<Long> customerIdList;List<Long> salesmanIdList;Long enterpriseId;LocalDate billTimeBeg;LocalDate billTimeEnd;EHeaderJoinMode headerJoin;EItemJoinMode itemJoin;EOrderByMode orderBy;EBillTimeMode billTimeMode;public ConsignMapperIn toMapperIn(){ConsignMapperIn consignMapperIn = new ConsignMapperIn();consignMapperIn.setItemIdList(itemIdList);consignMapperIn.setCustomerIdList(customerIdList);consignMapperIn.setSalesmanIdList(salesmanIdList);consignMapperIn.setEnterpriseId(enterpriseId);LocalDateTime billTimeBegT = billTimeBeg.atTime(0,0,0);LocalDateTime billTimeEndT = billTimeEnd.atTime(23,59,59);consignMapperIn.setBillTimeBeg(billTimeBegT);consignMapperIn.setBillTimeEnd(billTimeEndT);consignMapperIn.setBillTimeKeyBeg(CommonUtil.LocalDateTimeToSecond(billTimeBegT));consignMapperIn.setBillTimeKeyEnd(CommonUtil.LocalDateTimeToSecond(billTimeEndT));consignMapperIn.setBillTimeStrBeg(LocalDateTimeUtil.formatNormal(billTimeBegT));consignMapperIn.setBillTimeStrEnd(LocalDateTimeUtil.formatNormal(billTimeEndT));consignMapperIn.setGroup(Arrays.asList("item_id"));consignMapperIn.setHeaderJoin(headerJoin);consignMapperIn.setItemJoin(itemJoin);consignMapperIn.setOrderBy(orderBy);consignMapperIn.setBillTimeMode(billTimeMode);return consignMapperIn;}
}

mapper request类

@Data
public class ConsignMapperIn {List<Long> itemIdList;List<Long> customerIdList;List<Long> salesmanIdList;Long enterpriseId;List<String> group;LocalDateTime billTimeBeg;LocalDateTime billTimeEnd;String billTimeStrBeg;String billTimeStrEnd;Long billTimeKeyBeg;Long billTimeKeyEnd;EHeaderJoinMode headerJoin;EItemJoinMode itemJoin;EOrderByMode orderBy;EBillTimeMode billTimeMode;
}

4.3 一些枚举

我们把一些查询模式封装到枚举里,方便做测试

@RequiredArgsConstructor
@EnumDesc
public enum EBillTimeMode {BILL_TIME_KEY(1,"bill_time_key","使用时间换算的秒,bill_time_key作为查询键","bill_time_key"),BILL_TIME(2,"bill_time","使用datetime bill_time作为查询键","bill_time"),BILL_TIME_STR(3,"bill_time_str","使用字符串bill_time_str做查询键","bill_time_str");@EnumValue@Getterprivate final int code;@Getterprivate final String name;@Getterprivate final String desc;@Getterprivate final String col;
}
@RequiredArgsConstructor
@EnumDesc
public enum EHeaderJoinMode {NONE(0,"不连表","不连表,直接使用consign表做查询",null,null),ID_JOIN(1,"id关联","consign_header的id与consign的header_id做关联","id","header_id"),BILL_NO_JOIN(2,"订单号关联","header表的enp_id和bill_no与consin相应字段关联","bill_no","bill_no");@EnumValue@Getterprivate final int code;@Getterprivate final String name;@Getterprivate final String desc;@Getterprivate final String headerCol;@Getterprivate final String consignCol;
}
@RequiredArgsConstructor
@EnumDesc
public enum EItemJoinMode {NONE(0,"不连接","不连接item表",null,null),ID_JOIN(1,"id连接","使用id链接item","item_id","id"),STR_ID_JOIN(2,"字符串id连接","使用字符串id做连接","item_str_id","id"),REL_ID_JOIN(3,"关联id连接","不但使用字符串id做连接,item表也不用主键","item_str_id","rel_id");@EnumValue@Getterprivate final int code;@Getterprivate final String name;@Getterprivate final String desc;@Getterprivate final String consignCol;@Getterprivate final String itemCol;
}
@RequiredArgsConstructor
@EnumDesc
public enum EOrderByMode {PROFIT(1,"利润","按照利润排序","total_profit"),AMOUNT(2,"总金额","按总金额排序","total_amount"),CNT(3,"总数量","按总数量排序","total_cnt");@EnumValue@Getterprivate final int code;@Getterprivate final String name;@Getterprivate final String desc;@Getterprivate final String col;
}

4.4 xml的sql

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="indi.zhifa.study2024.consigntest.logic.busy.consign.mapper.ConsignMapper"><sql id="select">SELECT c.item_id,c.item_name,SUM(c.item_cnt) AS total_cnt, SUM(c.price * c.item_cnt) AS total_amount,<if test="IN.itemJoin.name() != 'NONE'">SUM((c.price - i.cost) * c.item_cnt) AS total_profit</if>FROM<choose><when test="IN.headerJoin.name() == 'ID_JOIN'">consign_header h JOIN consign c ON h.${IN.headerJoin.headerCol} = c.${IN.headerJoin.consignCol}</when><when test="IN.headerJoin.name() == 'BILL_NO_JOIN'">consign_header h JOIN consign c ON h.${IN.headerJoin.headerCol} = c.${IN.headerJoin.consignCol} AND h.enp_id = c.enp_id</when><otherwise>consign c</otherwise></choose><choose><when test="IN.itemJoin.name() == 'ID_JOIN'">JOIN item i ON c.${IN.itemJoin.consignCol} = i.${IN.itemJoin.itemCol}</when><when test="IN.itemJoin.name() == 'STR_ID_JOIN'">JOIN item i ON c.${IN.itemJoin.consignCol} = i.${IN.itemJoin.itemCol}</when><when test="IN.itemJoin.name() == 'REL_ID_JOIN'">JOIN item i ON c.${IN.itemJoin.consignCol} = i.${IN.itemJoin.itemCol} AND c.enp_id = i.enp_id</when></choose><where><if test="IN.itemIdList != null and IN.itemIdList.size > 0"><choose><when test="IN.itemIdList.size == 1">item_id = #{IN.itemIdList[0]}</when><otherwise>AND c.item_id IN<foreach collection="IN.itemIdList" item="item" open="(" separator="," close=")">#{item}</foreach></otherwise></choose></if><if test="IN.customerIdList != null and IN.customerIdList.size > 0">AND<choose><when test="IN.customerIdList.size == 1"><choose><when test="IN.headerJoin.name() == 'NONE'">c.customer_id</when><otherwise>h.customer_id</otherwise></choose>= #{IN.customerIdList[0]}</when><otherwise><choose><when test="IN.headerJoin.name() == 'NONE'">c.customer_id</when><otherwise>h.customer_id</otherwise></choose>IN<foreach collection="IN.customerIdList" item="item" open="(" separator="," close=")">#{item}</foreach></otherwise></choose></if><if test="IN.salesmanIdList != null and IN.salesmanIdList.size > 0"><choose><when test="IN.salesmanIdList.size == 1"><choose><when test="IN.headerJoin.name() ==  'NONE'">c.sales_id</when><otherwise>h.sales_id</otherwise></choose>= #{IN.salesmanIdList[0]}</when><otherwise>AND<choose><when test="IN.headerJoin.name() == 'NONE'">c.sales_id</when><otherwise>h.sales_id</otherwise></choose>IN<foreach collection="IN.customerIdList" item="item" open="(" separator="," close=")">#{item}</foreach></otherwise></choose></if><if test="IN.enterpriseId != null">AND<choose><when test="IN.headerJoin.name() != 'NONE'">h.enp_id = #{IN.enterpriseId}</when><otherwise>c.enp_id = #{IN.enterpriseId}</otherwise></choose></if><if test="IN.billTimeBeg != null and IN.billTimeEnd != null">AND<choose><when test="IN.headerJoin.name() != 'NONE'">h.${IN.billTimeMode.col} BETWEEN </when><otherwise>c.${IN.billTimeMode.col} BETWEEN </otherwise></choose><choose><when test="IN.billTimeMode.name() == 'BILL_TIME_KEY'">#{IN.billTimeKeyBeg} AND #{IN.billTimeKeyEnd}</when><when test="IN.billTimeMode.name() == 'BILL_TIME'">#{IN.billTimeBeg} AND #{IN.billTimeEnd}</when><when test="IN.billTimeMode.name() == 'BILL_TIME_STR'">#{IN.billTimeStrBeg} AND #{IN.billTimeStrEnd}</when></choose></if></where><if test="IN.group != null and IN.group.size > 0">GROUP BY<foreach collection="IN.group" item="item" open=" " separator="," close=" ">${item}</foreach></if>ORDER BY ${IN.orderBy.col}</sql><select id="page"resultType="indi.zhifa.study2024.consigntest.logic.busy.consign.entity.mapperOut.ConsignMapperOut"><include refid="select"></include></select><select id="list"resultType="indi.zhifa.study2024.consigntest.logic.busy.consign.entity.mapperOut.ConsignMapperOut"><include refid="select"></include></select>
</mapper>

五、实验

我们开始逐步实验,首先,我们检验一下之前生成的数据概况:

5.1、实验数据概况

如果用workbench,记得把最大查询时间改长
Edit->Preferences->SQL Editor
在这里插入图片描述

-- 10
select count(*) from enterprise
-- 126
select count(*) from sales_man
-- 377
select count(*) from customer
-- 58218
select count(*) from item
-- 1829939
select count(*) from consign_header
-- 80,505,624,查询时间40.9s
select count(*) from consignselect h.enp_id,h.enp_code,count(*) AS cnt from consign_header h group by h.enp_id
enp_idenp_codecnt
1869035491194941442enp_001248814
1869035491194941443enp_002263780
1869035491194941444enp_003120522
1869035491194941445enp_00484262
1869035491194941446enp_005174673
1869035491194941447enp_006342751
1869035491194941448enp_00752964
1869035491194941449enp_008172159
1869035491194941450enp_009181632
1869035491194941451enp_010188382

机器配置:
操作系统是ubuntu

lscpu
# 结果是4核,4500MHZ
free -h
# 内存32G

5.2、实验条目

case1 预期最慢的查询

我们和item连表,不使用主键,和header连表,也不使用主键,时间查询使用字符串
请求json

{"current": 2,"size": 10,"enterpriseId": 1869035491194941447,"billTimeBeg": "2024-04-01","billTimeEnd": "2024-07-31","headerJoin": "BILL_NO_JOIN","itemJoin": "REL_ID_JOIN","orderBy": "PROFIT","billTimeMode": "bill_time_key"
}

生成sql:

explain SELECT c.item_id,c.item_name,
SUM(c.item_cnt) AS total_cnt, 
SUM(c.price * c.item_cnt) AS total_amount, 
SUM((c.price - i.cost) * c.item_cnt) AS total_profit 
FROM consign_header h JOIN consign c 
ON h.bill_no = c.bill_no AND h.enp_id = c.enp_id 
JOIN item i ON c.item_str_id = i.rel_id AND c.enp_id = i.enp_id 
WHERE h.enp_id = 1869035491194941447 
AND h.bill_time_str BETWEEN '2024-04-01 00:00:00' AND '2024-07-31 23:59:59'
GROUP BY item_id 
ORDER BY total_profit LIMIT 10,10;

explain结果

select_typetabletypekeyken_kenrowsfiltered
simplehrangidx_time1611516100
simplecrefidx_enp_id_bill_no8844100
simpleirefidx_rel_id941100

执行时间

enp_idenp_codecnttime
1869035491194941442enp_00124881412.093s
1869035491194941443enp_00226378013.085s
1869035491194941444enp_0031205226.214s
1869035491194941445enp_004842624.952s
1869035491194941446enp_00517467316.676s
1869035491194941447enp_00634275118.114s
1869035491194941448enp_007529643.416s
1869035491194941449enp_00817215916.44s
1869035491194941450enp_00918163218.856s
1869035491194941451enp_01018838219.845s

case2 使用datetime时间

请求json

{"current": 2,"size": 10,"enterpriseId": 1869035491194941447,"billTimeBeg": "2024-04-01","billTimeEnd": "2024-07-31","headerJoin": "BILL_NO_JOIN","itemJoin": "REL_ID_JOIN","orderBy": "PROFIT","billTimeMode": "BILL_TIME"
}

生成sql:

SELECT c.item_id,c.item_name,
SUM(c.item_cnt) AS total_cnt, 
SUM(c.price * c.item_cnt) AS total_amount, 
SUM((c.price - i.cost) * c.item_cnt) AS total_profit 
FROM consign_header h JOIN consign c 
ON h.bill_no = c.bill_no AND h.enp_id = c.enp_id 
JOIN item i ON c.item_str_id = i.rel_id AND c.enp_id = i.enp_id 
WHERE h.enp_id = 1869035491194941447
AND h.bill_time BETWEEN '2024-04-01T00:00:00' 
AND '2024-07-31T23:59:59' 
GROUP BY item_id 
ORDER BY total_profit LIMIT 10,10;

explain结果

select_typetabletypekeyken_kenrowsfiltered
simplehrangidx_time1334610100
simplecrefidx_enp_id_bill_no8844100
simpleirefidx_rel_id941100

执行时间

enp_idenp_codecnttime
1869035491194941442enp_00124881414.381s
1869035491194941443enp_00226378013.346s
1869035491194941444enp_00312052210.169s
1869035491194941445enp_004842624.952s
1869035491194941446enp_0051746735.113s
1869035491194941447enp_00634275117.871s
1869035491194941448enp_007529643.318s
1869035491194941449enp_00817215916.636s
1869035491194941450enp_00918163218.813s
1869035491194941451enp_01018838219.714s

case3 使用datetime-key时间

请求json

{"current": 2,"size": 10,"enterpriseId": 1869035491194941447,"billTimeBeg": "2024-04-01","billTimeEnd": "2024-07-31","headerJoin": "BILL_NO_JOIN","itemJoin": "REL_ID_JOIN","orderBy": "PROFIT","billTimeMode": "BILL_TIME_KEY"
}

生成sql:

SELECT c.item_id,c.item_name,
SUM(c.item_cnt) AS total_cnt, 
SUM(c.price * c.item_cnt) AS total_amount, 
SUM((c.price - i.cost) * c.item_cnt) AS total_profit 
FROM consign_header h JOIN consign c 
ON h.bill_no = c.bill_no AND h.enp_id = c.enp_id 
JOIN item i ON c.item_str_id = i.rel_id AND c.enp_id = i.enp_id 
WHERE h.enp_id = 1869035491194941447
AND h.bill_time_key BETWEEN 1711900800 AND 1722441599
GROUP BY item_id 
ORDER BY total_profit LIMIT 10,10;

explain结果

select_typetabletypekeyken_kenrowsfiltered
simplehrangidx_time1630042100
simplecrefidx_enp_id_bill_no8844100
simpleirefidx_rel_id941100

执行时间

enp_idenp_codecnttime
1869035491194941442enp_00124881414.119s
1869035491194941443enp_00226378011.165s
1869035491194941444enp_0031205228.712s
1869035491194941445enp_004842624.852s
1869035491194941446enp_00517467316.83s
1869035491194941447enp_00634275118.21s
1869035491194941448enp_007529643.252s
1869035491194941449enp_00817215916.891s
1869035491194941450enp_00918163219.029s
1869035491194941451enp_01018838219.947s

case4 item连表使用STR_ID_JOIN

请求json

{"current": 2,"size": 10,"enterpriseId": 1869035491194941447,"billTimeBeg": "2024-04-01","billTimeEnd": "2024-07-31","headerJoin": "BILL_NO_JOIN","itemJoin": "STR_ID_JOIN","orderBy": "PROFIT","billTimeMode": "BILL_TIME_KEY"
}

生成sql

SELECT c.item_id,c.item_name,SUM(c.item_cnt) AS total_cnt, 
SUM(c.price * c.item_cnt) AS total_amount, 
SUM((c.price - i.cost) * c.item_cnt) AS total_profit 
FROM consign_header h 
JOIN consign c ON h.bill_no = c.bill_no AND h.enp_id = c.enp_id 
JOIN item i ON c.item_str_id = i.id
WHERE h.enp_id = 1869035491194941447
AND h.bill_time_key BETWEEN 1711900800 AND 1722441599 
GROUP BY item_id ORDER BY total_profit LIMIT 10,10;

explain结果

select_typetabletypekeyken_kenrowsfiltered
simplehrangidx_time_key1630042100
simplecrefidx_enp_id_bill_no8844100
simpleirefPRIMARY81100

执行时间

enp_idenp_codecnttime
1869035491194941442enp_0012488149.956s
1869035491194941443enp_00226378010.732s
1869035491194941444enp_0031205228.401s
1869035491194941445enp_004842624.028s
1869035491194941446enp_00517467313.223s
1869035491194941447enp_006408314.802s
1869035491194941448enp_007529642.838s
1869035491194941449enp_00817215913.314s
1869035491194941450enp_00918163215.115s
1869035491194941451enp_01018838215.298s

有了明显的时间优化,大概20%

case5 item连表使用ID_JOIN

请求json

{"current": 2,"size": 10,"enterpriseId": 1869035491194941447,"billTimeBeg": "2024-04-01","billTimeEnd": "2024-07-31","headerJoin": "BILL_NO_JOIN","itemJoin": "ID_JOIN","orderBy": "PROFIT","billTimeMode": "BILL_TIME_KEY"
}

这个问题,在mysql8中被优化了,测不出来问题,这里就不详述了

case6 header使用id连表

请求json

{"current": 2,"size": 10,"enterpriseId": 1869035491194941447,"billTimeBeg": "2024-04-01","billTimeEnd": "2024-07-31","headerJoin": "ID_JOIN","itemJoin": "ID_JOIN","orderBy": "PROFIT","billTimeMode": "BILL_TIME_KEY"
}

生成sql

SELECT c.item_id,c.item_name,SUM(c.item_cnt) AS total_cnt, 
SUM(c.price * c.item_cnt) AS total_amount, 
SUM((c.price - i.cost) * c.item_cnt) AS total_profit 
FROM consign_header h 
JOIN consign c ON h.id = c.header_id
JOIN item i ON c.item_id = i.id
WHERE h.enp_id = 1869035491194941447 
AND h.bill_time_key BETWEEN 1711900800 AND 1722441599 
GROUP BY item_id ORDER BY total_profit LIMIT 10,10;

explain结果

select_typetabletypekeyken_kenrowsfiltered
simplehrangidx_time_key1630042100
simplecrefidx_header_id846100
simpleirefPRIMARY81100

执行时间

enp_idenp_codecnttime
1869035491194941442enp_00124881419.311s
1869035491194941443enp_00226378018.534s
1869035491194941444enp_00312052213.849s
1869035491194941445enp_004842625.782s
1869035491194941446enp_00517467321.158s
1869035491194941447enp_00634275120.927s
1869035491194941448enp_007529643.087s
1869035491194941449enp_00817215919.982s
1869035491194941450enp_00918163223.256s
1869035491194941451enp_01018838226.057s

结论:
反倒时间比之前更长

case7 不连表header

请求json

{"current": 2,"size": 10,"enterpriseId": 1869035491194941447,"billTimeBeg": "2024-04-01","billTimeEnd": "2024-07-31","headerJoin": "NONE","itemJoin": "ID_JOIN","orderBy": "PROFIT","billTimeMode": "BILL_TIME_KEY"
}

生成sql

SELECT c.item_id,c.item_name,
SUM(c.item_cnt) AS total_cnt, 
SUM(c.price * c.item_cnt) AS total_amount, 
SUM((c.price - i.cost) * c.item_cnt) AS total_profit 
FROM consign c JOIN item i ON c.item_id = i.id 
WHERE c.enp_id = 1869035491194941447 
AND c.bill_time_key BETWEEN 1711900800 AND 1722441599
GROUP BY item_id ORDER BY total_profit LIMIT 10,10;

explain结果

select_typetabletypekeyken_kenrowsfiltered
simplecrangeidx_bill_date_key161127926100
simpleirefPRIMARY81100

执行时间

enp_idenp_codecnttime
1869035491194941442enp_00124881424.593s
1869035491194941443enp_00226378022.829s
1869035491194941444enp_00312052218.002s
1869035491194941445enp_004842627.154s
1869035491194941446enp_00517467326.363s
1869035491194941447enp_00634275128.986s
1869035491194941448enp_007529643.813s
1869035491194941449enp_00817215925.311s
1869035491194941450enp_00918163229.558s
1869035491194941451enp_01018838231.191s

结论:
反倒时间更长了

5.3、实验结论

case5,大概是效率最高的方式。

  • 直接使用id做表关联,不见得会快。
  • 时间key使用字符串甚至比dateTime更快,和转化为秒的key持平。

由于篇幅限制,进一步的测试在下一节再写。
或许,在制定主键策略时,可能用联合主键效果更好。

六、代码展示

还是老地方,请大家移步码云

相关文章:

芝法酱学习笔记(2.1)——sql性能优化1

一、前言 做程序员这行&#xff0c;相信大家都会接触到性能优化这个概念。并且多数程序员&#xff0c;为自己能参与到性能优化的工作而感到自豪。 在普通应用系统的业务开发中&#xff0c;程序的性能瓶颈往往在数据库查询的IO上&#xff0c;所以优化数据库查询则是重中之重的工…...

【Mysql】Java的JDBC编程

本节目标 数据库驱动 JDBC的概念及作用 掌握JDBC的工作原理 掌握JDBC中几个常用接口和类 掌握基于数据库的应用程序开发流程 一. 数据库编程的必备条件 编程语言&#xff0c;如Java&#xff0c;C、C、Python等 数据库&#xff0c;如Oracle&#xff0c;MySQL&#xff0c;S…...

PHPUnit使用指南:编写高效的单元测试

PHPUnit使用指南&#xff1a;编写高效的单元测试 单元测试是软件开发中不可或缺的一部分&#xff0c;它能够帮助开发者确保代码的正确性和稳定性。PHPUnit是PHP中最流行的单元测试框架之一&#xff0c;提供了一套强大的工具和功能来编写和运行测试。本文将详细介绍如何使用PHP…...

二、HTML5

一、HTML5简介 1、什么是HTML5 HTML5 是新一代的 HTML 标准&#xff0c;2014年10月由万维网联盟&#xff08;W3C&#xff09;完成标准制定。 官网地址&#xff1a; W3C 提供&#xff1a; https://www.w3.org/TR/html/index.html WHATW…...

授权模型MAC

MAC&#xff08;Mandatory Access Control&#xff09;是一种授权模型&#xff0c;用于实现对系统资源访问的强制控制。在MAC模型中&#xff0c;授权是基于预先定义的安全策略&#xff0c;且该策略由系统管理员来配置和管理。 在MAC模型中&#xff0c;每个用户和每个资源都被赋…...

GaussDB数据库迁移方案介绍

云数据库GaussDB提供了多种数据迁移方案&#xff0c;可满足从MySQL数据库、Oracle数据库、GaussDB数据库、PostgreSQL数据库、DB2 for LUW、RDS for SQL Server、Microsoft SQL Server数据库到云数据库GaussDB的迁移。 数据迁移工具有DRS、DAS和gs_loader。推荐使用DRS&#x…...

web3跨链桥协议-Nomad

项目介绍 Nomad是一个乐观跨链互操作协议。通过Nomad协议&#xff0c;Dapp能够在不同区块链间发送数据&#xff08;包括rollups&#xff09;&#xff0c;Dapp通过Nomad的合约和链下的代理对跨链数据、消息进行验证、传输。其安全通过乐观验证机制和欺诈证明制约验证者实现&…...

白话java设计模式

创建模式 单例模式&#xff08;Singleton Pattern&#xff09;&#xff1a; 就是一次创建多次使用&#xff0c;它的对象不会重复创建&#xff0c;可以全局来共享状态。 工厂模式&#xff08;Factory Method Pattern&#xff09;&#xff1a; 可以通过接口来进行实例化创建&a…...

代码的注释

代码注释是程序开发中至关重要的一部分&#xff0c;良好的注释能够大大提升代码的可读性、可维护性和团队协作效率。注释帮助开发人员理解代码的逻辑、目的和背后的设计思想&#xff0c;尤其是在面对复杂的业务逻辑或算法时&#xff0c;注释可以帮助未来的开发人员快速理解并有…...

Java中的Consumer接口应该如何使用(通俗易懂图解)

应用场景&#xff1a; 第一次程序员A写好了个基础的遍历方法&#xff1a; public class Demo1 {public static void main(String[] args) {//假设main方法为程序员B写的,此时需要去调用A写好的一个遍历方法//1.如果此时B突然发现想将字符串以小写的形式打印出来&#xff0c;则…...

数据库设计的基础与进阶:1NF、2NF、3NF及BCNF解析

目录 什么是数据库范式&#xff1f; 1. 第一范式&#xff08;1NF&#xff09; 2. 第二范式&#xff08;2NF&#xff09; 3. 第三范式&#xff08;3NF&#xff09; 4. 博茨-科德范式&#xff08;BCNF&#xff09; 总结 在数据库设计中&#xff0c;范式是为了确保数据存储结…...

ARM Cortex-A7 MPCore 架构

1、Cortex-A7 MPCore 简介 Cortex-A7 MPcore 处理器支持 1~4 核,通常是和 Cortex-A15 组成 big.LITTLE 架构的, Cortex-A15 作为大核负责高性能运算,比如玩游戏啥的, Cortex-A7 负责普通应用,因为 CortexA7 省电。 Cortex-A7 本身性能也不弱,不要看它叫做 Cortex-A7 但是…...

【操作系统】数据集合集!

本文将为您介绍经典、热门的数据集&#xff0c;希望对您在选择适合的数据集时有所帮助。 1 HarmonyOS 更新时间&#xff1a;2024-07-20 访问地址: GitHub 描述&#xff1a; 是首个基于微内核的全场景分布式操作系统&#xff0c;是华为自主研发的操作系统&#xff0c;华为将率…...

原生js图片预览

下面的图片预览是从一个JSON文件中加载图片列表&#xff0c;并且支持点击缩略图预览大图&#xff0c;还可以使用鼠标滚轮进行图片缩放。接下来了给大家把html、css、js一个一个的讲解一下 首先是html <div class"container"></div><div id"imag…...

【系统】Mac crontab 无法退出编辑模式问题

【系统】Mac crontab 无法退出编辑模式问题 背景一、问题回答1.定位原因&#xff1a;2.确认编辑器类型3.确保编辑器进入正确3.1 确认是否有crontab调度任务3.2 进入编辑器并确保编辑器正常3.3 保存操作 4.确认crontab任务存在5.确保脚本的可执行性和正确性 二、后续 背景 之前…...

【进程篇】04.进程的状态与优先级

一、进程的状态 1.1 进程的状态 1.1.1 并行与并发 • 并行: 多个进程在多个CPU下分别&#xff0c;同时进行运行 • 并发: 多个进程在一个CPU下采用进程切换的方式&#xff0c;在一个时间片内&#xff0c;让多个进程都得以推进 1.1.2 时间片的概念 LInux/windows这些民用级别…...

linux下蓝牙调试工具hcitool的使用

hcitool 是一个用于蓝牙设备管理的命令行工具&#xff0c;主要用于查看和管理蓝牙设备。以下是一些常见的用法和示例&#xff1a; 1. 查看本地蓝牙适配器信息 使用 hcitool dev 命令可以查看本地蓝牙适配器的信息。 hcitool dev示例输出&#xff1a; Devices:hci0 00:11:22…...

【RAG实战】Prompting vs. RAG vs. Finetuning: 如何选择LLM应用选择最佳方案

在构建基于大型语言模型&#xff08;LLM&#xff09;的应用时&#xff0c;通常不可能立即使用模型而无需任何调整。为了保持高实用性&#xff0c;我们可以选择以下几种方法之一&#xff1a; Prompt Engineering&#xff08;提示工程&#xff09;Fine-tuning&#xff08;微调&a…...

EasyExcel 动态设置表格的背景颜色和排列

项目中使用EasyExcel把数据以excel格式导出&#xff0c;其中设置某一行、某一列单元格的背景颜色、排列方式十分常用&#xff0c;记录下来方便以后查阅。 1. 导入maven依赖&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>easy…...

python俄罗斯方块.py

俄罗斯方块.py import pygame import random# 初始化游戏 pygame.init()# 设置游戏窗口的大小 screen_width 800 screen_height 600 play_width 300 play_height 600 block_size 30top_left_x (screen_width - play_width) // 2 top_left_y screen_height - play_heigh…...

IP协议详解

目录 一. IP协议概述 1. 概念 2. 特点 (1) 无连接性 (2) 不可靠传输 (3) 数据包分片和重组 二. IP协议报文格式 1. 版本 (4位) 2. 首部长度 (4位) 3. 服务类型 (8位) 4. 总长度 (16位) 5. 标识, 标志位, 片偏移 6. 生存时间 (8位) 7. 协议 (8位) 8. 首部检验和 (1…...

青少年编程与数学 02-004 Go语言Web编程 02课题、依赖管理

青少年编程与数学 02-004 Go语言Web编程 02课题、依赖管理 课题摘要:一、项目结构各目录说明&#xff1a; 二、依赖项三、依赖管理任务四、依赖管理步骤1. 初始化Go Modules项目2. 添加依赖3. 指定依赖版本4. 更新依赖5. 清理未使用的依赖6. 离线工作7. 模块隔离8. 可重现构建 …...

代码生成器

源码 表结构 代码的目录结构 后端代码 前端代码 查询数据库的表 前端 后端 只查询当前数据库的表去除掉定时任务和生成器的表格去除掉已经导入的表格<select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult"…...

MySQL 性能调优:打造高效数据库

SQL 语句层面的性能调优策略 合理选择字段属性 在创建 MySQL 表时&#xff0c;为了获取更好的性能&#xff0c;选择合适的字段属性至关重要。 首先&#xff0c;要依据实际情况合理设置字段的类型及宽度。例如&#xff0c;对于像手机号码这类固定长度为 11 位的字段&#xff…...

Elasticsearch 实战应用:提升数据洞察与交互体验

随着数据量的不断增长和数据处理需求的日益复杂&#xff0c;Elasticsearch 在实战应用中的价值愈发凸显。在本次教学中&#xff0c;我们继续深入探索 Elasticsearch 的更多高级实战应用&#xff0c;致力于培养学生在数据洞察和用户交互方面的卓越能力。 一、数据建模与优化策略…...

Ubuntu 配置静态 IP 地址

在 Ubuntu 系统中配置静态 IP 地址&#xff0c;适用于服务器或虚拟机需要固定 IP 的场景。以下是详细的步骤说明。 1. 确认网络接口信息 在配置之前&#xff0c;先确认系统的网络接口名称和当前的网络配置。 1.1 查看网络接口 运行以下命令获取网络接口的名称&#xff08;如…...

2025美赛数学建模十大常用算法之层次分析法AHP详解

层次分析法&#xff08;Analytic Hierarchy Process, AHP&#xff09;详解 一、层次分析法简介 层次分析法&#xff08;AHP&#xff09;是一种系统化的多准则决策方法&#xff0c;由美国运筹学家萨蒂&#xff08;Thomas L. Saaty&#xff09;于20世纪70年代提出。AHP通过构建层…...

鸿蒙学习笔记:用户登录界面

文章目录 1. 提出任务2. 完成任务2.1 创建鸿蒙项目2.2 准备图片资源2.3 编写首页代码2.4 启动应用 3. 实战小结 1. 提出任务 本次任务聚焦于运用 ArkUI 打造用户登录界面。需呈现特定元素&#xff1a;一张图片增添视觉感&#xff0c;两个分别用于账号与密码的文本输入框&#…...

电脑上怎么运行手机APP(电脑上运行手机APP的4种方法)

玩家可能不愿意在小屏幕上使用 Android 应用程序。此外&#xff0c;某些游戏玩家可能更喜欢在更大的屏幕上寻找刺激的体验&#xff0c;例如 PC 提供的体验&#xff0c;这可以增强他们的乐趣。因此&#xff0c;他们可能对如何在 PC 上流畅地运行 Android 应用程序感到好奇。本指…...

Java原生实现代码沙箱的实现

代码沙箱实现 代码沙箱&#xff1a;只负责接收代码和输入&#xff0c;返回编译运行的结果&#xff0c;不负责判题&#xff08;可作为独立项目/服务&#xff0c;提供给其他需要执行代码的项目使用&#xff09; 以Java语言为主&#xff0c;实现代码沙箱。主要学习其思想、关键流…...

Docker基础命令实战

问题&#xff1a;Error response from daemon: Get "https://index.docker.io/v1/search?qmysql&n25": dial tcp 199.59.148.9:443: connect: connection refused 在pull nginx时出现的报错&#xff0c;可能原因是镜像源过期了&#xff0c;我们需要更换镜像源 …...

【QT实战の心情笔记】

文章目录 界面布局主要界面分为三部分&#xff1a;1. 笔记列表区域2. 笔记内容编辑区域3. 操作按钮区域 Qt Designer 界面设计步骤完整界面布局图各控件设置和属性Qt Designer 文件 (.ui) 数据库表结构SQL 表结构&#xff1a; 逻辑代码1. 项目结构2. Note 类 (Note.h 和 Note.c…...

关于Unity VFX 在Spawn状态的一些笔记

一. periodic burst 和 single burst 的区别 1. Single Burst 定义:Single Burst 是一次性发射粒子,只在粒子系统启动时触发一次。 它是一个瞬时的发射行为,适合单次效果。 特性: 只触发一次。发射时间通常是粒子系统启动时。不会重复发射,除非重新触发粒子系统。适用场景…...

AutoMQ 流表一体新特性 Table Topic 发布: 无缝集成 AWS S3 Table 和 Iceberg

超越共享存储&#xff1a;使用 Apache Iceberg 中的 AutoMQ Table Topic 实现流处理与分析的统一 自 2023 年底官宣以来&#xff0c;AutoMQ 成功地将 Apache Kafka 从“Shared Nothing architecture”转变为“Shared Storage architecture”&#xff0c;这为京东、知乎、小红书…...

springboot445新冠物资管理(论文+源码)_kaic

摘 要 使用旧方法对新冠物资管理的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在新冠物资管理的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发的新冠物资管…...

【AIGC】LangChain 环境搭建及模型API能力集成使用详解

目录 一、前言 二、LangChain 概述 2.1 LangChain 是什么 2.2 LangChain 主要特点 2.3 LangChain能做什么和能力一览 2.4 LangChain 主要应用场景 三、环境准备 3.1 python 开发环境 3.1.1 python环境 3.1.2 配置vscode环境 3.1.3 安装LangChain相关插件包 3.2 获取…...

YOLOv8目标检测(六)_封装API接口

YOLOv8目标检测(一)_检测流程梳理&#xff1a;YOLOv8目标检测(一)_检测流程梳理_yolo检测流程-CSDN博客 YOLOv8目标检测(二)_准备数据集&#xff1a;YOLOv8目标检测(二)_准备数据集_yolov8 数据集准备-CSDN博客 YOLOv8目标检测(三)_训练模型&#xff1a;YOLOv8目标检测(三)_训…...

Firecrawl教程①:自动化抓取与数据转化,赋能AI应用

Firecrawl教程①:自动化抓取与数据转化,赋能AI应用 前言一、功能特点1. 支持 LLM 可处理的数据格式2. 全面抓取网站3. 强大的操作支持4. 灵活的定制选项5. 支持多种编程语言 SDK二、如何开始使用 Firecrawl第一步:获取 API 密钥第二步:官网在线工具使用第三步:安装 Firecr…...

2-2-18-17 QNX系统架构之“实时”

阅读前言 本文以QNX系统官方的文档英文原版资料为参考&#xff0c;翻译和逐句校对后&#xff0c;对QNX操作系统的相关概念进行了深度整理&#xff0c;旨在帮助想要了解QNX的读者及开发者可以快速阅读&#xff0c;而不必查看晦涩难懂的英文原文&#xff0c;这些文章将会作为一个…...

Linux下部署MySQL8.0集群 - 主从复制(一主两从)

目录 一、部署前准备 1、查看系统信息 # 查看系统版本 cat /etc/red* # 查看系统位数 getconf LONG_BIT[rootlocalhost ~]# cat /etc/red* CentOS Linux release 7.5.1804 (Core) [rootlocalhost ~]# getconf LONG_BIT 642、下载对应安装包 进入MySQL官网&#xff1a;https:…...

将HTML转换为PDF:使用Spire.Doc的详细指南

目录 引言 1. 为什么选择 Spire.Doc&#xff1f; 1.1 主要特点 1.2 适用场景 2. 准备工作 2.1 引入 Spire.Doc 依赖 2.2 禁用 SSL 证书验证 3. 实现功能 3.1 主类结构 3.2 代码解析 4. 处理图像 5. 性能优化 5.1 异步下载图像 示例代码 5.2 批量处理优化 示例代…...

HarmonyOS 实践 - 设计模式在代码中的作用

文章目录 前言设计模式概述单例模式&#xff1a;全局状态管理代码分析 策略模式&#xff1a;界面主题切换代码分析 示例测试单例模式测试策略模式测试 体验评价总结 前言 在软件开发中&#xff0c;设计模式是公认的最佳实践&#xff0c;它能帮助开发者通过模块化和规范化的代码…...

kubeadm一键部署K8S 集群架构

kubeadm一键部署K8S 集群架构(centos7) https://www.k8src.cn/ https://kubernetes.io/zh-cn/docs/home/ https://blog.csdn.net/m0_58709145/article/details/140128179 https://blog.csdn.net/jiaqijiaqi666/article/details/129745828 Kubeadm init报错[ERROR CRI]: contai…...

[NSSCTF 2022 Spring Recruit]factor

给了&#xff1a; 说n是p*q 先求一下p*q factordb.com 然后再让pq 得到&#xff1a;31207540994438423298 接着用md5加密得到falg NSSCTF{7957a92ef959998115a6bda6d992656e}...

go 中使用redis 基础用法

1、安装redis 参考链接&#xff1a;https://www.codeleading.com/article/98554130215/ 1.1 查看是否有redis yum 源 yum install redis没有可用的软件包&#xff0c;执行1.2 1.2下载fedora的epel仓库 yum install epel-release --下载fedora的epel仓库1.3启动redis s…...

git branch -r(--remotes )显示你本地仓库知道的所有 远程分支 的列表

好的&#xff0c;git branch -r 这个命令用于列出远程分支。让我详细解释一下&#xff1a; 命令&#xff1a; git branch -rdgqdgqdeMac-mini ProductAuthentication % git branch -rorigin/main作用&#xff1a; 这个命令会显示你本地仓库知道的所有 远程分支 的列表。它不…...

回归预测 | MATLAB实现CNN-BiGRU卷积神经网络结合双向门控循环单元多输入单输出回归预测

回归预测 | MATLAB实现CNN-BiGRU卷积神经网络结合双向门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现CNN-BiGRU卷积神经网络结合双向门控循环单元多输入单输出回归预测预测效果基本介绍程序设计参考资料预测效果 基本介绍 CNN-BiGRU,即卷积神经网络(CNN)与双…...

【人工智能数学基础】——深入详解贝叶斯理论:掌握贝叶斯定理及其在分类和预测中的应用

深入详解贝叶斯理论&#xff1a;掌握贝叶斯定理及其在分类和预测中的应用 贝叶斯理论&#xff08;Bayesian Theory&#xff09;是概率论和统计学中的一个重要分支&#xff0c;它以托马斯贝叶斯&#xff08;Thomas Bayes&#xff09;命名&#xff0c;主要关注如何根据新的证据更…...

Gin-vue-admin(1):环境配置和安装

目录 环境配置如果443网络连接问题&#xff0c;需要添加代理服务器 后端运行前端运行 环境配置 git clone https://gitcode.com/gh_mirrors/gi/gin-vue-admin.git到server文件目录下 go mod tidygo mod tidy 是 Go 语言模块系统中的一个命令&#xff0c;用于维护 go.mod 文件…...

深入解析与防范:基于缓冲区溢出的FTP服务器攻击及调用计算器示例

深入解析与防范&#xff1a;基于缓冲区溢出的FTP服务器攻击及调用计算器示例 摘要 本文将详细探讨一种利用缓冲区溢出漏洞对FTP服务器进行远程攻击的技术&#xff0c;并通过分析给定代码示例&#xff0c;揭示其工作原理和潜在风险。我们将重点讨论如何在靶机上实现调用计算器…...