跳到主要内容

异步编排与SQL优化

问题复现(9.5s)

当游客到某个景点后,会去查询景点,食物,购物商城等信息

long start = System.currentTimeMillis();
List<OutBaseProductInfoVo> scenicList = productCoordinateService.getScenicList(nearbyVoParams);
List<OutBaseProductInfoVo> accommodationList = productCoordinateService.getAccommodationList(nearbyVoParams);
List<OutBaseProductInfoVo> foodList = productCoordinateService.getFoodList(nearbyVoParams);
List<OutBaseProductInfoVo> shoppingList = productCoordinateService.getShoppingList(nearbyVoParams);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");

SQL执行记录:

image-20240805170249708

整体的执行时间:

image-20240805170403260

这里不做SQL的排查,只做任务的处理,我们可以看到这里的任务之间没有关系,因此可以考虑

异步编排-整体优化(6s)

long start = System.currentTimeMillis();

CompletableFuture<List<OutBaseProductInfoVo>> scenicFuture = CompletableFuture.supplyAsync(() -> productCoordinateService.getScenicList(nearbyVoParams));
CompletableFuture<List<OutBaseProductInfoVo>> accommodationFuture = CompletableFuture.supplyAsync(() -> productCoordinateService.getAccommodationList(nearbyVoParams));
CompletableFuture<List<OutBaseProductInfoVo>> foodFuture = CompletableFuture.supplyAsync(() -> productCoordinateService.getFoodList(nearbyVoParams));
CompletableFuture<List<OutBaseProductInfoVo>> shoppingFuture = CompletableFuture.supplyAsync(() -> productCoordinateService.getShoppingList(nearbyVoParams));

WXNearByVo nearByVo = new WXNearByVo();
nearByVo.setScenicList(scenicFuture.get());
nearByVo.setAccommodationList(accommodationFuture.get());
nearByVo.setFoodList(foodFuture.get());
nearByVo.setShoppingList(shoppingFuture.get());
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");

优化后效果:

image-20240805171024892

整体从9s->6s

各项优化(4s)

这么简单的需求能查询四十多次数据库,显然是SQL写的有问题

具体到每一项上面

long start = System.currentTimeMillis();
List<OutBaseProductInfoVo> scenicList = productCoordinateService.getScenicList(nearbyVoParams);
long end1 = System.currentTimeMillis();
System.out.println("scenic耗时:" + (end1 - start) + "ms");
List<OutBaseProductInfoVo> accommodationList = productCoordinateService.getAccommodationList(nearbyVoParams);
long end2 = System.currentTimeMillis();
System.out.println("accommodation耗时:" + (end2 - end1) + "ms");
List<OutBaseProductInfoVo> foodList = productCoordinateService.getFoodList(nearbyVoParams);
long end3 = System.currentTimeMillis();
System.out.println("food耗时:" + (end3 - end2) + "ms");
List<OutBaseProductInfoVo> shoppingList = productCoordinateService.getShoppingList(nearbyVoParams);
long end4 = System.currentTimeMillis();
System.out.println("shopping耗时:" + (end4 - end3) + "ms");
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");

结果:

scenic耗时:2322ms
accommodation耗时:2816ms
food耗时:2857ms
shopping耗时:4035ms

shooping

首先考虑shooping优化

排查到问题,循环查库

List<NearbyVo> nearbyVos = productCoordinateDao.selectNearbyProductList(myParams);
List<OutBaseProductInfoVo> outBaseProductInfoVos = new ArrayList<OutBaseProductInfoVo>();
for (NearbyVo nearbyVo : nearbyVos) {
OutBaseProductInfoVo outBaseProductInfoVo = productCoordinateDao
.findShoppingById(nearbyVo.getProductId());
outBaseProductInfoVos.add(outBaseProductInfoVo);
}

显然只需要一次查询就可以拿到结果,优化后:

        List<Long> productIds = nearbyVos.stream().map(NearbyVo::getProductId).collect(Collectors.toList());
List<OutBaseProductInfoVo> outBaseProductInfoVos = productCoordinateDao.findShoppingByIds(productIds);

优化结果:

image-20240805174558317

接着优化其他,注意到基本都存在循环查询带来的问题,然后这个并不能解决太多的耗时,因此还需要考虑索引方面

