我的java开发测试
type: channel
link: https://www.ownsyou.cn/
description: Just So So ...
items: 不爱了，然后呢？
items: 我们为什么不爱了
items: Async注解与虚拟线程
items: OpenResty、Tengine、Nginx使用
items: 数据库访问五大核心技术
items: SpringBoot Jar包冲突在线检测
items: 高性能短链构建
items: 后端性能优化
items: GraalVM替代JVM的方式方法和不足
items: 怀化之旅-2025.5.5

不爱了，然后呢？
type: item
date: 2026-07-19T17:02:48+08:00
description: 这个板块主要提及的是自我重建，自我修复的部分。羞耻感一个人不被看见、不被接纳、不被爱会产生羞耻感。羞耻感是指：当个体意识到自己的行为、想法或自身特征违反了社会公认的道德准则、价值观，或是没达到自己内心期望的标准时，所产生的一种痛苦、难堪、甚至贬低自我的感受。与羞愧不一样，羞耻关乎“我是谁”这个问题，是做人的失败；而羞愧关乎“我做了什么”，是做事的失败。羞耻感会在亲密关系中出现，是因为权力不对等，强势方对弱势方的审视和排斥，通常是先有结论，再寻找或者杜撰各种证据和标准。强势方不断羞辱弱势方获得权力，弱势方害怕被伤害、被抛弃，就主动放弃自己的权益，竭力讨好强势方，试图获取强势方的“肯定”、“支持”或“恩宠”。羞耻感会让人产生回避型为和掩饰情绪：回避行为包括了回避他人和回避自我这两个方向，前者让人主动孤立自己，后者让人陷入各种成瘾行为。掩饰情绪则包括了攻击他人和攻击自我这两个方向，前者增加关系冲突，后者让人陷入抑郁。比如别人的感受和诉求永远是第一位的，自己总是可有可无的靠后。超越羞耻感要敢于直面它，对改变保持耐心，主动去面对羞耻感，重新修剪自己；用优势视角看待自己，意识到自身的清醒和勇气，主动发挥自己的优势；借助人际环境的力量，在与人的互动中，不要接近那些指责你不行，否定你的成就，将成就归因于运气，甚至要让你把信任全数建立在他身上的人。超越羞耻感就要一点点做自我尊重的事情，培养自己的高自尊。自尊作为名词时，是指的我们对自己自我价值的和能力的感觉，高自尊是指我们觉得自己是有价值的、有能力的、值得被爱的。而自尊作为动词时，是指我们对自己的尊重和喜欢，是觉得自己值得拥有想要的生活，也能用行动来争取这样的生活。在超越羞耻感时，要寻求帮助，自我挑战羞耻感，不要去相信别人合理化后的行为，要看到本质，不然羞耻感就会作为一种控制手段被利用。如果因为羞耻感，而无法在亲密关系中做真实的自己，那么就会给自己、伴侣和关系都带来困扰。培养自我需要树立信念，找到这份信念的来源和喂养这份信念的人、事、物，然后切断供养、消除不合理的信念，培养有利于自我的信念。只有当我们直面放下对”被爱“的幻想时，我们才会获得更多的改变力量。在这个过程中或许会遭受到很多挫折，因为当我们设定目标，并全力以赴时，其实这是一次冒险，冒险就意味着可能会失败，直面冒险带来的失败可能，和面对失败时的坚持，恰恰也是我们赢得自我尊重的最好的机会。成功超越羞耻感的标志是：不再用”是否足够成功、足够优秀“和”是否符合他人期待和外界的标准“来定义自我价值，而是纯粹能够因为”我存在“而感到安稳和喜悦。允许自己真实、允许自己不完美、允许自己偶尔笨拙和犯错，坚信真实的自己值得被爱和尊重。当下的社会环境对弱势群体并不友好，因为环境本身就在源源不断地催生羞耻感，环境告诉你”是因为你不好，所以你才得不到你想要的“，或者是”因为你不好，所以我才不给你你想要的“，但是很多时候，事实并非如此。我们无法在短时间内改变大环境，但是可以从对自己、对身边人更包容、更友善开始，努力塑造更好的环境——主动参与创造理想中的环境，这也是一种对羞耻感的超越。冲突冲突换个角度上来讲，其实是”爱正在存在“的迹象，也是”爱可以发展“的契机。因为在亲密关系中，冲突正是双方在争夺做自我的权力和空间。当一个人爱自己，就会更有勇气为了自己的权益面对冲突。当两个人都确信彼此相爱着，两个人也都会更有勇气直面冲突，因为双方知道这并不会立刻导致关系破裂，其实和爱是一种能力一样，冲突也是一种能力。好的冲突是双方表达真实想法和需求、增进彼此了解、改善关系质量的机会。一些人正是因为重视关系，希望通过解决问题来让关系更好、更稳固，于是主动挑起了冲突。很多时候，糟糕的并不是冲突本身，而是冲突发生时双方的处理方式。如果有更多的倾听和合作，那么就有机会共赢。即便有一方退让、做出了牺牲，彼此也有机会用新的事情弥补另一方，让感恩和公平得以在关系中存在。亲密关系中冲突的定义是指：当个体的动机、目标、信念、观点和行为妨碍别人或与别人矛盾，就会发生人机冲突。冲突产生于差异，既可能表现为一时的情绪，也可能表现为持久的信念和人格。对亲密关系的想法也会产生冲突，例如当对方有强烈的完美主义倾向，以理性和体面作为表现；而另一方觉得亲密关系是时刻和谐的，以感性和灵活作为表现；两者的差异就会产生冲突。不爱了确实会导致更多的冲突，因为不爱所以只想要自私地做自己，当两个人都这样，差异必然导致冲突。但是冲突本身不一定会导致不爱了，很多时候是破化性冲突中地指责和攻击扩大了关系的裂缝——比如“别烦了”这就是指责，遭受指责的一方既可能感觉自己不再被爱了，也可能在认同指责时的内容后变得“不爱自己”了，于是，不仅关系中的爱在消失，对自己的爱也在消失。指责是为了避责和控制，常见的有“你太敏感了”，这些指责隐藏着因果…

