腾讯业务百万数据 6s 响应,APIJSON 性能优化背后的故事

2023-11-09

最近发生了一件大事儿,APIJSON 再也不用担心被人质疑性能问题了哈哈!

某周三腾讯 CSIG 某项目组(已经用 APIJSON 做完一期)突然反馈了查询大量数据性能急剧下降的情况:

某张表 2.3KW 记录,用 APIJSON 万能通用接口 /get 查数据 LIMIT 100 要 10s,LIMIT 1000 要 98s!

可把我吓了一跳,这么慢还怎么用。。。想了会先让他们 Log.DEBUG = false 关掉日志看看:

还同样那张表,还是同样的同域 CURL 请求 /get,LIMIT 100 降为 2s,LIMIT 1000 降为 30s。

看起来勉强够用,一般都是分页查 10 条 20 条的样子,但他们的业务“LIMIT 1000 的需求还挺多的”。

这可是腾讯内第一个用 APIJSON 的团队及项目,不仅用了好几个月,还在内网写了多篇文章帮忙推广,

怎么都不能辜负他们的信任和期望,更不能让这个事件成为 APIJSON 性能差的污点从而影响用户口碑。

image

我看着日志寻思着应该是数组内主表生成了 1000 个 SQLConfig 并调用 getSQL 等过程导致解析耗时过长,

而除了第 0 条,剩下的 999 条记录都完全没必要重复这个过程,尤其是期间大量打印日志非常耗时。

当时是为了兼容多表关联查询,自己的业务又几乎没有 LIMIT 100 以上的需求,所以影响不大就忽略了。

现在如果把第 0 条数据的解析结果(ObjectParser, SQLConfig 等类及变量状态)缓存起来给后面的复用

那不就可以把 2s 压缩到只比 SQL 执行耗时 133ms 多出少量 APIJSON 解析过程耗时 的时长了?

“我有方案了,应该可以把 2s 优化到 200ms 左右” “我这周末优化下” 我拍着脑门给出一个承诺。

这个组的后端同事职业本能地探究细节:“现在的 30s 主要耗时在哪里?” “怎么搞?”

“执行 SQL 133ms 耗时正常,整体慢是 日志(大头) 和 解析(小头) 的锅” “SQLExecutor 应该一次返回整个列表给 Parser”

看起来这个同事得到了些许安慰:“这个可以优化的话,LIMIT 1000 耗时 30s 估计也只要几秒了”

周末除了 APIAuto-机器学习 HTTP 接口工具 的简单更新:

  • 零代码回归测试:用经验法解决冷启动问题,在没有校验标准时也能进行断言
  • 自动生成注释:新增根对象全局关键词键值对的语法提示

剩下重点都是 APIJSON 的性能优化,我分成了两步:

1.查数组主表时把 SQLExecutor.execute 查到的原始列表全部返回,缓存到新增的 arrayMainCacheMap

https://github.com/Tencent/APIJSON/commit/bb4896d208e813a3f5eec21f60e1c083621b1f92

2.查数组主表时把第 0 个 ObjectParser 实例缓存到新增的 arrayObjectParserCacheMap

https://github.com/Tencent/APIJSON/commit/a406242a81f2b303a1c55e6a4f5c3c835e62e53a

期间尝试修改返回类型 JSONObject 为 List<JSONObject> 发现改动范围过大,而且只有这个场景需要,

于是就改为在 JSONObject 中加个肯定不会是表字段的特殊字段 @RAW@LIST 来带上 ResultSet 对应的 List;

改完后性能确实大幅提升,但发现除了第 0 条,后面的数组项全都丢了副表记录,于是反复调试修改多次终于解决;

接着按第 2 步优化,性能再次大幅提升,手动测试也没问题,用 APIAuto 一跑回归测试发现计数字段 total 没返回,

这次运气好点,因为之前也碰到过同样问题,我解决后加了比较详细的注释说明,所以回顾审视后没多久就解决了。

每步完成后,我都对 TestRecord[], 朋友圈多层嵌套列表 等查询 LIMIT 100 用优化前后代码各跑 10 次以上,

最终无论是 都取最小值,还是 去掉几个最小和最大值后其余取平均值,得出的前后性能对比结论基本一致:

步骤 1 将单表列表耗时降低为原来 42%,朋友圈多层嵌套列表降为原来的 82%;

步骤 2 将单表列表耗时降低为原来 60%,朋友圈多层嵌套列表降为原来的 77%;

算出总体上将单表列表耗时降低为原来 25%,朋友圈多层嵌套列表降为原来的 63%

MacBook Pro 英寸 2015 年初期 13 Intel i5 双核 2.9 GHz,250G SSD,OSX EI Capitan 10.11.6

Docker Community Edition 18.03.1-ce-mac65 (24312)

MySQL Community Server 5.7.17 - 跑在 Docker 里,性能会比直接安装的差不少

Eclipse Java  EE Neon.1a Release (4.6.1) - 全量保留日志,比用有限保留日志的 IntelliJ IDEA 2018.2.8 (IU-182.5262.2) 慢一个数量级

实测结果如下:

TestRecord[] 查询前后对比 耗时降至 35%,性能提升 186% 为原来 2.9 倍:

朋友圈列表查询前后对比 耗时降至 34%,性能提升 192% 为原来 2.9 倍:

改为 [], []/User[], []/[] 组合且每个数组长度 count 都改为 0(对应 100 条),最多返回 100*(1 + 100 + 100*2) = 30100 条记录:

注:找不到 5651ms 对应那张图了,用最接近的图替代

模拟 to C 应用的这个极端情况下 生成 SQL 数从 1397 减少至 750,耗时降至 83%,性能提升 20% 为原来 1.2 倍。

最多才降低耗时为原来 25%?那估计他们组原来 2s 只能优化到  500ms(承诺 200ms 左右),30s 只能优化到 7.5s。

天,牛都吹出去了,这下怎么办?... 算了算了,都一点了,先睡吧,先发版明天让他们试试看,唉...

于是我就打着哈欠把 APIJSON 和 APIAuto 都部署到 apijson.cn,简单自测没问题,然后到两点多就洗洗睡了...

(后面又反复多次自测,虽然结果都和以上接近,但 Release 上用了更保守低调的数值)

第二天上午通知腾讯 CSIG 某项目组的同事更新 APIJSON,下午同事在 APIJSON 咨询群 大赞“给力!”并发了几张截屏。

一看 LIMIT 1000000 我虎躯一震:好家伙,上来就线上生产环境百万数据暴力测试,年轻人不讲武德了么?

---------- | ---------- | ---------- | ---------- | -------------
   Total   |  Received  | Time Total | Time Spent | Current Speed
   72.5M   |    72.5M   |   0:00:05  |   0:00:05  |     20.0M

/get >> http请求结束:5624

一时没控记住记几,在公司连续大喊 “我靠!一百万五秒!一百万五秒!查一百万条记录耗时五秒!”

差点还喊出洗脑广告 “OMG,顶它顶它顶它!!!”

把周边同事吓了一跳,过了一会有几个后端同事反应过来纷纷点赞!

冷静下来我仔细分析:

由于他们这次用 CURL 测试接口时,对应的 APIJSON 服务关掉了日志,不能直接看到服务从接收参数到响应结果的耗时,

所以按以上数据计算是 总耗时 5624ms - 数据 72.5M/下载速度 20.0M每秒 = 1999ms,也就是服务执行过程耗时仅仅 2s!

显然 APIJSON 这次性能优化效果显著远超预期,同时 2.3KW 大表查 100W 记录仅 1s 左右也反应了 腾讯 TDSQL 的迅猛!

然而后来同事反馈,这次带条件查出来实际上不到 100W,只有 12W 多数据。

瞬间把我从丰满的理想拉回了骨感的现实,嗯,革命尚未成功,仍需再接再厉!

经过了几个版本迭代,4.8.0 终于迎来再一次大幅性能提升,这次终于做到了!

APIJSON 4.7.0 和 4.8.0 腾讯 CSIG 某项目性能测试结果

1. 测试环境

1.1 机器配置

腾讯云tke docker pod, 4 核 / 16G。

1.2 db机器配置

腾讯云8核32000MB内存,805GB存储空间

1.3 测试表建表DML、数据量(mysql 5.7)