food

        List<OutBaseProductInfoVo> outBaseProductInfoVos = new ArrayList<OutBaseProductInfoVo>();
for (NearbyVo nearbyVo : nearbyVos) {
OutBaseProductInfoVo outBaseProductInfoVo = productCoordinateDao
.findFoodInfoById(nearbyVo.getProductId());
outBaseProductInfoVos.add(outBaseProductInfoVo);
}

优化:

        List<Long> productIds = nearbyVos.stream().map(NearbyVo::getProductId).collect(Collectors.toList());
List<OutBaseProductInfoVo> outBaseProductInfoVos = productCoordinateDao.findFoodInfoByIds(productIds);

Accomodation

List<OutBaseProductInfoVo>     outBaseProductInfoVos = new ArrayList<OutBaseProductInfoVo>();
for (NearbyVo nearbyVo : nearbyVos) {
OutBaseProductInfoVo outBaseProductInfoVo = productCoordinateDao
.findAccomodationInfoById(nearbyVo.getProductId());
outBaseProductInfoVos.add(outBaseProductInfoVo);
}

优化

        List<Long> productIds = nearbyVos.stream().map(NearbyVo::getProductId).collect(Collectors.toList());
List<OutBaseProductInfoVo> outBaseProductInfoVos = productCoordinateDao.findAccomodationInfoByIds(productIds);

scenic

List<OutBaseProductInfoVo> outBaseProductInfoVos = new ArrayList<OutBaseProductInfoVo>();
for (NearbyVo nearbyVo : nearbyVos) {
OutBaseProductInfoVo outBaseProductInfoVo = productCoordinateDao
.findScenicSpotInfoById(nearbyVo.getProductId());
outBaseProductInfoVos.add(outBaseProductInfoVo);
}

优化:

List<Long> productIds = nearbyVos.stream().map(NearbyVo::getProductId).collect(Collectors.toList());
List<OutBaseProductInfoVo> outBaseProductInfoVos = productCoordinateDao.findScenicSpotInfoByIds(productIds);

观察优化循环查询后的结果,这个结果是相对于10s的,而不是异步编排的

image-20240805174812078

使用异步编排:

image-20240805175056872

目前是从9s优化到4s左右:9->4

SQL语句执行降到了8条:44->8

image-20240805175547096

-- 1 -- ==>  
SELECT product_id, round( ( ST_DISTANCE_SPHERE ( st_GeomFromText ('POINT (101.94488525 36.83127213)'), coordinate ) ), 2 )/1000 distance FROM product_coordinate WHERE product_type = 4 ORDER BY distance ASC LIMIT 10;
-- 2 -- ==>
SELECT product_id, round( ( ST_DISTANCE_SPHERE ( st_GeomFromText ('POINT (101.94488525 36.83127213)'), coordinate ) ), 2 )/1000 distance FROM product_coordinate WHERE product_type = 1 ORDER BY distance ASC LIMIT 10;
-- 3 -- ==>
SELECT product_id, round( ( ST_DISTANCE_SPHERE ( st_GeomFromText ('POINT (101.94488525 36.83127213)'), coordinate ) ), 2 )/1000 distance FROM product_coordinate WHERE product_type = 3 ORDER BY distance ASC LIMIT 10;
-- 4 -- ==>
SELECT product_id, round( ( ST_DISTANCE_SPHERE ( st_GeomFromText ('POINT (101.94488525 36.83127213)'), coordinate ) ), 2 )/1000 distance FROM product_coordinate WHERE product_type = 2 ORDER BY distance ASC LIMIT 10;
-- 5 -- ==>
SELECT a.id,a.name,a.list_pic as img,a.tag,a.introduce as introduction,a.score,4 as product_Subject_Id,a.scenic_spot_id,s.longitude,s.latitude, a.price,a.address,a.small_routine_url,a.h5_url,a.appid, count( 1 ) AS explain_num,city_name,a.list_pic,a.thumb_pic,a.introduce FROM scenic_spot_product a INNER JOIN `scenic_spot_product_navigation` b ON a.id = b.product_id inner join scenic_spot s on a.scenic_spot_id=s.id INNER JOIN sale_product sp ON s.id = sp.sid WHERE a.`status` = 0 AND a.del = 0 AND b.plate_info_navigation_id = 1 AND a.scenic_spot_id in ( 9051 , 9012 , 9096 , 9084 , 5031 , 13981 , 2410 , 4426 , 8755 , 4141 )
-- 6 -- ==>
SELECT a.id, a.name, a.list_pic as img, a.tag, a.introduce as introduction, a.score, 1 as product_Subject_Id, a.address, a.latitude, a.longitude,city_name,a.list_pic,a.thumb_pic,a.introduce FROM product_accommodation a INNER JOIN `product_accommodation_navigation` b ON a.id = b.product_id WHERE a.`status` = 0 AND a.del = 0 AND b.plate_info_navigation_id = 8 AND a.id in ( 827269 , 827324 , 827332 , 827340 , 827331 , 827262 , 827204 , 827597 , 827233 , 827328 )
-- 7 -- ==>
SELECT a.id, a.name, a.list_pic as img, a.tag, a.introduce as introduction, a.score, 1 as product_Subject_Id, a.address, a.latitude, a.longitude,city_name,a.list_pic,a.thumb_pic,a.introduce FROM product_shopping a INNER JOIN `product_shopping_navigation` b ON a.id = b.product_id WHERE a.`status` = 0 AND a.del = 0 AND b.plate_info_navigation_id = 7 AND a.id in ( 993161 , 993553 , 993162 , 993356 , 993825 , 993406 , 993387 , 993571 , 993803 , 993425 )
-- 8 -- ==>
SELECT a.id, a.name, a.list_pic as img, a.tag, a.introduce as introduction, a.score, 1 as product_Subject_Id, a.address, a.latitude, a.longitude,city_name,a.list_pic,a.thumb_pic,a.introduce FROM product_fine_food a INNER JOIN `product_fine_food_navigation` b ON a.id = b.product_id WHERE a.`status` = 0 AND a.del = 0 AND b.plate_info_navigation_id = 2 AND a.id in ( 984509 , 985245 , 985247 , 984860 , 984621 , 985068 , 984924 , 985101 , 985087 , 984583 )