我们为什么不爱了
type: item
date: 2026-07-19T17:02:11+08:00
description: 《曹雪敏》的这本书很值得一品，很多视角都在引领我们从现代角度，贯穿原生家庭、性别偏见、权力关系这几个不同视角，从社会、父母到两性，诠释了为什么爱情变得困难，怎么样爱自己。正如作者所说：所有关系的背后，都是我们与自己的关系，先让自己成为发光的人，再考虑要不要投入一段感情。不爱了，正在成为一种时代情绪统计数据呈现出来的“不爱了”可能是一种主动的选择，是人自由意志的体现，不再盲目踏入爱情的陷阱，不再盲从“人生就要结婚生小孩”这样的传统剧本，这无疑是一种进步。原生家庭为什么我们不爱了，因为爱的主体是人，一个人的童年经历会影响他成年后的自尊、风险感知、择偶策略、养育后代的风格等。情感创伤：就是我们经历过的但是还没有释怀的失望与绝望。创伤会让人丧失对未来的想象力和创造力，使得大脑自动关闭了对其他可能性的想象和探索。如果以扭曲的自我概念和解释方式来理解自己和世界，以伤害性的模式和他人、和这个世界发生互动，那么创伤带来的影响是深远的。个人边界被侵犯，会破坏一个人的“公正世界假设”——不再认为世界是可预测的、可信任的，对世界没有安全感，就容易丧失好奇心和探索的勇气。而贫穷会放大所有危险因素带来的影响。如何改变这种状态：不再扮演受害者的角色，愿意自我负责。自我负责不是把一切结果都归于自己，也不是“只靠自己”来孤立地寻求改变，而是分离课题后，专注于自己的课题和目标。找到你的自由：分辨可以改变的和不能改变的，接纳改变不了的，改变可以改变的。模仿——要知道好的相处方式是怎么样的，并且勤加练习。接纳自己在成长过程中地反复，做好迎接挑战甚至痛苦的准备。改变不一定会让你收获大团圆结局，所以还要拥抱开放的结局。有时候，一段关系中如果的确没有爱，也的确没有爱的意愿和爱的能力，那么“不爱了”反而是“爱自己”的选择，“爱自己”才是每个人无论如何都要追求的过程和结局。性别偏见性别偏见：指的是基于性别的刻板印象和不公平对待，不仅是个人的观点或态度，更是根植于文化、传统、社会结构和制度之中的系统性问题。在性别偏见下，男性对女性的“爱”的体验和行为，也许只是一种情绪、一种冲突或者是一种策略；反之亦然，女性对男性的“爱”，或许是男性的外在、霸道、财富和地位等。当人看到对方的真实性，知道对方的伪装、自私、卑劣和怯懦之后，可能会打破爱情的滤镜，这正是这个时代单身人士正在经历的觉醒，而女性尤为强烈，因为其受到的压迫更现实。女性在原生家庭中感受不到爱与尊重，忽视对未来的长期规划，忙于照顾他人，而且受到母父的控制，重复其人生。而逃离原生家庭之后，在与男性的交往中，很可能只是进入了男性剥削女性的另一个环境。不少男性在“爱情”中其实一直在自私地权衡利弊，当然女性也有可能有，不过追求的结果不同。因为男性在社会环境中容易形成固定性思维，只愿意待在被认可地舒适圈中，往往害怕挑战，遇到问题选择逃避，甚至把质疑当成挑衅，倒打一耙。其实婚姻和爱情是分离的，婚姻本质上是法律制度和契约，两个人结婚，是自愿接受制度赋予地权益和责任，是个人和国家制度的合作，也是国家制度对个人的管控，整个婚姻法中没有一条关于爱情的条例。婚姻和爱情是两回事，爱情是个人意志，是自由的；而婚姻是现实，也是法律和社会关系，有相应的限制、规则和功能。爱是关系的动力和目标之一，但不是必须的。现实生活中的关系相处需要至少不冲突的关系目标、相应的能力和人格基础，这些反而是必须的。婚姻从进化心理学角度来看，其实一直是统治者更需要婚姻，因为这能稳定社群，这点与费孝通的《生育制度》有所契同。而这些目的没有一项和爱情相关。女性的心理问题，来源于实际体验到的痛苦和文化引导的“爱情带来幸福”的故事不一样；而男性，则是因为实际体验到的挫折，和一直被告知的“男性天赋异禀、无所不能”的故事不一样。婚姻不见得带来幸福，很多时候只是上位者的利益诉求罢了。人最终都要直面人性，靠自己积累权力、武器和钱，用它们来管理人性，赢得自由和幸福。但是是否就对婚恋完全失望呢？不见得，因为人终会走向死亡，在死亡这一终极的孤独和毁灭面前，爱与被爱始终是救赎。不平等问题的背后其实都是在涉及权力关系，而权力和偏见一样，都是爱的敌人。权力关系权力——一种可以自己执行后无需考量他者想法与感受的特殊能力。当关系变成了权力取向的关系时，则全是由高位者的意志来决定关系的发展，我们常说的共识、尊重、理解、沟通和欣赏，通常都发生在平等取向的亲密关系中。而权力取向的亲密关系中，控制和支配是主要的底色。高位者会持续改变低位者的思想和行为来满足自己，同时抵抗低位者施加的任何带有企图的影响，让低位者觉得不公平又无力。如果从政治角度思考，那么就是：人会致力于去做对自己有利的事情，而不是对他人有利的事情。也就是对方不会关注你的利益，换句话说：如果他怎么对你，你都不会离开，甚至会继续对他好，那么他为什么要用行动爱你…

