新闻详情

新闻详情

首页 / 资讯中心 / 详情

高并发DAO层压测实践:连接数管控、错峰访问与并行限流

发布时间:2026/9/30 15:23:20来源:尧图网络
高并发DAO层压测实践:连接数管控、错峰访问与并行限流
前几天帮团队做了一轮DAO层压测遇到的问题特别典型线程池线程数一抬到200数据库连接池就开始抛无法获取连接的异常紧接着一堆查询超时、事务回滚最后MySQL直接报Too many connections。排查了一圈问题不在SQL本身而是根本没管好连接数、访问节奏和并发闸门这三件事。这篇文章就把这一轮踩坑和实践沉淀下来的方案完整拆开讲核心就三条连接数管控、错峰访问、并行限流目标是让DAO层在高并发测试下既能把压力打足又不至于把数据库和连接池搞崩。如果你正被并发一高就连接超时测试经常误杀生产库之类的问题折磨或者想搞清楚HikariCP参数到底怎么定、压测流量到底该怎么发出去这篇文章应该能给你一套立即可用的思路和代码。1. 高并发下DAO层测试的痛点到底在哪1.1 你以为的问题往往不是你以为的很多人做DAO层并发测试第一反应是SQL写得太慢、索引没建好于是把注意力全放在EXPLAIN和索引优化上。但真实场景里高并发下DAO层最先崩的往往不是SQL执行耗时而是连接获取这个入口环节。一次查询本身可能只要50毫秒但如果线程都在排队等连接等待时间可能被拉到1秒、2秒甚至直接超时。换句话说瓶颈往往不在数据库执行端而在于应用和数据库之间的这层管道。我见到过一个典型的错误做法压测脚本里用固定线程池以最大速率疯狂提交查询结果连接池被瞬间打满大量请求在connectionTimeout超时后抛异常。测试结论直接变成这个DAO撑不住并发但实际上SQL本身一点问题没有。这就是没有把资源管理纳入测试设计导致的误判。真正合理的做法是先管住连接、再管住流量节奏、最后管住并行度让压力测试的压力真正作用在目标代码上而不是堆积在资源抢占上。1.2 这篇文章要解决的三件事基于上面的痛点整篇文章围绕三条主线展开连接数管控理解连接池参数的含义用合理的配置把数据库连接当成和线程池一样需要显式编排的资源来管理。错峰访问让测试流量不再瞬间全部涌向数据库而是模拟真实用户访问的随机性和分批特征。并行限流用信号量、令牌桶等手段给并发请求装上阀门让压测的并发度精确可控。这三件事不是独立的实际操作中通常是组合使用的先定好连接池上限再设计错峰策略最后用限流机制兜底。接下来逐个拆解。2. 连接数管控数据库连接是硬资源不是无限续杯2.1 先搞懂连接池的几个核心参数连接数管控的第一步是搞清楚你用的连接池到底有哪些旋钮。目前Java生态里最主流的连接池是HikariCPSpring Boot 2.x以上默认集成。它的核心参数远不止maximumPoolSize和minimumIdle这两个我列一下实测中影响最明显的几个参数默认值作用测试时的设置建议maximumPoolSize10连接池最多持有的连接数结合数据库max_connections和压测并发度计算minimumIdle等于maximumPoolSize空闲时保底连接数建议先等于maximumPoolSize减少频繁建连connectionTimeout30000ms获取连接的最大等待时间压测场景建议缩小到3000~5000ms快速暴露问题idleTimeout600000ms空闲连接存活时间仅在minimumIdle小于maximumPoolSize时生效maxLifetime1800000ms连接最大存活时间必须小于数据库wait_timeout否则会被DB端回收validationTimeout5000ms连接校验超时默认即可不建议小于1000ms这里最容易被忽略的是maxLifetime和数据库wait_timeout之间的关系。MySQL默认wait_timeout是8小时但很多云数据库会设置为10分钟甚至更短。如果连接池里的连接maxLifetime比数据库的断开时间还长就会出现一个诡异的现象测试刚开始一切正常跑了一会儿突然大量报通信链路异常其实连接早被数据库那边回收了连接池还在傻傻地复用死连接。2.2 怎么确定合理的连接数连接数的设置没有绝对标准但有一个从HikariCP官方文档延伸出来的思路可以借鉴池大小 Tn × (Cm - 1) 1其中Tn是数据库能同时处理的查询数Cm是单个连接上能并行的查询数大多数数据库是1PG在某些场景下可以大于1。对MySQL来说这个公式简化下来就是你期望的同一时刻在执行的SQL数量1。但在压测场景里我更喜欢反过来算。先看数据库侧的max_connections是多少比如MySQL默认151线上可能调到500。然后从业务侧算应用实例数×每个实例的maximumPoolSize一定不能超过数据库max_connections的70%左右。剩下的份额要留给运维、备份、监控这些管理连接。举个例子单实例应用数据库max_connections200那连接池maximumPoolSize最多设到140左右再留点余量设120反而是更理性的选择。这里有个反直觉的点连接池不是越大越好。连接数开得过大数据库侧要维护的连接上下文就多每次查询的开销反而变大。而且在线程数固定的情况下多余的连接只会闲置没有实际意义。真正的关键指标是活跃连接数/最大连接数的比值压测时要盯这个值是否逼近上限。注意如果你用的是Spring Boot配置文件里spring.datasource.hikari.connection-timeout等参数的单位是毫秒别把30000写成30那意味着30毫秒就超时了。这种单位错误我见过不止一次。2.3 实测HikariCP配置与监控下面给出一份我在压测环境里实际用过的HikariCP配置以Spring Boot的application.yml为例spring: datasource: hikari: # 核心最大连接数结合DB max_connections的70%计算 maximum-pool-size: 120 # 最小空闲连接数压测初期建议等于最大值避免频繁建连 minimum-idle: 120 # 获取连接超时压测场景缩短避免线程无限等待 connection-timeout: 5000 # 连接最大存活时间必须小于数据库wait_timeout max-lifetime: 300000 # 空闲超时minimumIdle等于maximumPoolSize时此参数不生效 idle-timeout: 600000 # 连接有效性校验超时 validation-timeout: 3000配置完成后重点不是盯着配置看而是看运行时指标。HikariCP自带Metrics可以接入Micrometer和Prometheus但我压测时最常用的是两个土办法第一个是直接查MySQL状态。压测过程中反复执行SHOW STATUS LIKE Threads_connected观察这个值和连接池maximumPoolSize的关系。如果Threads_connected持续接近甚至超过max_connections说明连接数还是没有真正管住需要继续下调连接池或者收紧并发。第二个是看应用侧的连接池活跃数。如果你用了Spring Boot Actuator直接访问/metrics/hikaricp.connections.active或者在代码里用HikariDataSource的getActiveConnections()方法实时打印。压测时写一个定时任务每5秒输出活跃连接数和等待获取连接线程数这个数据比任何日志都直接。2.4 连接数管控的常见误区误区一连接池配置改完就完事。实际上每次压测都要先确认数据库max_connections没被其他任务占用否则你这边刚把连接池调到150那边一个数据同步任务就把连接数占满了。误区二为了稳妥把连接池调到很小。有人为了防止打爆数据库直接把maximumPoolSize设成5。结果是并发了20个测试线程90%的时间都在等连接压测变成了一场排队演习根本测不出DAO层的真实性能。误区三忽略连接池预热。连接池刚启动时是空的第一次高并发冲进来所有线程同时去创建新连接反而会让数据库承受一次连接风暴。压测前我会用一个预热任务并发地把连接池填满让minimumIdle真正生效后再开始正式测试。3. 错峰访问用抖动分批模拟真实流量3.1 为什么要规划访问节奏真实业务里的流量永远不是绝对整齐的千军万马同时冲锋。用户访问有随机性、有操作间隔数据库承受的是有起伏但整体平滑的负载。但很多压测脚本是循环里直接发请求100个线程同一毫秒全部打出去这相当于让数据库在最差情况下试运行。用这种方式测出来的最低性能当然有参考价值但它完全忽略了真实场景里的流量整形效应。错峰访问要解决的就是这个问题把集中的流量尖峰打散让请求以更接近真实业务的方式落到数据库上。这么做有两个好处一是避免瞬间把连接池打满导致连锁雪崩二是能更真实地考察DAO层在持续压力下的表现而不是只看那一瞬间的峰值。3.2 Jitter模式给请求加上随机延迟最简单的错峰手段是加随机延迟也就是Jitter。做法是每个线程在发请求之前先随机睡一段时间这个随机值落在某个区间内。比如基础延迟50毫秒抖动范围200毫秒那么每个请求实际延迟在50到250毫秒之间随机分布。// 错峰访问的核心Jitter随机延迟 private void jitterBeforeRequest() throws InterruptedException { // 基础延迟 随机抖动模拟用户思考时间 long baseDelay 50L; long jitterRange 200L; long delay baseDelay ThreadLocalRandom.current().nextLong(jitterRange); Thread.sleep(delay); }这个随机延迟的存在会让请求到达数据库的时间变得错落有致而不是整整齐齐地排成一堵墙。别小看这几十毫秒的随机性它对连接池的冲击是几何级下降的。批量场景里1000个请求如果同时到达连接池要瞬间创建或排队1000个连接请求但如果均匀分布在1秒内到达数据库每秒只需要处理1000/秒左右的请求连接池的排队压力完全不同。3.3 分批错峰模拟业务高峰期的流量特征Jitter适合平滑整体流量但有些业务场景的流量本身就是分批的。典型的例子每天早上九点打卡系统大量用户集中访问但每个人的操作时间是错开的。模拟这种场景光靠全局随机还不够需要按批次组织请求。分批错峰的做法是把测试时间分成多个时间窗口每个窗口内启动固定数量的线程窗口之间留出间隔。这样流量就会呈现阶梯上升、持续稳定、阶梯下降的形态更接近真实业务峰值曲线。可以配合ScheduledExecutorService实现// 分批错峰调度器每500ms释放一批请求 ScheduledExecutorService scheduler Executors.newScheduledThreadPool(1); AtomicInteger batchIndex new AtomicInteger(0); // 共10批每批50个并发 int totalBatches 10; int batchSize 50; for (int batch 0; batch totalBatches; batch) { scheduler.schedule(() - { for (int i 0; i batchSize; i) { executor.submit(() - daoMethod()); } }, batch * 500L, TimeUnit.MILLISECONDS); }分批错峰还有一个隐藏的好处它给连接池留出了呼吸空间。每批请求发起后连接池有时间完成连接的分配和释放不会出现上一批请求还没处理完、下一批又涌进来的情况。这在测试DAO层长事务时尤其重要——长事务本身就占着连接不释放再叠加无限涌入的流量几乎必崩。3.4 错峰访问的边界什么时候不该用错峰访问不是万能的。如果你的测试目标就是验证极限并发下系统能否扛住那这时候反而应该去掉所有延迟和分批让所有流量同时打进去专门制造最极端的冲击。错峰访问最有价值的场景是压测回归、容量评估、稳定性测试。它测的是系统在接近真实负载下的表现而不是极限承压能力。实际操作中我会做两轮测试第一轮用同时冲锋模式测出上限看看极限值是多少第二轮用错峰访问测出真实负载下的稳定性看看持续运行一段时间会不会出问题。这俩是互补的不是替代关系。4. 并行限流高并发测试的刹车系统4.1 Semaphore最直接的并发闸门错峰访问解决了请求何时发出的问题并行限流解决的是同时有多少请求在飞的问题。很多时候我们需要精确控制并发度比如最多允许50个线程同时去查数据库。这时候Java自带的Semaphore就是最趁手的工具。Semaphore本质是一个计数器acquire()拿信号量release()还回信号量。它可以把任意数量的线程闸在门外只放行固定数量的并发// 用信号量控制最大并发数为50 Semaphore concurrencyGate new Semaphore(50); public void queryWithLimit(Long userId) { try { concurrencyGate.acquire(); // 进入临界区此时最多50个线程同时执行 orderDao.queryByUserId(userId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 必须在finally中释放否则异常时信号量丢失 concurrencyGate.release(); } }用Semaphore做并发控制的精髓在于finally里释放信号量这一点和锁的释放一样重要。一旦有线程在临界区内抛异常而没释放信号量可用信号量就永久少了一个并发度会持续下降测试结果越来越失真。这是我在代码评审里一定会盯的地方。4.2 令牌桶平滑突发流量Semaphore能限制并发度但无法限制请求速率。假设每个请求耗时10毫秒50个并发意味着每秒最多5000个请求这个速率可能还是太高。如果想要每秒最多1000个请求这种更细粒度的控制就要用到令牌桶算法。Guava的RateLimiter是Java生态里最常用的令牌桶实现它的create方法传入每秒发放的令牌数acquire方法会阻塞等待令牌// 令牌桶限流每秒最多放行200次访问 RateLimiter rateLimiter RateLimiter.create(200.0); public void queryWithRateLimit(Long userId) { // 等待获取令牌这里可以设置超时兜底 rateLimiter.acquire(); orderDao.queryByUserId(userId); }RateLimiter和Semaphore的区别一句话概括Semaphore管同时有几个RateLimiter管每秒几个。实际压测里两者经常叠着用外层信号量控制并发窗口内层令牌桶控制请求速率双保险。比如一种经典的组合配置并发上限50每秒请求上限200。即使某个操作执行得特别快请求速率也不会失控即使某个操作执行得特别慢并发窗口也不会无限积压。4.3 把它们组合到一套压测用例里前面三样工具Jitter、Semaphore、RateLimiter单独说都很简单真正的价值在于组合。下面给出一套我实际使用过的DAO层压测骨架代码把三者融合在一起public class DaoStressTest { // 全局并发闸门最多同时处理50个请求 private final Semaphore concurrencyGate new Semaphore(50); // 全局速率闸门每秒最多200个请求 private final RateLimiter rateLimiter RateLimiter.create(200.0); // 测试总请求数 private static final int TOTAL_REQUESTS 2000; Test public void stressTest() throws InterruptedException { // 用固定线程池承载测试流量线程池大小要大于并发闸门数 ExecutorService executor Executors.newFixedThreadPool(100); CountDownLatch finishLatch new CountDownLatch(TOTAL_REQUESTS); for (int i 0; i TOTAL_REQUESTS; i) { final Long userId generateUserId(i); executor.submit(() - { try { // 第一层错峰访问添加随机延迟 jitterBeforeRequest(); // 第二层获取令牌控制请求速率 rateLimiter.acquire(); // 第三层获取信号量控制并发窗口 concurrencyGate.acquire(); try { // 真正的DAO调用 OrderDao dao ApplicationContextHolder.getBean(OrderDao.class); dao.queryByUserId(userId); } finally { concurrencyGate.release(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { finishLatch.countDown(); } }); } // 等待全部请求完成60秒超时兜底 boolean completed finishLatch.await(60, TimeUnit.SECONDS); executor.shutdown(); Assert.assertTrue(测试线程未在60秒内完成, completed); } }这套结构的核心思想是先通过错峰访问让流量分布更自然再通过速率闸门限制单位时间内的请求总量最后通过并发闸门限制同时执行的请求数。三层叠加之后压力测试变成了受控条件下的高强度验证而不是失控状态的雪崩模拟。4.4 限流参数怎么定限流参数不是拍脑袋定的需要从业务指标倒推。假如你的目标是验证系统能否支撑每秒500笔订单那令牌桶的速率就该设在略高于500的位置比如550留出一点余量但又不至于完全无压力。并发闸门的数值通常来源于两个约束数据库连接池的可用连接数以及每个请求的平均执行耗时。一个粗略的估算方式是并发数 目标QPS × 单请求平均耗时。如果目标QPS是500单请求平均耗时20毫秒那么并发数大约就是10。单纯把并发开满而不考虑目标QPS很多时候是在空转。另一个实践技巧是从松到紧进行标定第一轮先不设限流测出系统的自然最大值第二轮把限流参数压到这个自然最大值的70%~80%观察系统在有压力但不至于崩溃的状态下的表现。这个状态往往才是线上真正会经历的。5. 测试数据准备与隔离并发测试最容易翻车的地方5.1 并发下的数据污染问题连接数管控、错峰访问、并行限流解决的是资源层的问题。但做DAO层高并发测试时还有个高频翻车点测试数据互相干扰。最典型的场景是多个线程同时查同一张表其中一个线程把某条记录的状态从待支付改成了已支付其他线程读到旧数据或者因为数据状态变更导致SQL匹配不到记录最后抛出一堆莫名其妙的空结果和断言失败。这时首先要把它和环境问题区分开来先确认是数据被改了而不是查询逻辑有并发bug。我见过有人把测试断言失败当成DAO的并发bug排查了两天最后发现是测试数据本身被别的线程动了。所以在压测开始前一定要做数据隔离规划。5.2 按线程维度切割数据数据隔离的常用做法是按线程ID或参数范围切割。比如每个测试线程只操作user_id % 线程数对应的那部分数据或者给每个线程分配独立的业务主键范围。这样即使并发很高线程之间也不会操作同一批数据。代码层面可以这样处理// 按线程号切割数据范围避免并发线程互相污染 ExecutorService executor Executors.newFixedThreadPool(threadCount); for (int threadIndex 0; threadIndex threadCount; threadIndex) { final int index threadIndex; executor.submit(() - { // 每个线程只处理自己负责的userId范围 long startUserId index * dataSizePerThread; long endUserId (index 1) * dataSizePerThread; for (long uid startUserId; uid endUserId; uid) { orderDao.queryByUserId(uid); } }); }按范围切割数据的方案简单可靠但需要测试数据本身是预先准备好的而不是实时生成的。压测前我通常会先在库里灌一批测试数据总数据量约为实际需求的1.5倍留出一些余量防止边界索引问题。5.3 事务边界管理DAO层测试还有一个经常被忽略的细节事务边界。很多人认为DAO层的方法本身不带Transactional所以测试时不需要考虑事务。但实际上如果你的测试方法或测试类上挂了Transactional比如为了自动回滚而DAO层内部又依赖数据库的autocommit这两者会互相干扰。典型的坑是测试方法标记了Transactional压测过程中1000个并发线程共用一个事务上下文结果数据库连接从始至终被同一个线程占用着释放不了。这比连接池耗尽更隐蔽因为连接池活跃数看起来很正常但整个压测实际上变成了串行执行。我的建议是DAO层压测不要用Transactional让每个调用按照真实的autocommit模式独立提交。如果确实需要清理测试数据用独立的清理脚本在测试结束后跑而不是依赖事务回滚。6. 常见问题与排查技巧实录6.1 连接池活跃数只增不减大概率是连接泄漏压测过程中如果发现HikariCP的活跃连接数持续上升即使压测并发已经稳定活跃数还在往上爬八成是代码里有连接没归还。排查思路是这样一条链路先通过SHOW PROCESSLIST看数据库侧是否有大量Sleep状态的连接再在应用侧打印当前活跃连接和调用栈定位到是哪个DAO方法获取了连接但没有释放。这里我强烈建议一个实践压测前给连接池配上泄漏检测。HikariCP虽然不像某些连接池那样内置leakDetectionThreshold但你可以通过设置泄漏阈值来辅助判断。更直接的方式是压测结束前打印一次连接池状态如果在确认无并发请求后活跃连接数仍然大于0就说明有连接泄漏// 压测结束后检查是否有连接泄漏 HikariDataSource dataSource ApplicationContextHolder.getBean(HikariDataSource.class); int activeConnections dataSource.getHikariPoolMXBean().getActiveConnections(); Assert.assertEquals(存在连接泄漏活跃连接数未归零, 0, activeConnections);6.2 大量Connection is not available异常这个异常的本质是connectionTimeout内没有拿到连接。排查时要分两个方向一是连接池确实被打满了二是某个慢查询长时间占着连接不释放。看异常出现的时间点能区分如果测试一开始就报偏向前者如果跑了一段后才开始报更可能是某个SQL越跑越慢大量连接被慢SQL占住。前者通过降低并发或增大连接池解决后者则要回头优化SQL和事务粒度。实测中还有个容易误导的现象日志里先是Connection is not available过了几秒又冒出很多Communications link failure。这不是两个问题而是同一个根因的连锁反应连接池满员导致获取超时超时后某个线程直接关闭了连接数据库侧对应的会话被中断其他正在使用该物理连接的请求就会收到通信链路异常。所以看到这类异常组合时先查连接池而不是去查网络。6.3 maxLifetime设置不合理导致的周期性波动如果你发现压测过程中的QPS曲线呈现规律的锯齿形波动每隔一段时间就出现一次低谷然后又恢复正常很可能和maxLifetime有关。连接池里的连接在同一时间批量被回收导致周期性出现连接数量不足。解决办法是让maxLifetime带一个随机偏移HikariCP其实默认会往maxLifetime上加一个最大2.5%的随机值来避免这个问题但如果你手工把maxLifetime设得和数据库wait_timeout非常接近随机偏移的空间就没了仍然可能出现批量回收。稳妥的做法是把maxLifetime设为数据库wait_timeout的60%~70%。假设wait_timeout是10分钟maxLifetime设为6分钟左右就够了。另外还需要预留连接重建的时间避免回收一批建一批的节奏互相叠加。6.4 MySQL连接数被占满后运维连接都进不去这是最危险的场景。压测时如果连接池参数设置失控把数据库的max_connections全部占了DBA想连上去查状态都连不进去。我有一次压测就遇到过SHOW PROCESSLIST都执行不了最后只能重启数据库实例。所以压测前一定要强制留出管理连接额度。MySQL的保留连接是通过官方设计预留的实际实践中可以这样做在应用侧把最大连接数压到max_connections的70%以内同时在数据库侧配置足够的空闲超时确保压测结束后连接能快速释放。更重要的是压测脚本里必须有一个紧急刹车机制设定一个连接池活跃数告警阈值达到阈值时自动暂停后续请求。用代码实现的话可以在压测循环里检查活跃连接占比超过90%就Thread.sleep等待一段时间再继续。7. 这轮实践沉淀下来的几个心得这三套方法单独拿出来都不算高深技术但组合起来效果确实明显。我在团队落地这套方案后压测时的连接超时异常几乎消失了测试结论也更能反映DAO层的真实水平。有几点实操心得想分享一下。第一个心得是连接数管控要先算后配不要照搬网上的模板值。每个环境的数据库max_connections、应用实例数、预期QPS都不一样照搬配置等于没配置。花10分钟算清楚目标并发、单请求耗时、数据库连接上限之间的关系比盲目调参要高效得多。第二个心得是错峰访问和并行限流不是给弱鸡系统准备的正式压测里它们反而是精确控制变量的工具。控制了流量到达的节奏和并发窗口测试结果才具备可复现性。同样是测出200 QPS一次是在随机抖动下测出来的一次是固定速率怼出来的前者更接近上线后的真实表现。第三个心得是关于测试代码的维护把连接池参数、并发数、速率、错峰范围全部抽取成配置项每次压测只需要改配置不用改代码。我在项目里就是这么做的压测前的准备时间从半小时缩短到五分钟。后来这套配置模型也被用到了线上容量评估里每次发版前都能快速验证DAO层是否出现性能回退。最后说一个小技巧压测完成后不要急着收工把连接池活跃数、等待获取连接的线程数、数据库Threads_connected这几个指标拉出来和压测曲线对齐看一遍。很多时候性能瓶颈的线索就藏在并发请求数已经下降了但连接池活跃数还在高位徘徊这种细节里。能观察到这层信息的团队才算是真正把连接数管控和压测这件事做透了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Cortex-M IAP升级死机根源:VTOR向量表重映射硬规则 2026/9/30 21:58:19