CREATE TABLE `t_xxxx_xxxx` (
    `x_id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
    `x_xxxx_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xID',
    `x_xid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xxID',
    `x_xx_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xID',
    `x_xxxx_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xxxID',
    `x_xxxxx_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xxID',
    `x_xxxx_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xxID',
    `x_uin` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'xxuin',
    `x_send_time` datetime DEFAULT NULL COMMENT '推送消息时间',
    `x_xxxx_result` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xx结果',
    `x_xxx_xxxx_result` varchar(255) DEFAULT '' COMMENT 'xx结果',
    `x_result` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '0错误 1正确 2未设置',
    `x_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间, 落地时间',
    `x_credit` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'xx数量',
    `x_xxxxxx_xxx_id` varchar(32) NOT NULL COMMENT '公共参数, 上报应用',
    `x_xxxxxx_source` varchar(32) NOT NULL COMMENT '公共参数, 上报服务名',
    `x_xxxxxx_server` varchar(32) NOT NULL COMMENT '公共参数, 上报服务端ip',
    `x_xxxxxx_event_time` datetime NOT NULL COMMENT '公共参数, 上报时间',
    `x_xxxxxx_client` varchar(32) NOT NULL COMMENT '公共参数, 客户端ip',
    `x_xxxxxx_trace_id` varchar(64) NOT NULL COMMENT '公共参数',
    `x_xxxxxx_sdk` varchar(16) NOT NULL COMMENT '公共参数, sdk版本',
    PRIMARY KEY (`x_id`, `x_uin`),
    UNIQUE KEY `udx_uid_xxxxid` (`x_uin`, `x_xxxx_id`),
    KEY `idx_xid` (`x_xid`)
  ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4  COMMENT = 'xx事件表'; 
  • 数据量:18558903

  • mysql版本:5.7

  • 数据分布:使用group by 统计,基于其中x_xid来group by,得到以下表格:

select x_xid, count(x_id) counter from t_xxxx_xxxx group by x_xid order by counter desc limit 10;

x_xid counter
xxxx36 696376
xxxx38 418576
xxxx63 384503
xxxx40 372080
xxxx41 301364
xxxx08 248243
xxxx46 223820
xxxx07 220234
xxxx44 207721
xxxx02 152795

1.4 日志打印设置

Log.DEBUG = false;

AbstractParser.IS_PRINT_REQUEST_STRING_LOG = false;
AbstractParser.IS_PRINT_REQUEST_ENDTIME_LOG = false;
AbstractParser.IS_PRINT_BIG_LOG = false;

2. 测试脚本 (使用Table[]: {Table: {}}格式)

脚本统计方式:

  • 基于linux time命令输出的realtime来统计。

  • 分2个场景测试:一个不带where条件、一个带x_xid in (xxxx36,xxxx38)的条件,该条件能匹配出100W+数据,方便覆盖10W-100W之间的任何数据量场景,这里事先用select x_xid, count(x_id) c from t_xxxx_xxxx group by x_xid order by c desc;这样的语句对表做了统计,x_xid=xxxx36有696376条记录,x_xid=xxxx38有418576条记录。

脚本:apitest.sh

#!/bin/bash
printf -- '--------------------------\n开始不带where条件的情况测试\n'
time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":100000, "T_xxxx_xxxx":{"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 10w_no_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":200000, "T_xxxx_xxxx":{"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 20w_no_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":500000, "T_xxxx_xxxx":{"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 50w_no_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":800000, "T_xxxx_xxxx":{"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 80w_no_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":1000000, "T_xxxx_xxxx":{"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 100w_no_where.log



printf -- '--------------------------\n开始带where条件的情况测试\n'
time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":100000, "T_xxxx_xxxx":{"x_xid{}":[xxxx36,xxxx38],"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 10w_with_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":200000, "T_xxxx_xxxx":{"x_xid{}":[xxxx36,xxxx38],"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 20w_with_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":500000, "T_xxxx_xxxx":{"x_xid{}":[xxxx36,xxxx38],"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 50w_with_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":800000, "T_xxxx_xxxx":{"x_xid{}":[xxxx36,xxxx38],"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 80w_with_where.log

time curl  -X POST -H 'Content-Type:application/json' 'http://x.xxx.xx.xxx:xxxx/get' -d '{"T_xxxx_xxxx[]":{"count":1000000, "T_xxxx_xxxx":{"x_xid{}":[xxxx36,xxxx38],"@column":"x_uin,x_send_time,x_xxxx_id,x_xid,x_xx_id,x_xxxxx_id,x_xxxx_result,x_result,x_credit"}}}' > 100w_with_where.log

也就是 MySQL 5.7 共 1.9KW 记录的大表,统计 CRUL 10-20M/s 网速从发起请求到接收完回包的总时长

数量级 4.7.0(5次取平均值) 4.8.0(5次取平均值) 是否正常回包 where条件 性能提升
10W 1.739s 1.159s 50%。即((1/1.159-1/1.739)/(1/1.739))*100%
20W 3.518s 2.676s 31.5%
50W 9.257s 6.952s 33.2%
80W 16.236s 10.697s -Xmx=3192M时无法正常回包,OOM错误,调大-Xmx参数后ok。 51.8%
100W 19.748s 14.466s -Xmx=3192M时无法正常回包,OOM错误,调大-Xmx参数后ok 36.5%
10W 1.928s 1.392s "x_xid{}":[xxxx36,xxxx38],覆盖数据超过100W数据。 38.5%
20W 4.149s 2.852s "x_xid{}":[xxxx36,xxxx38] 45.5%
50W 10.652s 7.231s "x_xid{}":[xxxx36,xxxx38] 47.3%
80W 16.975s 12.465s 调整了-Xmx后正常回包 "x_xid{}":[xxxx36,xxxx38] 36.2%
100W 20.632s 16.481s 调整了-Xmx后正常回包 "x_xid{}":[xxxx36,xxxx38] 25.2%

感谢同事给的详细测试报告,在他已脱敏的基础上二次脱敏,并和他确认后对外公开~

以之前的 APIJSON 4.6.0 连接 2.3KW 大表带条件查出 12W+ 数据来估计:

---------- | ---------- | ---------- | ---------- | -------------
   Total   |  Received  | Time Total | Time Spent | Current Speed
   72.5M   |    72.5M   |   0:00:05  |   0:00:05  |     20.0M

/get >> http请求结束:5624

4.6.0-4.7.2 都没有明显的性能优化,所以 4.7.0 只花了约 2s 应该是因为换了张表,平均每行数据量减少了约 65% 为原来的 35%。
APIJSON 4.6.0 查原来 2.3KW 大表中 100W 数据按新旧表数据量比例估计耗时 = 20.632s / 65% = 31.7s;
APIJSON 4.6.0 查原来 2.3KW 大表中 100W 数据按同表查出数据量比例估计耗时 = 100W*72.5M/(12-13W)/(20M/s) = 27.9s-30.2s。

两种方式估算结果基本一致,也可以按这个 35% 新旧表平均每行数据量比例估算排除网络耗时后的整个服务耗时:

APIJSON 4.6.0 查原来 2.3KW 大表中 12W+ 数据量服务耗时 = 总耗时 5.624s - 数据 72.5M/下载速度 20.0Mbps = 2.00s;
APIJSON 4.7.0 查现在 1.9KW 大表中 10W 数据量服务耗时 = 总耗时 1.928s - 数据 72.5M*35%(10/12)/(下载速度 10-20.0Mbps) = 0.00-0.87s;
APIJSON 4.8.0 查现在 1.9KW 大表中 10W 数据量服务耗时 = 总耗时 1.392s - 数据 72.5M*35%
(10/12)/(下载速度 10-20.0Mbps) = 0.00-0.33s,降低 62%;
APIJSON 4.7.0 查现在 1.9KW 大表中 100W 数据量服务耗时 = 总耗时 20.632s - 数据 725M*35%(10/12)/(下载速度 10-20.0Mbps) = 0.00-10.06s;
APIJSON 4.8.0 查现在 1.9KW 大表中 100W 数据量服务耗时 = 总耗时 16.481s - 数据 725M*35%
(10/12)/(下载速度 10-20.0Mbps) = 0.00-5.91s,降低 41%。

也就是 1.9KW 大表带条件查出 100W 条记录,APIJSON 4.8.0 的从 完成接收参数 到 开始返回数据 的整个服务耗时不到 6s!

APIJSON - 零代码接口和文档

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

腾讯业务百万数据 6s 响应,APIJSON 性能优化背后的故事 的相关文章

  • java中高效的输入流到字符串方法

    因此 我在 Java 中的 诚然非常简单 应用程序上运行探查器 令我惊讶的是 仅次于需要在时间上发出 HTTP 请求的方法的是我的方法 inputStreamToString方法 目前它的定义如下 public static String
  • Android Studio 与 Google Play 服务的编译问题

    我正在运行 Android Studio 0 8 4 并在 Android Studio 0 8 2 上尝试过此操作 我正在运行 Java JDK 1 8 0 11 并尝试使用 JDK 1 8 0 05 每当我尝试构建我的 android
  • 存储过程函数中的动态表名

    我编写了一个存储过程函数来从表中获取名称 问题是我希望将表名作为参数传入 有几个不同的表我需要使用此函数 DELIMITER CREATE DEFINER root localhost FUNCTION getName tableName
  • 当前平台不支持桌面 API

    我遇到过这个错误 java lang UnsupportedOperationException 当前平台不支持桌面 API 我将从我的 java 应用程序中打开一个文件 我用这个方法 Desktop getDesktop open new
  • Google 表格使用 API 密钥而不是 client_secret.json

    In the QuickStart java示例Java 快速入门 https developers google com sheets api quickstart java他们使用OAuth client ID识别该应用程序 这会弹出一
  • 如何在正则表达式中编写可选单词?

    我想编写一个识别以下模式的 java 正则表达式 abc def the ghi and abc def ghi 我试过这个 abc def the ghi 但是 它没有识别第二种模式 我哪里出错了 abc def the ghi 删除多余
  • 是否可以使用 Apache Tika 提取表信息?

    我正在寻找 pdf 和 MS Office 文档格式的解析器 以从文件中提取表格信息 当我看到 Apache Tika 时 正在考虑编写单独的实现 我能够从任何这些文件格式中提取全文 但我的要求是提取表格数据 我希望有 2 列采用键值格式
  • java:为什么主线程等待子线程完成

    我有一个简单的java程序 主线程 main 创建并启动另一个线程t class T extends Thread Override public void run while true System out println Inside
  • 如何检查单词是否在wordNet中

    我开始了解wordNet直到我知道我找到了synonymous对于一个特定的词 现在我有一个文件 我想使用标记化该文本n gram例如 String s I like to wear tee shirt 使用后n gram这将是 I lik
  • 在 Eclipse 中删除空块之前的新行

    我更喜欢奥尔曼式 http en wikipedia org wiki Brace style Allman style大括号 例如 if foo magical prancing unicorn stuff 而不是 if foo unma
  • JFrame 在连续运行代码时冻结

    我在使用时遇到问题JFrame 它会冻结 连续运行代码 下面是我的代码 点击时btnRun 我调用了该函数MainLoop ActionListener btnRun Click new ActionListener Override pu
  • java.exe 以非零退出值 1 结束

    只是为了开始 我并不是真正尝试从 Android 中的 xlsx 文件中读取单元格 我已经尝试了几乎所有我在 Google 上搜索到的内容 但是每次 在两台不同的 PC 上 都是 Java 1 7 0 79 当我尝试构建 运行 这个应用程序
  • 扩展多个类

    我知道 Java 不支持多重继承 因为不允许扩展多个类 我只是想知道我的问题是否有解决方法 我有一个名为CustomAction需要扩展两个抽象类 BaseAction and QuoteBaseAction 我无法更改这些抽象类中的任何一
  • 从字符串中删除重音符号

    Android 中有没有什么方法 据我所知 没有 java text Normalizer 可以从字符串中删除任何重音 例如 变成 eau 如果可能的话 我想避免解析字符串来检查每个字符 java text NormalizerAndroi
  • IMAP 和 PHP - 从已发送文件夹和收件箱文件夹中获取所有电子邮件

    我正在尝试获取接收和发送的所有电子邮件 并使用 PHP 将其写入 mySQL 数据库 我使用的主机名是 hostname imap gmail com 993 imap ssl INBOX 它仅引用收件箱 并成功抓取收到的电子邮件 为了抓取
  • 在 Eclipse RCP 应用程序中禁用插件贡献

    我经常遇到这个问题 但尚未找到解决方案 每当我编写一个新的基于 Eclipse RCP 的应用程序并包含来自 Eclipse 平台的插件时 我都会 继承 其中一些插件的 UI 贡献 大多数贡献 菜单项 键盘快捷键 属性页 都很有用 但有时我
  • 如何使用 AffineTransform.quadrantRotate 旋转位图?

    我想旋转一个bitmap关于它的中心点 然后将其绘制成更大的图形上下文 位图是40x40 pixels 图形上下文是500x500 pixels 这就是我正在做的 BufferedImage bi new BufferedImage 500
  • log4j.properties 在 Wildfly 上无法正常工作

    我的类路径中有一个 log4j properties 文件 它位于 APP XX jar log4j properties 位置 我注意到在ear文件中我还可以在lib文件夹中找到log4j 1 2 17 jar 但无论我在 log4j p
  • 如何在不使用 -cp 开关的情况下在 Groovy 中自动加载数据库 jar?

    我想简化调用 Oracle 数据库的 Groovy 脚本的执行 如何将 ojdbc jar 添加到默认类路径以便我可以运行 groovy RunScript groovy 代替 groovy cp ojdbc5 jar RunScript
  • Graphics2D setfont() 严重减慢了 java 应用程序的启动速度

    我正在用java制作一个游戏 它每秒刷新60次 每次执行循环时 我都会使用 g2d 来绘制图像和字符串 如果我这样做的话一切都会很好g2d setFont new Font Arial Font PLAIN 8 和抽绳 这将是正常的 但如果

随机推荐

  • Ubuntu 安装配置Samba服务器

    一 描述 Samba文件服务器可以在网络上实现不同操作系统的文件共享 它可以让你从笔记本电脑访问你的桌面文件 并与Windows和macOS用户共享文件 Samba是通过Network LAN 局域网来实现的 二 安装 要安装Samba 我
  • 架构是什么

    是什么 架构是什么 众说纷纭 架构 Architecture 一词最早源自建筑学术语 后来才被计算机科学领域借用 以下是其在维基百科 Wikipedia 中的定义 架构是规划 设计和构建建筑及其物理结构的过程与产物 在计算机工程中 架构是描
  • 苏嵌嵌入式Linux实训 第2天

    今天是嵌入式学习的第二天 也是正式教学开始的第一天 全程由梁老师为我们讲解 由于仅仅是新手的接触 不懂的地方还有很多 有时甚至跟不上老师的节奏 当然这也需要我们课后的复习和总结 不断积累才能不断收获 1 课程内容 今天的课程主要是对嵌入式开
  • 删除当前目录下所有.py[cdo]文件的命令

    rm f py 命令会删除当前目录下所有的 py 文件 但不会删除包含这些文件的目录 这个命令并没有错误 但是如果你只希望删除当前目录下的 py 文件而不包括子目录中的文件 你可以使用 rm f py cdo 在解释这个命令之前 首先来了解
  • Ubuntu 下安装 QQ

    安装流程 一 QQ Linux版本下载 二 安装 一 QQ Linux版本下载 1 使用以下指令查看自己的 Ubuntu 版本的类型 uname a 可查看到我的 Ubuntu 版本为 x86 64的版本 因此可以点击此处链接前往 QQ 官
  • Springboot + MySQL+ JPA II save方法详解

    JPA没有专门的update接口 save接口同时支持update操作 一 save 单条添加 Service层中添加save方法 save是三方件自带接口不需要再dao层中添加 Transactional public User save
  • Java:继承和多态

    继承 什么是继承以及为什么需要继承 继承机制 是面向对象程序设计是代码实现复用中至关重要的一步 它允许程序在保持原有类的特性的基础上来进行扩充 增加新功能等 总的来说 继承就是将不同类之间的共性进行抽取 抽取出来的这些共同的特性就可以单独写
  • Jupyter Notebook工具中ndarry数组的使用(一)

    今天学习了jupyter notebook工具中ndarray数组的使用 具体包括 第一步 导入numpy包 import numpy as np 第二步 创建ndarray数组 通过numpy模块中的常用的几个函数进行创建ndarray多
  • 月薪过万的Java面试

    写了一个月 篇幅太长了 都写不下了 被逼无奈 只能拆分 面试题 HashMap底层实现原理 红黑树 B 树 B树的结构原理 volatile关键字 CAS 比较与交换 实现原理 答案 理论 第一章 HashMap底层实现原理 红黑树 B 树
  • JAVA图片压缩

    图片压缩代码操作 需要压缩的原图片路径为 src 压缩后存放的路径为 dist 需要压缩的宽度为 width 需要压缩的后的高度为 height File srcfile new File src 原图片是否存在 if srcfile ex
  • python几个轻量级web框架

    我最近发表了一篇名为 7 Minimal Node js Web Frameworks for 2014 and Beyond 的博文 目前它是我博客访问量最高的文章 超过10000人浏览 分享和评论了这些我总结到一起的web框架 这教会了
  • 金晟富:4.17黄金冲高遇阻急需调整!后市黄金原油操作建议

    前言导读 各位投资朋友 转眼间又到周末了 祝大家周末愉快 此刻的你还在到处看文章找策略吗 不知晟富每日及时给到你的现价单你关注了多少呢 每天的多空是否让你犹豫再犹豫 一单损完又害怕下一单 如果你还在迷茫犹豫的道路上 不妨可留意下金晟富的文章
  • simulink中PID控制器搭建

    Simulink 是一个用于仿真 建模和仿真的软件工具 您可以在其中搭建 PID 控制器 以下是如何搭建一个简单的 PID 控制器的步骤 启动 Simulink 打开一个新模型 在模型窗口中插入一个 PID 控制器 模块 可以在 Simul
  • STM32 电机教程 1 - 用ST Motor Profiler 测量无刷电机参数

    前言 在对电机进行控制前 往往需要先知道电机的一些参数 但是在实际应用过程中 经常会出现在控制一个电机参 但对电机的参数如相电阻电感的参数不够了解的情况 本节给大家演示基本ST Motor Profiler测量电机参数的操作过程 让大家在以
  • Spring Cloud 与 Dubbo 区别

    1 定位点不同 SpringCloud SpirngCloud 定位为微服务架构下的一站式解决方案 Dubbo 关注点主要在于服务的调用 流量分发 流量监控和熔断 2 dubbo基于rpc 底层netty SpirngCloud基于http
  • 【点云处理之论文狂读前沿版7】—— Masked Autoencoders for Point Cloud Self-supervised Learning

    Masked Autoencoders for Point Cloud Self supervised Learning 摘要 1 引言 3 Point MAE 3 1 Point Cloud Masking and Embedding 3
  • Docker专题(八)-Docker-Docker部署SpringBoot项目

    1 手工方式 1 1 准备Springboot jar项目 将项目打包成jar 1 2 编写Dockerfile FROM java 8 VOLUME tmp ADD elk web 1 0 SNAPSHOT jar elk jar EXP
  • PhpStorm最全攻略

    本教程主要内容的是日常开发 测试 部署工作流的一些技巧和工具配置方法 并尽量将最有用的部分提取出来并结合实际场景做介绍 而并不是仅仅对PhpStorm的功能的简单罗列 如果读者有改善的建议 可以在教程下方留言或直接与作者联系 共同促进内容的
  • 工具:npm/node版本更换(Windows版本) 含报错exit status 1报错,出现乱码的解决方法

    npm版本更换 更换指定版本 npm g install npm 6 14 11 更换最新版本 npm install g npm node版本更换 方法1 下载nvm https www runoob com w3cnote nvm ma
  • 腾讯业务百万数据 6s 响应,APIJSON 性能优化背后的故事

    最近发生了一件大事儿 APIJSON 再也不用担心被人质疑性能问题了哈哈 某周三腾讯 CSIG 某项目组 已经用 APIJSON 做完一期 突然反馈了查询大量数据性能急剧下降的情况 某张表 2 3KW 记录 用 APIJSON 万能通用接口