Async注解与虚拟线程
type: item
date: 2025-09-22T09:56:21+08:00
description: Async注解与虚拟线程现代Spring应用程序都面临着同样的难题：如何在不消耗过多CPU资源或阻塞线程的情况下，快速运行大量任务？提到并发处理，我们经常听到三个概念：虚拟线程、线程池和 @Async注解。实际上这三者是完全不同的东西，起作用的地方也不同：• 虚拟线程（Virtual Threads）：JVM的一种执行模型• 线程池（Thread Pools）：资源管理器• @Async注解：Spring的任务路由工具，将工作分发给你选择的执行器理解它们的区别对于应用的延迟、云成本和可靠性至关重要。选择错误可能导致P95延迟飙升、队列堆积和超时问题。虚拟线程虚拟线程它们在阻塞I/O操作时能够快速地地暂停和恢复。核心优势• 高并发：可以创建数百万个虚拟线程• 低成本：内存占用极小（约8KB vs 传统线程的2MB）• 高效I/O：在I/O阻塞时自动让出CPU资源适用场景// 适合：大量I/O密集型任务 @Async public CompletableFuture<String> fetchDataFromAPI(String url) { // 网络请求、数据库查询等 return restTemplate.getForObject(url, String.class); }线程池线程池是一种资源管理策略，通过预先创建固定数量的线程来处理任务，避免频繁创建和销毁线程的开销。配置示例@Configuration public class AsyncConfig { @Bean(name = "taskExecutor") public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(50); executor.setQueueCapacity(100); executor.setThreadNamePrefix("async-task-"); executor.initialize(); return executor; } }@Async注解@Async是Spring提供的任务路由器，它将方法调用转发给指定的执行器处理。基本用法@Service public class UserService { @Async("taskExecutor") public CompletableFuture<User> processUser(Long userId) { // 异步处理逻辑 User user = userRepository.findById(userId); // 耗时操作... return CompletableFuture.completedFuture(user); } }配置虚拟线程执行器要使用虚拟线程功能，需要满足以下版本要求：• Java 21+：虚拟线程正式版本• Spring Boot 3.2+：完整支持虚拟线程配置• Spring Framework 6.1+：底层框架支持@Configuration @EnableAsync public class VirtualThreadConfig { @Bean(name = "virtualThreadExecutor") public TaskExecutor virtualThreadExecutor() { return new TaskExecutorAdapter( Executors.newVirtualThreadPerTaskExecutor() ); } @Bean(name = "fixedThreadPool") public TaskExecutor fixedThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20); executor.setMaxPoolSize(40); executor.setQueueCapacity(200); executor.setThreadNamePrefix("fixed-pool-"); executor.initialize(); return executor; } }Service中使用@Service public class DataProcessingService { // 使用虚拟线程处理I/O密集型任务 @Async("virtualThreadExecutor") publ…