剩下的事情就是考虑索引优化了

空间索引优化(1.7s)

依次考虑8条SQL语句,看每个的执行时间进行索引优化:

第一条语句:

SELECT product_id, round( ( ST_DISTANCE_SPHERE ( st_GeomFromText ('POINT (101.94488525 36.83127213)'), coordinate ) ), 2 )/1000 distance 
FROM product_coordinate WHERE product_type = 4 ORDER BY distance ASC LIMIT 10;

执行结果:这条SQL居然要1.35s

image-20240805201235981

630311,1257.74461
630218,1256.89393
630217,1259.53702
622560,291.97209
622540,291.92565
1237732,1209.59362
534416,1209.66005
634247,1120.00637
622538,291.05516
622544,291.05866

先记录结果,为了后面优化后要和结果一致

查看索引:

create spatial index coordinate_index
on product_coordinate (coordinate);
create unique index product_coordinate_unique_key
on product_coordinate (coordinate(25), product_type, product_id);

可以看到有空间索引,当然还有联合索引

查询执行计划explain:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEproduct_coordinatenullALLnullnullnullnull157826010Using where; Using filesort

可以看到sql语句走了全表扫描,并且还有文件排序,那么这个肯定是非常耗时的,而且数据量150万

观察到sql语句中:

round((ST_DISTANCE_SPHERE(st_GeomFromText('POINT (101.94488525 36.83127213)'), coordinate)), 2) / 1000 distance

查询结果是函数,这个表达式的目的是计算两点之间的距离,并将结果四舍五入到小数点后两位,然后将结果转换为千米

  • ST_GeomFromText('POINT (101.94488525 36.83127213)')这个函数将WKT(Well-Known Text)格式的点字符串转换为几何对象。

  • ST_DISTANCE_SPHERE(geom1, geom2)这个函数计算两个几何对象之间的大圆距离(地球表面上的最短距离),结果以米为单位。

下面开始优化,首先去掉排序,去掉round,去掉无关的变量,看哪个会走全表

explain SELECT coordinate FROM product_coordinate WHERE product_type = 4 LIMIT 10;

这样会全表扫描:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEproduct_coordinatenullALLnullnullnullnull157826010Using where

分析原因:

  • PRIMARY 索引:在 id 列上。

  • coordinate_index:空间索引在 coordinate 列上。

  • product_coordinate_unique_key:唯一索引在 coordinate(25), product_type, product_id 列上。