Cortex-M IAP升级死机根源:VTOR向量表重映射硬规则

1. 项目概述:为什么IAP升级后单片机一进中断就死机?这不是玄学,是VTOR踩了硬件铁律“iap boot里面定义的变量复位后会怎样”——这个问题在嵌入式论坛里每年至少被问八百遍,但真正能答到点子上的人不到一成。我带过的三个应届生&a…

阅读更多 →
STM32CubeMX从下载到生成代码:嵌入式新手避坑指南 2026/9/30 21:58:18

STM32CubeMX从下载到生成代码:嵌入式新手避坑指南

1. 为什么我劝你别再手写STM32初始化代码第一次接触STM32的人,十有八九都经历过这样的场景:翻着几百页的参考手册,对着时钟树图发呆,好不容易把RCC配置寄存器一个个填完,结果串口就是不出数据。更崩溃的是,…

阅读更多 →
2026.1.9:VSCode集成claude插件完美方案,把settings.json改到TaoToken,用Kimi K2计费 2026/9/30 21:57:33

2026.1.9:VSCode集成claude插件完美方案,把settings.json改到TaoToken,用Kimi K2计费

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
把论文里的数据,画成一眼能懂的图 2026/9/30 21:56:22

把论文里的数据,画成一眼能懂的图

凌晨一点,论文正文已经写到讨论部分,真正卡住人的却不是文字,而是一张图:实验数据放进去之后,到底该用柱状图、折线图,还是散点图?图做得太简单,结论不突出;图做得太复杂…

阅读更多 →
第7章:RAGFlow Chat 助手创建与提示词配置 2026/9/30 21:55:56

第7章:RAGFlow Chat 助手创建与提示词配置

1 项目背景 业务场景 HR 制度问答机器人上线一个月后,「云帆科技」的不同部门开始提需求了。财务部说:"我们的报销制度能不能也搞个问答?"行政部说:“办公用品申领流程能不能也接进去?“但每个部门对机器人…

阅读更多 →
TikTok数据分析工具怎么选?6款定价实测+TaoToken配置CLI/MCP接入 2026/9/30 21:55:50

TikTok数据分析工具怎么选?6款定价实测+TaoToken配置CLI/MCP接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