OpenResty、Tengine、Nginx使用
type: item
date: 2025-09-18T11:33:13+08:00
description: OpenResty、Tengine、NginxOpenResty官网地址：https://openresty.org/cn/OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台，其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。OpenResty® 通过汇聚各种设计精良的 Nginx 模块（主要由 OpenResty 团队自主开发），从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样，Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块，快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部，充分利用 Nginx 的非阻塞 I/O 模型，不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应OpenResty其实就是一个基于Nginx的Web平台，但它最牛的地方在于集成了LuaJIT和很多精心编写的Lua库。简单来说，就是让你可以在Nginx里面写Lua代码，这听起来可能没什么，但用过的人都知道这有多香。比如，你产品经理突然要求在用户访问某个页面时，根据用户的地理位置显示不同的内容。如果用传统的方式，要么改后端代码重新发布，要么搭建一套复杂的CDN规则。但用OpenResty，我直接在nginx.conf里写了几行Lua代码就搞定了：location /api/content { access_by_lua_block { local ip = ngx.var.remote_addr local geo = require "resty.geoip" local country = geo.lookup(ip) if country == "CN" then ngx.var.backend = "china_backend" else ngx.var.backend = "global_backend" end } proxy_pass http://$backend; }这种灵活性是原生Nginx根本做不到的。你想想，如果每次这种小需求都要动用整个开发流程，那得多麻烦。OpenResty的安装也很简单，在Rocky Linux release 8.10上直接用yum就行：sudo yum install yum-utils sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo sudo yum install openresty装完之后，你会发现它的目录结构和Nginx差不多，配置文件还是在/usr/local/openresty/nginx/conf/nginx.conf。但是多了很多Lua相关的模块，比如lua-resty-redis、lua-resty-mysql这些，基本上常用的都给你准备好了。#这是我修改后的文件 [root@webtest ~]# cat /usr/local/openresty/nginx/conf/nginx.conf worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location / { default_type text/html; content_by_lua_block { ngx.say(" hello, world ") } } } }Tengine官网：https://tengine.taobao.org/Tengine是阿里巴巴开源的Web服务器，基于Nginx开发。说到阿里的技术，那肯定是在高并发场景下被虐过千百遍才开源出来的，所以在性能和稳定性方面确实有两把刷子。安装官网下载编译安装包，上传到服务器[root@webtest ~]# tar -xf tengine-3.1.0.tar.gz [root@webtest ~]# cd tengine-3.1.0 [root@webtest ~]# ./configure [root@webtest ~]# make [root@webtest ~]# make installTengine最让我印象深刻的是它的动态模块加载功能。以前…