由于查询只包含 product_type 这一列,因此不会利用上述索引,从而导致全表扫描。为了避免全表扫描,需要一个直接在 product_type 列上的索引。

索引失效的主要原因在于现有的索引未能充分覆盖 WHERE 子句中的条件,也没有有效地优化查询。因此考虑对type建立索引:

create index product_coordinate_product_type_index
on product_coordinate (product_type);
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEproduct_coordinatenullrefproduct_coordinate_product_type_indexproduct_coordinate_product_type_index4const789130100null

此时发现走索引了,但是发现仍然要扫描789130行数据,因为索引选择性不高(即 product_type 列上有太多相同的值),导致仍然需要扫描大量行。这是索引未能显著减少查询成本的原因。

优化思路:

先进行一个空间范围查询,缩小查询范围,然后再计算距离。这种方法可以有效地利用空间索引,提高查询效率。

1度的纬度大约等于111公里,在高纬度地区会小一些。

优化:

explain
SELECT product_id,
ST_DISTANCE_SPHERE(st_GeomFromText('POINT (101.94488525 36.83127213)'), coordinate) distance
FROM product_coordinate
WHERE product_type = 4
and MBRContains(
ST_Buffer(
ST_GeomFromText('POINT (101.94488525 36.83127213)'),
10000 / 111000
),
coordinate
)
ORDER BY distance ASC
LIMIT 10;

执行结果:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEproduct_coordinatenullrangeproduct_coordinate_unique_key,product_coordinate_product_type_index,coordinate_indexcoordinate_index34null1550Using where; Using filesort

type: range - 表示使用范围扫描,这是一个较好的访问类型,特别是在使用索引时。

possible_keys: 提供了一些可能使用的索引,包括product_coordinate_unique_keyproduct_coordinate_product_type_indexcoordinate_index

key: coordinate_index - 实际使用的索引是coordinate_index

key_len: 34 - 索引键的长度。

ref: null - 没有引用其他表或列。

rows: 15 - MySQL估计需要扫描15行来满足查询条件。

filtered: 50 - MySQL估计有50%的行会通过过滤条件。

Extra: Using where; Using filesort - 额外的信息,表示MySQL在过滤行时使用了WHERE条件,并且在排序时使用了文件排序。

执行耗时:

image-20240805215453146

这次只需要0.07s了,因为我们覆盖到的索引非常多,基本上都覆盖了,这样的结果和一开始的病不对,我们还缺少了一次计算,但是这个计算肯定不能放到sql里,因为函数计算会导致索引失效,我们仔细看查询的结果,我们的到的结果是米,只需要在Java代码中在内存中除以1000即可,这样就可以避免索引失效

image-20240805215837626

优化后的xml语句:

<select id="selectNearbyProductList" resultMap="NearbyVo"
parameterType="com.tourism.restful.vo.NearbyVoParams">

SELECT product_id,
ST_DISTANCE_SPHERE(ST_GeomFromText('POINT (${longitude} ${latitude})'), coordinate)AS distance
FROM product_coordinate
WHERE product_type = #{productType}
AND MBRContains(
ST_Buffer(
ST_GeomFromText('POINT (${longitude} ${latitude})'),
100000 / 111000
),
coordinate
)
ORDER BY distance ASC
LIMIT #{pageSize};
</select>

执行结果:

image-20240805220806964

此时整体的代码优化已经来到了1.7s

索引优化(0.5s)

执行的SQL语句为:

explain 
SELECT a.id,
a.name,
a.list_pic as img,
a.tag,
a.introduce as introduction,
a.score,
1 as product_Subject_Id,
a.address,
a.latitude,
a.longitude,
city_name,
a.list_pic,
a.thumb_pic,
a.introduce
FROM product_fine_food a
INNER JOIN `product_fine_food_navigation` b ON a.id = b.product_id
WHERE a.`status` = 0
AND a.del = 0
AND b.plate_info_navigation_id = 2
AND a.id in (9051, 9012, 9096, 9084, 5031, 13981, 2410, 4426, 8755, 4141)

执行情况:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,idPRIMARY8null105Using where
1SIMPLEbnullALLnullnullnullnull2324341Using where; Using join buffer (Block Nested Loop)