数据库访问五大核心技术
type: item
date: 2025-09-16T16:19:26+08:00
description: 数据库访问五大核心技术简介在项目开发时，数据库访问技术是必不可少的，它直接决定了数据持久化的效率、开发维护的便捷性以及系统整体的性能表现。本文对比五种主流数据库访问技术：JOOQ 以类型安全的 SQL 和数据库优先设计著称，适合复杂查询与性能敏感场景，但需接受代码生成和商业授权限制；MyBatis 通过轻量级框架平衡了 SQL 控制与开发效率，支持动态 SQL 和多数据库适配，但需手动管理 SQL 与事务，类型安全较弱。此外，其他常见技术如 JDBC（原生但冗余）、Hibernate/JPA（全自动化 ORM 但学习曲线陡峭）、Spring Data JDBC/JPA（简化 CRUD 但灵活性受限）也各有适用场景。JDBC技术JDBC（Java数据库连接）是Java中为数据库连接提供基础支持的核心API。它是一个底层API，可对SQL执行和结果处理提供直接控制。可以把JDBC看作是Java数据库编程领域的“汇编语言”——功能强大，但需要更多手动操作。如下示例：private final DataSource dataSource ; public User queryByJdbc(Long id) { String sql = "SELECT * FROM a_user WHERE id = ?"; try (Connection conn = this.dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setLong(1, id); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { return mapRowToUser(rs); } } } return null; } private User mapRowToUser(ResultSet rs) throws SQLException { User user = new User(); user.setId(rs.getLong("id")); user.setName(rs.getString("name")); // ... return user; }JDBC 适用场景性能关键型应用：需要最大程度控制 SQL 执行，以优化性能复杂查询：ORM 框架难以表达的复杂 SQL 查询遗留系统集成：需要与现有存储过程（Stored Procedures）交互简单数据访问的微服务：数据访问模式简单，无需 ORM 框架的额外开销。学习目的：理解数据库底层原理和 JDBC 核心机制JDBC 优势最大性能控制：直接执行 SQL，避免 ORM 框架的额外开销完整 SQL 功能支持：可使用数据库特有的优化和功能（如特定语法、索引提示）轻量级：内存占用小，启动速度快行为可预测：编写的 SQL 会被精确执行，无 ORM 的隐式行为通用兼容性：适用于所有支持 JDBC 的数据库（MySQL、Oracle、PostgreSQL 等）JDBC 劣势冗余代码多：需要手动处理连接、语句（Statement）和结果集（ResultSet）的创建与关闭手动对象映射：需自行将数据库行（Row）转换为 Java 对象（POJO）SQL 注入风险：若未正确使用参数化查询（PreparedStatement），易受攻击数据库移植性问题：原生 SQL 可能在不同数据库间不兼容（如分页语法差异）无内置缓存：需自行实现缓存策略（如使用 Redis 或本地缓存）注：Spring中，JdbcTemplate与JdbcClient（Spring 6+）通过模板化设计，将原生JDBC的冗余代码量降低70%，实现高效数据库访问。Hibernate技术Hibernate 是一个成熟的对象关系映射（ORM）框架，它通过面向对象编程的方式抽象化数据库操作。该框架能够自动处理 Java 对象与数据库表之间的映射关系，为面向对象开发者提供了一种更符合自然编程习惯的模型。private final SessionFactory sessionFactory ; public User queryByHibernate(Long id) { try(Session session = sessionFactory.openSession()) { User user = session.find(User.class, id) ; return user ; } }我们还可以使用HQL语句进行查询User user = session.createQuery("FROM User WHERE id = ?1", User.class) .setPa…

SpringBoot Jar包冲突在线检测
type: item
date: 2025-09-16T15:49:00+08:00
description: SpringBoot Jar包冲突在线检测1. 痛点背景在 Spring Boot 项目开发和运维中，Jar 包冲突是让开发者最头疼的问题之一：常见冲突场景类重复：不同依赖引入了相同的类，启动时报 ClassCastException 或 NoSuchMethodError版本冲突：同一个库的不同版本混用，行为不一致，线上才暴露问题日志混乱：SLF4J + Logback + Log4j 多个实现共存，日志输出异常驱动重复：MySQL 5.x 和 8.x 驱动同时存在，连接异常现有方案的局限性• mvn dependency:tree：只能编译期分析，无法反映运行时 classpath• IDE 插件：需要人工操作，无法自动化集成• 第三方工具：过重，难以嵌入 Spring Boot 应用考虑引入一个轻量、可嵌入、运行时可见的 Jar 包冲突检测工具。2. 技术方案设计核心架构运行时扫描 → 冲突检测 → 配置化建议 → Web 可视化 ↓ ↓ ↓ ClassLoader 规则引擎 模板系统 适配器 智能分析 变量替换 关键技术点1. 多环境 ClassLoader 适配public List<URL> getClasspathUrls() { ClassLoaderclassLoader= Thread.currentThread().getContextClassLoader(); List<URL> urls = newArrayList<>(); // 遍历所有 ClassLoader 层级 ClassLoadercurrent= classLoader; while (current != null) { if (current instanceof URLClassLoader urlClassLoader) { urls.addAll(Arrays.asList(urlClassLoader.getURLs())); } current = current.getParent(); } // Spring Boot LaunchedURLClassLoader 特殊处理 if (classLoader.getClass().getName().contains("LaunchedURLClassLoader")) { urls.addAll(extractFromLaunchedClassLoader(classLoader)); } return urls.stream().distinct().toList(); }2. 三维冲突检测算法// 类重复检测 Map<String, List<JarInfo>> classToJarsMap = newHashMap<>(); for (JarInfo jar : jars) { for (String className : jar.getClasses()) { classToJarsMap.computeIfAbsent(className, k -> newArrayList<>()).add(jar); } } // 版本冲突检测 Map<String, List<JarInfo>> nameToJarsMap = jars.stream() .collect(Collectors.groupingBy(JarInfo::getName)); // JAR 重复检测（基于签名） Map<String, List<JarInfo>> signatureMap = jars.stream() .collect(Collectors.groupingBy(this::generateJarSignature));3. 配置化规则引擎完全摒弃硬编码，通过 YAML 配置定义所有规则：conflict: advisor: rules: slf4j-logging: patterns: [".*slf4j.*", ".*logback.*", ".*log4j.*"] severity:HIGH advice:|3. 核心实现Jar包扫描器支持开发环境和生产环境的智能扫描：@Component publicclassJarScanner { public List<JarInfo> scanJars() { List<JarInfo> jars = newArrayList<>(); List<URL> urls = classLoaderAdapter.getClasspathUrls(); for (URL url : urls) { Stringpath= url.getPath(); if (shouldExclude(path)) continue; if…

高性能短链构建
type: item
date: 2025-09-10T08:43:57+08:00
description: 高性能短链构建短链必须性这里列举3个原因来讲一下短链存在的必要性。相对安全短链不容易暴露访问参数，生成方式可以完全迎合短信平台的规则，能够有效地规避关键词、域名屏蔽等风险，而原始 URL地址，很可能因为包含特殊字符被短信系统误判，导致链接无法跳转。平台限制短信发送平台有字数限制，在整条短信字数不变的前提下，把链接缩短，其他部分的文字描述就能增加，这样似乎更能达到该短信的实际目的（比如，营销）。短链构成短链的组成通常包含两个部分：域名 + 随机码https://www.ownsyou.com/s2TYdWd短链的域名最好和其他业务域名分开，而且要尽量简短，可以不具备业务含义（比如：xyz.com），因为短链大部分是用于营销，可能会被三方平台屏蔽。(非营销无需考虑这个问题)短链的随机码需要全局唯一，建议10位以下。短链跳转原理首先，我们先看一个短链跳转的简单例子，如下代码，定义了一个 302重定向的代码示例：import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.servlet.view.RedirectView; @Controller publicclass RedirectController { @GetMapping("/{shortCode}") public RedirectView redirect(@PathVariable String shortCode) { String destUrl = "https://yuanjava.com"; // destUrl = getDestUrlByShortCode(shortCode); //真实的业务逻辑 returnnew RedirectView(destUrl); }接着，在浏览器访问短链”http://127.0.0.1:8080/s2TYdWd” 后，请求会被重定向到 https://yuanjava.com 从ws抓包可以看到 302状态码并且请求被 Location到另外一个 URL，整个交互流程图如下：用户通过短链访问到短链系统，然后通过302重定向到目标系统最后，总结下短链跳转的核心思想：生成随机码，将随机码和目标 URL（长链）的映射关系存入数据库；用域名+随机码生成短链，并推送给目标用户；当用户点击短链后，请求会先到达短链系统，短链系统根据随机码查找出对应的目标 URL，接着将请求 302重定向到目标 URL（长链）；关于重定向有 301 和 302两种，如何选择？302，代表临时重定向：每次请求短链，请求都会先到达短链系统，然后重定向到目标 URL（长链），这样，方便短链系统做一些统计点击数等操作；通常采用 302301，代表永久重定向：第一次请求拿到目标长链接后，下次再次请求短链，请求不会到达短链系统，而是直接跳转到浏览器缓存的目标 URL（长链），短链系统只能统计到第一次访问的数据；一般不采用 301。短链生成从短链组成章节可知：短链=域名+随机码，因此，如何生成短链的问题变成了如何生成一个全局唯一的随机码，通常会有 3种做法：Base62Base62 表示法是一种基数为62的数制系统，包含26个英文大写字母（A-Z），26个英文小写字母（a-z）和10个数字（0-9）。这样，共有62个字符可以用来表示数值。如下代码：import java.security.SecureRandom; publicclass RandomCodeGenerator { privatestaticfinal String CHAR_62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; privatestaticfinal SecureRandom random = new SecureRandom(); public static String generateRandomCode(int length) { StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; i++) { int rndCharAt = random.nextInt(CHAR_62.length()); char rndChar = CHAR_62.charAt(…

后端性能优化
type: item
date: 2025-09-05T17:03:34+08:00
description: 后端性能优化MySQL主要针对索引，对于MySQL优化的博文已经很多了，可以参阅，这里主要讲关于索引的几个点。索引：MySQL的所有C端查询我们要尽量保证必须用到索引，尤其是那种数据量增长快的表。所以不要在索引字段进行函数运算，不要使用取反等等。联合索引：联合索引使用可能是最多的了，sql没写对可能就导致了全表扫描，记住最左匹配原则，将区分度高的字段放在前面。分页查询：C端经常展示一些列表，比如文章列表，作品列表等，基本都用到了分页查询，这里需要注意的是order by fieldName。要保证fieldName在索引中，不然就可能会出现filesort了，这是个外排序，在数据量大的时候是很致命的。在我们这个项目中，我没说的时候，所有的分页查询都有这个问题。养成个好习惯：创建表的时候就设计好索引，不要到最后出事了再去加，那会数据量很大再加就迟了。分库分表一般预估年增量记录在500W以内都不需要分，我们之前是1000W以内就不分。如果只是临时做活动使用的就更不需要分了，除非活动期间量真的达到了几亿。简单介绍下分的原则数据量大就分表，否则单表的查询性能肯定是下降很多并发高就分库，因为单库的资源就那么多，支持的连接数不会太高。大多数情况下分库分表是一起的，因为量大和并发高经常是同时出现。一开始分就给足资源，二次分表成本太大，在快手一般上来就是10个库和1000张表。需要特别注意的是shardKey，围绕用户展开的就用userId来做shardKey，这在绝大多数情况下没问题的。如何分，见仁见智，可以使用shardingjdbc，优点很明显是减少开发成本。也可以自己手动分，优点也很明显，便于定制化和制定规则。如果你犹豫不决就shardingjdbc吧，尤其是小团队的时候。注意事项1、减少操作数据库次数比如我要批量更新用户的奖励，有人会写这样的代码for (UserReward r : rewardList) { rewardDao.update(r); }如果rewardList的size=10,那会操作数据库10次，也就是10次IO，而明显我们可以一次批量更新解决：rewardDao.batchUpdate(rewardList);虽然看着是那么理所当然，我们的项目甚至不少地方有下面类似的代码User user = userService.getById(userId); // doSomething userService.updateById(user); // doSomething2 userService.updateById(user); // doSomething3 userService.updateById(user);明明可以一次update就解决的为什么要update多次？引申一下，有的时候甚至都不需要操作数据库，这种情况很多时候不容易发现。举个例子，之前活动的时候在活动页面会展示任务信息和用户获得的奖励。// 查询用户任务 UserTask userTask = userTaskService.getById(id, userId); // 构建外显响应 UserTaskResp userTaskVo = buildVo(userTask); // 从结算表查询结算信息，从而得知获得的奖励 UserTaskSettlement s = userTaskSettlementService.getByTaskId(userTaskId, userId); // 填充奖励信息 fillReward(userTaskVo, s);活动页的流量是很大的，平白无故增大了结算表的流量。因为大部分用户是完不成任务的，所以根本没有获得奖励，所以可以判断用户是否完成了任务，只有完成了任务才去查询结算表即可。使用迭代器查询如果我们要查询的数据量很多，比如瓜分活动需要查出来所有参与并完成活动的用户，假设就一张表，有2000W数据，满足要求的用户可能有百万。如何做？直接一个批量查询的sql？肯定是不行的会出现包溢出异常一次查询大量数据到内存，会占用大量内存甚至full gc。正确的做法是分批查询，比如每次查询200条，然后进行处理。当然，不介意写while这种循环，一来不优雅，二来容易写出死循环的代码，最后也没法复用。可以参考下面的迭代器写法import com.google.common.collect.AbstractIterator; import org.apache.commons.collections.CollectionUtils; import java.util.Iterator; import java.util.List; import java.util.function.BiFunction; impo…

GraalVM替代JVM的方式方法和不足
type: item
date: 2025-06-03T16:03:26+08:00
description: GraalVM替代JVM的方式方法和不足GraalVM 基本上可以看成是Oracle大力发展的新一代JVM，但是目前的使用情况还不大乐观，而且逐渐发现大家都有往GO语言转的趋势，本文具体分析一下替代方案和不足之处，为什么大家还是更希望使用GO。GraalVM与JVM关系JVM 全称 Java 虚拟机，我们都知道，Java 程序是运行在虚拟机上的，虚拟机提供 Java 运行时，支持解释执行和部分的（JIT）即时编译器，并且负责分配和管理 Java 运行所需的内存，我们所说的各种垃圾收集器都工作在 JVM 中。比如 Oracle JDK、OpenJDK ，默认的 JVM 是 HotSpot 虚拟机。GraalVM 可以完全取代上面提到的那几种虚拟机，比如 HotSpot。把你之前运行在 HotSpot 上的代码直接平移到 GraalVM 上，不用做任何的改变，甚至都感知不到，项目可以完美的运行。GraalVM主要想搭建一个Framework，最终目的是实现支持任何一种语言，可以共同跑在 GraalVM 上，不存在跨语言调用的壁垒。GraalVM 和JDK关系Java 虚拟机都是内置在 JDK 中的，比如Orcale JDK、OpenJDK，默认内置的都是 HotSpot 虚拟机。GraalVM 也是一种 JDK，一种高性能的 JDK。完全可以用它替代 OpenJDK、Orcale JDK。GraalVM 如何运行 Java 程序GraalVM - 还包含 Graal （JIT）即时编译器，可以结合 HotSpot 使用GraalVM – 是一种高性能 JDK，旨在加速 Java 应用程序性能，同时消耗更少的资源。GraalVM - 是一种支持多语言混编的虚拟机程序，不仅可以运行 JVM 系列的语言，也可支持其他语言。GraalVM 提供了两种方式来运行 Java 程序。第一种：结合 HotSpot 使用GraalVM 包含 Graal （JIT）即时编译器，自从 JDK 9u 版本之后，Orcale JDK 和 OpenJDK 就集成了 Graal 即时编译器。这样Java 既有解释运行也有即时编译。当程序运行时，解释器首先发挥作用，代码可以直接执行。随着时间推移，即时编译器逐渐发挥作用，把越来越多的代码编译优化成本地代码，来获取更高的执行效率。即时编译器可以选择性地编译热点代码，省去了很多编译时间，也节省很多的空间。比如多次执行的方法或者循环、递归等。JDK 默认使用的是 C2 即时编译器，C2是用C++编写的。而使用下面的参数可以用 Graal 替换 C2。-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompilerGraal 编译器是用 Java 实现的，用 Java 实现自己的编译器。Graal 基于一些假设的条件，采取更加激进的方式进行优化。采用 Graal 编译器之后，对性能有会有一定的提升。但是Graal，VM不支持JDK8第二种：AOT 编译本地可执行程序AOT 提前编译，是相对于即时编译而言的。AOT在运行过程中耗费 CPU 资源来进行即时编译，而程序也能够在启动的瞬间就达到理想的性能。例如 C 和 C++语言采用的是AOT静态编译，直接将代码转换成机器码执行。而 Java 一直采用的是解释 + 即时编译技术，大多数情况下 Java 即时编译的性能并不比静态编译差，但是还是一直朝着 AOT 编译的方向努力。但是 Java 对于 AOT 来说有一些难点，比如类的动态加载和反射调用。GraalVM 的 AOT 编译实际上是借助了 SubstrateVM 编译框架，可以将 SubstrateVM 理解为一个内嵌精简版的 JVM，包含异常处理，同步，线程管理，内存管理（垃圾回收）和 JNI 等组件。SubstrateVM 的启动时间非常短，内存开销非常少。除了运行时占用的内存少之外，用这种方式最终生成的可执行文件也非常小。这对于云端部署非常友好。目前很多场景下都使用 Docker容器的方式部署，打一个 Java 程序的镜像包要包含完整的 JVM 环境和编译好的 Jar 包。而AOT 方式可以最大限度的缩小 Docker 镜像的体积。缺点对于反射这种纯粹在运行时才能确定的部分，不可能完全通过优化编译器解决，只能通过增加配置的方式解决。多语言支持GraalVM 中的另一个核心组件 Truffle是一个用 Java 写就的语言实现框架。基于 Truffle 的语言实现仅需用 Java 实现词法分析、语法分析以及针对语法分析所生成的抽象语法树（Abstract Syntax Tree，AST）的解释执行器，便可以享用由 Truffle 提供的各项运行时优化。就一个完整的 Truff…

怀化之旅-2025.5.5
type: item
date: 2025-05-09T09:25:00+08:00
description: 怀化之旅-2025.5.5今天的路线是玉皇宫——人民公园——市政府——市民广场——回家。起床！今天的早餐时白糖包和香菇干笋包（好吧，这个也放了辣椒哈哈哈）玉皇宫早上来参拜一下玉皇宫，据说这是这边最大的道教宫殿，还蛮有名气，其实这个确实挺好看的，当然师傅们可能就是香火比较少，所以遇到每个人都会问是不是来拜拜的。我碰到了一对情侣也是来参观的，他们不好意思，我就和师傅说了，我们仨都是来参观的，师傅作了作揖，也很愉快地让我们进去了，只是特地要求按照规矩来，不要走中间，左右两侧进入。山大门在修缮，有很多神像都在修：玉灵官、哼哈二将、青龙、白虎、朱雀、玄武、龙王、武陵山神、城隍爷。这里就不给大家放神像的图片了，因为还在修复中，他们估计也不太愿意让人见到这个模样。踏着阶梯上来可以在这里拍到很不错的一个整体景色可以看到对面的小区，也可以看到怀化市的风景。这里你要说百家姓呢，倒也不一定，主要还是怀化这边的姓氏，类似于祠堂一样的感觉吧，不过确实供奉了一些神像例如女娲、炎帝、黄帝，所以也算是比较有意思的宫殿。财神殿的话也是肯定会有的了。如果你去佛教的话一般就是地藏王在右侧，然后左侧珈蓝关公，所以怀化的玉皇宫是比较纯粹的道教了。走，去市政府如果开电动可能更快，但是我不急，在这里和一个老奶奶聊起了天。对方捡到了一张身份证，还问我能不能帮忙送给他，因为是沅陵县的实在太远了，而且我要返程了，我看了下失主年龄不算很大，估计会补办的，就让她别再担心了。老奶奶问我哪里人，我说广东的，她说她有个佛山亲戚清明节带他们一起去佛山玩了。但是由于熟悉这里的环境，觉得还是回来比较舒服。她还问我来这里干嘛，是来出差吗？哈哈哈，我看起来很像来出差的吗？我记得我搭高铁来的时候，隔壁坐的怀化阿姨也问我是不是来出差，可能怀化这座城市还是更偏向做生意吧。离市政府最近的地方其实是妇幼保健院，所以就在这里下车了。老大爷们富有生活气息地在这里唱歌，搞艺术创作。说实话，我觉得古城还不如人民公园，这里跑跑步放松放松，看看山水也是很不错的。整个怀化地政务都在这一圈了，有不同的业务局，例如住建局啊等等。因为想去法院看看，所以得去以前的市民广场，就在这里等公交，当然实际上要去的是“市委”站。法院怀化蛮神奇的，真的有很多广东肠粉，粤菜也不少，有很多早茶，虽然正宗不正宗我没试过，但是总归可能和广东的情谊也蛮深。怀化最近都在宣传这个，其实你在哪里都能看到。茶颜悦色要离开怀化回广州了，离开前再来尝试喝一杯茶颜悦色吧，这次我要喝声声乌龙了。谢谢某人推荐，很好喝。在这里打车回酒店收拾东西，打车正好遇到了拼车的，遇到了个带小孩的母亲。我也不是很急，所以我就跟司机说可以的，没问题。我觉得母亲带小孩还蛮幸苦，司机还一直跟我说现在的年轻人比较自私自我，我觉得吧应该要分情况考虑吧，也希望大家能多给这个社会一些包容的机会。候车厅好多人啊！回到广州了。