image-20240805221712253

可以看到第一个表走了索引,而且只用查找10个数据,基本上没有问题了,而第二张表要找20万数据,虽然耗时不大,但是为了以后考虑,肯定要加索引

观察第二个表的建标语句:

create table product_fine_food_navigation
(
id int auto_increment
primary key,
product_id bigint(11) null,
plate_info_navigation_id bigint(11) null,
order_num int null,
status int default 0 null comment '0 显示 -1 隐藏'
)
charset = utf8;

也就是这个表除了主键索引,其他索引都没有加,那么我们开始根据条件加索引,也就是product_id以及plate_info_navigation_id需要走索引

create index product_fine_food_navigation_plate_info_navigation_id_index
on product_fine_food_navigation (plate_info_navigation_id);

create index product_fine_food_navigation_product_id_index
on product_fine_food_navigation (product_id);

此时再去看执行计划:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,idPRIMARY8null105Using where
1SIMPLEbnullrefproduct_fine_food_navigation_plate_info_navigation_id_index,product_fine_food_navigation_product_id_indexproduct_fine_food_navigation_product_id_index9leyoobao.a.id150Using where

发现全部都走索引了,此时执行耗时:

image-20240805221827687

也就是从0.35秒优化到0.05秒

此时再去看接口耗时:

image-20240805222055627

这样的语句有四个,都需要去优化,其他表分别是:product_shopping_navigationscenic_spot_product_navigationproduct_accommodation_navigation

accommodation

SELECT a.id, a.name, a.list_pic as img, a.tag, a.introduce as introduction, a.score, 1 as product_Subject_Id, a.address, a.latitude, a.longitude,city_name,a.list_pic,a.thumb_pic,a.introduce FROM product_accommodation a INNER JOIN `product_accommodation_navigation` b ON a.id = b.product_id WHERE a.`status` = 0 AND a.del = 0 AND b.plate_info_navigation_id = 8 AND a.id in ( 9051 , 9012 , 9096 , 9084 , 5031 , 13981 , 2410 , 4426 , 8755 , 4141 )

执行计划:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,idPRIMARY8null105Using where
1SIMPLEbnullALLnullnullnullnull2362731Using where; Using join buffer (Block Nested Loop)

添加索引:

create index product_accommodation_navigation_plate_info_navigation_id_index
on product_accommodation_navigation (plate_info_navigation_id);

create index product_accommodation_navigation_product_id_index
on product_accommodation_navigation (product_id);

执行计划:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,idPRIMARY8null105Using where
1SIMPLEbnullrefproduct_accommodation_navigation_plate_info_navigation_id_index,product_accommodation_navigation_product_id_indexproduct_accommodation_navigation_product_id_index9leyoobao.a.id150Using where

耗时:

image-20240805224451717

shopping

explain
SELECT a.id,
a.name,
a.list_pic as img,
a.tag,
a.introduce as introduction,
a.score,
1 as product_Subject_Id,
a.address,
a.latitude,
a.longitude,
city_name,
a.list_pic,
a.thumb_pic,
a.introduce
FROM product_shopping a
INNER JOIN `product_shopping_navigation` b ON a.id = b.product_id
WHERE a.`status` = 0
AND a.del = 0
AND b.plate_info_navigation_id = 7
AND a.id in (984509, 985245, 985247, 984860, 984621, 985068, 984924, 985101, 985087, 984583)

执行计划:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,idPRIMARY8null105Using where
1SIMPLEbnullALLnullnullnullnull5974881Using where; Using join buffer (Block Nested Loop)

添加索引:

create index product_shopping_navigation_plate_info_navigation_id_index
on product_shopping_navigation (plate_info_navigation_id);

create index product_shopping_navigation_product_id_index
on product_shopping_navigation (product_id);

执行计划:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,idPRIMARY8null105Using where
1SIMPLEbnullrefproduct_shopping_navigation_plate_info_navigation_id_index,product_shopping_navigation_product_id_indexproduct_shopping_navigation_product_id_index9leyoobao.a.id150Using where

耗时:

image-20240805224709868

scenic

explain
SELECT a.id,
a.name,
a.list_pic as img,
a.tag,
a.introduce as introduction,
a.score,
4 as product_Subject_Id,
a.scenic_spot_id,
s.longitude,
s.latitude,
a.price,
a.address,
a.small_routine_url,
a.h5_url,
a.appid,
count(1) AS explain_num,
city_name,
a.list_pic,
a.thumb_pic,
a.introduce
FROM scenic_spot_product a
INNER JOIN `scenic_spot_product_navigation` b ON a.id = b.product_id
inner join scenic_spot s on a.scenic_spot_id = s.id
INNER JOIN sale_product sp ON s.id = sp.sid
WHERE a.`status` = 0
AND a.del = 0
AND b.plate_info_navigation_id = 1
AND a.scenic_spot_id in (9051, 9012, 9096, 9084, 5031, 13981, 2410, 4426, 8755, 4141)

执行计划:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,scenic_spot_id,idscenic_spot_id5null105Using index condition; Using where
1SIMPLEsnulleq_refPRIMARY,idPRIMARY8leyoobao.a.scenic_spot_id1100Using where
1SIMPLEbnullALLnullnullnullnull285811Using where; Using join buffer (Block Nested Loop)
1SIMPLEspnullALLnullnullnullnull1351910Using where; Using join buffer (Block Nested Loop)

可以发现这里两个表没有走索引

建立索引:

scenic_spot_product_navigation表:

create index scenic_spot_product_navigation_product_id_index
on scenic_spot_product_navigation (product_id);
create index scenic_spot_product_navigation_plate_info_navigation_id_index
on scenic_spot_product_navigation (plate_info_navigation_id);

sale_product表:

create index sale_product_sid_index
on sale_product (sid);

此时再去看执行计划:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEanullrangePRIMARY,scenic_spot_id,idscenic_spot_id5null105Using index condition; Using where
1SIMPLEsnulleq_refPRIMARY,idPRIMARY8leyoobao.a.scenic_spot_id1100Using where
1SIMPLEbnullrefscenic_spot_product_navigation_product_id_index,scenic_spot_product_navigation_plate_info_navigation_id_indexscenic_spot_product_navigation_product_id_index9leyoobao.a.id250Using where
1SIMPLEspnullindexsale_product_sid_indexsale_product_sid_index2003null1351910Using where; Using index; Using join buffer (Block Nested Loop

可以看到基本上都走索引了

耗时:

image-20240805225336999

此时耗时来到了487ms

image-20240805225500729

关闭事务(188ms)

我们观察到上面会存在事务,Transactional,然后我们是查询,里面不会做任何修改操作,因此不需要回滚这种操作,完全可以关闭事务,也就是优化将事务移动到每个方法上,而不是加在类上。

image-20240805230039337

Mybatis Log 插件Bug

在多线程情况下查询日志有问题,会有重复,而控制台的日志没问题

image-20240805223923101

为什么MBRContains可以走索引

MBRContains 和 ST_DISTANCE_SPHERE 的区别

  1. MBRContains:
    • MBRContains 是一个基于最小包围矩形(MBR, Minimum Bounding Rectangle)的快速空间过滤操作。
    • 这种操作可以利用空间索引(如 R 树)来快速确定一个几何对象是否在指定的范围内。
    • 空间索引存储的是几何对象的最小包围矩形,所以 MBRContains 可以直接利用这些预计算的矩形进行高效过滤。
  2. ST_DISTANCE_SPHERE:
    • ST_DISTANCE_SPHERE 是一个计算两点之间球面距离的函数。
    • 这种计算是基于具体的几何坐标进行的,需要遍历每个点并计算精确的球面距离。
    • 由于这种计算需要实际的点坐标,无法直接利用空间索引中的最小包围矩形进行加速,因此不能直接利用索引。

为什么 MBRContains 可以走索引

  • 索引机制
    • 空间索引(如 R 树)是基于几何对象的最小包围矩形进行索引的,能够快速进行范围查询。
    • MBRContains 的查询可以直接利用这些最小包围矩形进行初步过滤,减少需要精确计算的对象数量。

为什么 ST_DISTANCE_SPHERE 不可以走索引

  • 函数计算
    • ST_DISTANCE_SPHERE 需要计算每个点的实际球面距离,这需要具体的坐标数据。
    • 索引仅能用于预先定义的简单操作和范围查询,不能用于复杂的数学计算。
    • 这种函数计算需要在筛选后的结果集上逐个进行,无法通过索引加速。