项目优化

项目优化
Photo by Faheem Ahmed / Unsplash

我已经完全理解目前各个企业的hr或者项目组成员看着下面这份千篇一律的hmdp优化版简历已经头都麻了 完全挑不出来到底该面谁然后只能挑学历高的面。

在经过思考之后我感觉我的简历可以继续有几个优化点 然后能告诉他们 我更有一点点点点自己的思考 然后约面我 ;w; 我没招了 只感觉上不去下不来

本文优化项目后续会放到GitHub上,其实我也是依靠他人的基础继续优化。不过大家继续学习都是一样的,重点是有没有继续深挖的想法和思考业务的过程。

xhs上月如风的hmdp优化版简历

诚然 这一版hmdp确实是比一开始的好了很多:引入消息队列,拥抱llm和ai等的确是提升,但是大半年前够看的东西,再加上很多人直接照搬,直接让他成为了新版本的“基础”,也就是千篇一律。

实际上该项目可以往美团应用的方向去想,在实际的生产环境的业务中我们还需要什么?还可以加什么?如果这是现在项目的base,我能通过什么去优化来脱颖而出?

那我们要对这个项目动刀子了

关于一键部署

首先第一步,连一键部署都没有,在这边一个一个启动是在劝退人吗

站在用户角度看:
一个 web 应用如果连一键部署都没有,尤其还面向个人开发者、开源社区、小团队,那确实会显得有点难绷。不是技术不行,而是产品化意识不够

因为很多人看项目,不只看它能不能跑,还看:

  • 能不能快速跑起来
  • 出错后好不好排查
  • 新手会不会被环境折磨死
  • 部署流程是不是可复制

写了个很牛的系统,结果部署要手配 Nginx、手改 env、手建数据库、手跑迁移、手配 HTTPS、手处理跨域……那别人第一反应往往不是“这个项目真强”,而是

我为什么要给自己找罪受?;w;

那总之我们先整一个docker-compose.yml 这样才知道我做的是个产品 部署的中间也发现原项目的一点磕磕绊绊的问题 之后我会在理解好系统架构后继续学习k8s 在这里想到的面试题及一些简单操作命令放到最后。

services:
  mysql:
    image: mysql:8.0
    container_name: hmdp-mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      TZ: Asia/Shanghai
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./sql:/docker-entrypoint-initdb.d:ro
    command:
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"]
      interval: 10s
      timeout: 5s
      retries: 20

  redis:
    image: redis:7-alpine
    container_name: hmdp-redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: ["redis-server", "--appendonly", "yes"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 20

  kafka:
    image: apache/kafka:3.7.0
    container_name: hmdp-kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
    volumes:
      - kafka-data:/var/lib/kafka/data

  backend:
    build:
      context: .
      dockerfile: docker/backend.Dockerfile
    container_name: hmdp-backend
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
      kafka:
        condition: service_started
    environment:
      SPRING_PROFILES_ACTIVE: docker
      HMDP_IMAGE_UPLOAD_DIR: /app/data/imgs
      TZ: Asia/Shanghai
    ports:
      - "8085:8085"

  frontend:
    build:
      context: .
      dockerfile: docker/frontend.Dockerfile
    container_name: hmdp-frontend
    depends_on:
      backend:
        condition: service_started
    ports:
      - "5173:80"

volumes:
  mysql-data:
  redis-data:
  kafka-data:

关于分库分表

我感觉在这种业务之下,真实的数据量绝对是巨额的。那我们可以选择分库分表来避免一张表、一个库膨胀到一个不可观的大小。进一步说,分库分表的核心目的不只是“把数据拆开”,而是从容量和性能两个层面一起做治理。

首先是容量问题。像订单、用户、优惠券、秒杀记录这类核心业务表,随着时间增长会持续累积,单表一旦达到千万级、上亿级,索引体积会非常大,查询和写入都会越来越重,单机磁盘、内存、IO 压力也会被持续放大。把数据拆到多个库、多个表后,单库单表的数据规模就能被控制在更合理的范围内。

其次是性能问题。单表过大时,即使 SQL 本身写得没问题,也可能因为索引层级变深、热点页竞争严重、锁冲突增多,导致查询延迟和写入吞吐明显下降。通过水平分片,可以把压力打散到不同库表甚至不同机器上,让系统整体吞吐能力提升。

再往业务层看,分库分表通常不是随便拆,而是围绕访问模式设计分片键。比如按 user_id 分库,适合“按用户查订单”这种高频场景;按 voucher_id 分表,可以进一步分散券维度的并发热点。也就是说,分片规则本质上是在“数据分布均衡”和“核心查询路径命中效率”之间找平衡。

不过分库分表也不是没有代价。它会带来跨分片查询复杂、分页和聚合成本上升、扩容迁移困难、分片键一旦选错很难调整等问题。所以实际落地时,一般是当单库单表已经逼近容量或性能瓶颈,或者业务增长非常明确时,才会引入像 ShardingSphere 这样的中间件来统一管理逻辑表和真实库表映射。

而我目前为了学习已经引入了 ShardingSphere 这个中间件。它的好处在于,我不需要在业务代码里手动判断“这条数据该落哪个库、哪张表”,而是把分片规则统一配置在中间件层,由它在 JDBC 层完成路由、改写和执行。

具体来说,业务代码看到的仍然是逻辑表,比如 tb_user、tb_seckill_voucher、tb_voucher_order,开发方式和单库单表时基本一致;但在底层,ShardingSphere 会根据我配置的分库键、分表键和分片算法,把 SQL 自动路由到真实的库表,比如 hmdp_0.tb_voucher_order_0 或 hmdp_1.tb_voucher_order_1。这样做的优势是业务侵入性比较低,后续如果要调整分片规则,也主要是在配置层和数据层处理,而不是把大量分片逻辑散落在代码里。

在我这个项目里,它承担的核心职责主要有三块。第一是数据源统一管理,我把多个真实数据源交给它托管;第二是逻辑表到真实表的映射,比如配置实际节点 ds_${0..1}.tb_xxx_${0..1};第三是分片算法执行,比如对 ID 做取模、对手机号做哈希取模等。这样在应用层写 MyBatis 或 MyBatis-Plus 的时候,还是正常查逻辑表,中间件会把 SQL 自动展开成真实 SQL。

我引入它也是想更系统地理解分库分表在真实项目里的落地方式,而不是只停留在概念层。因为只会说“可以按用户 ID 取模分库”其实还不够,真正落地时还要考虑逻辑表和真实表的映射、分片键怎么选、广播表怎么处理、跨分片查询会发生什么、以及启动和运维阶段如何验证真实 SQL 是否路由正确。ShardingSphere 正好能把这些问题都串起来。

我感觉最有说法的就是在秒杀券这边,存订单逻辑的时候 用user_id来分库,用order_id来分表。前者保证根据用户查订单的时候不会打偏,减少跨库开销;后者保证查order的时候把数据打散,请求不会打在一张表,很有意思。这个设计体现的是:分片规则应该围绕业务访问路径和数据分布特征来定,而不是只看某个字段好不好取模。

关于简历描述

我去了你总不能改都不改直接照搬吧 那我第一个pass的就是你(没错就是我)

经历了STAR法则的修改之后 把简历搓出来了一点感觉 并且尽量缩减到一页 接下来要把我的blog塞进简历里了 不知道面试官看到这么多答辩是什么心情。。。

后续改进

直接尝试学习并且上es和优化llm这边的东西,会的多一点也没问题 坚持慢慢学习就好。

感觉本文内可以问的面试题 回答是GPT-5.4

Docker 的核心概念有哪些?
核心概念主要有四个:

  • Image:镜像,应用运行模板
  • Container:容器,镜像的运行实例
  • Volume:数据卷,用来持久化数据
  • Network:网络,用来让容器之间通信

镜像和容器有什么区别?
镜像是静态模板,容器是镜像启动后的运行实例。

  • 镜像不变,类似只读模板
  • 容器是运行态,有自己的进程、网络、可写层
  • 一个镜像可以启动多个容器

Docker 怎么实现隔离?
答:
底层主要依赖 Linux 的 Namespace 和 Cgroups。

  • Namespace 做资源隔离,比如进程、网络、挂载点
  • Cgroups 做资源限制,比如 CPU、内存
  • UnionFS 提供分层文件系统

Docker 容器之间怎么通信?
同一个 Docker network 下的容器可以通过服务名直接访问。

比如 Compose 里:

  • 后端访问 MySQL 可以直接用 mysql (3306)
  • 访问 Redis 可以用 redis (6379)

因为 Compose 会自动创建网络并提供服务名解析。

Docker 容器退出了,怎么排查?
一般按这个顺序:

  1. 看容器状态:docker ps -a
  2. 看日志:docker logs
  3. 看 Compose 日志:docker compose logs
  4. 看环境变量、端口、挂载、依赖服务是否正常
  5. 判断是应用异常、配置异常,还是中间件没起来


你在项目里是怎么用 Docker 的?
该项目用 Docker Compose 把 MySQL、Redis、消息队列、后端、前端一起编排起来。数据库数据目录挂 volume 保证持久化,后端通过服务名访问中间件,配合 healthcheck 保证依赖就绪后再启动应用。这样本地开发、测试环境和部署环境的一致性更好。

Docker常用命令

镜像相关

docker images
查看本地镜像

docker build -t myapp:latest .
根据当前目录 Dockerfile 构建镜像

docker rmi myapp:latest
删除镜像

docker pull nginx
拉取镜像

docker push myrepo/myapp:latest
推送镜像到仓库


容器相关

docker ps
查看运行中的容器

docker ps -a
查看所有容器,包括已退出的

docker run -d --name my-nginx -p 8080:80 nginx
后台启动一个容器并映射端口

docker stop my-nginx
停止容器

docker start my-nginx
启动已存在的容器

docker restart my-nginx
重启容器

docker rm my-nginx
删除容器

docker logs my-nginx
查看容器日志

docker logs -f my-nginx
持续跟踪容器日志

docker exec -it my-nginx sh
进入容器终端


数据卷相关

docker volume ls
查看 volume 列表

docker volume inspect mysql-data
查看某个 volume 详情

docker volume rm mysql-data
删除 volume


网络相关

docker network ls
查看网络

docker network inspect bridge
查看某个网络详情


系统清理相关

docker system df
查看 Docker 占用空间

docker system prune
清理未使用的资源

docker system prune -a
清理更多无用镜像和资源


Compose 相关

docker compose up
启动服务

docker compose up -d
后台启动服务

docker compose up --build
启动前先重新构建镜像

docker compose down
停止并删除容器、网络

docker compose down -v
停止并删除容器、网络、volume

docker compose stop
停止服务但不删除容器

docker compose start
启动已存在的服务容器

docker compose restart
重启服务

docker compose ps
查看 Compose 服务状态

docker compose logs
查看所有服务日志

docker compose logs -f backend
持续查看某个服务日志

docker compose exec mysql mysql -uroot -p
进入某个服务容器并执行命令


这几个最值得背

开发里最常用的一般是:

  • docker ps
  • docker logs -f <container>
  • docker exec -it <container> sh
  • docker build -t xxx .
  • docker compose up -d
  • docker compose up --build
  • docker compose down
  • docker compose down -v
  • docker compose ps
  • docker compose logs -f <service>

(以下为自己感悟)

个人感觉哈,在我使用的过程中最常用的肯定是build完start和stop、compose up/down

然后排问题就log 然后进终端exec

之前刚用这玩意的时候不用 docker-compose 还用 network 组 ;w;

哈哈。面试都没有。有面的话,估计没有很多面试经验一软脚,也讲不全讲不出来。想死。;w;


❤️
如果想和我联系请点About!! 请多多评论指点QAQ

Read more

Day4 / 5

这两天脑子昏昏的 感觉就是笔试题害怕出hot100之外然后写不上然后被卡 但是越刷脑子越昏越不想思考 是不是有点太累了 day4去了招聘会 感觉能碰到门槛的其他厂也挺不错的 好像不一定要冲很高。 想了一会打算回去更新一波简历让各位大佬能看到确实有不一样的地方 那就足够了 我的代码能力可能是真的很差?已经在保持手感了刷一次忘一次是真的很难过 我觉得更应该去理解开发流程和项目 正常开发时候代码量什么的也不会很爆炸...? 打算今天把脑子放松一会

By XnLemon
Day3

Day3

今日有点点小困倦 感觉现在就是vibe出项目的优化方向然后继续跟进思考学习,通过业务的不同去思考架构和选型的不同,让ai帮助我追问我,我继续追问他让我更能深入了解业务的实现。其实感觉这样学习挺好的,让我能更清楚目前努力优化哪个方向,为什么要这样优化,业务的边界在哪里,尽可能去兜底。 我个人简历上还能如何优化才能让各位面试官满意,在这种没有量化提升的情景下,我只能想到更多一点就会更好,所以我的下一版简历会挂上我的个人blog和将自己的项目描述的更产品一点,因为我认为最后都应该落实到生产上,我们应该知道一个项目全流程是什么样的,部署之后该如何维护也是要懂的,但是我是真的没有这种生产环境经验全靠猜测是真的很难绷,我很后悔没有一开始就选择开发这个方向。其实我觉得还是很有意思的。唉没有办法。 hot100精简版题解锐意制作中... https://www.yuque.com/xianing-ktxvd/izcbal/fx038xykolwav8as?singleDoc#

By XnLemon
Day2

Day2

今天下午被导员拉过去参加宣讲/招聘会(第二次了 我就不该信能有什么机会) 看到来学校春招的企业路边到让我昏头 又看到很多迷茫的大四学生 我很难过 这些企业 仅讲开发 都是很白菜很路边的 为什么大部分计算机系的大学生要被哄着过四年然后被推到这些地方 不得不讲的神秘教学方式(也有可能是春招的原因) 个人理解这种课程设计就是为了 让绝大部分人毕业 今日将项目二次优化 明天将优化后的推上去 去理解并找出业务中的关键才能进步 lc继续刷继续练 夏柠没水平这一块 明天lc + 八股 + 更新项目继续学习 争取早日进面拿经验

By XnLemon
Day1

Day1

今天是这个站建起来的第一天,我想记一些日记。 我直到这学期真正开始特别忙碌起来才觉得大学生活是充实的,在早九到晚六/晚八从图书馆回来之后娱乐一下躺在床上的那种感觉是神秘的,很可惜我直到现在才体会到。 今天下午做了一份笔试,手很生,撕过的树题没撕出来,差一题没进面,有点可惜?也许有点可惜,但是证明我还是不够强。只有真正体会过这种一直石沉大海的感觉才能感受到真没招了,我也不清楚我现在存在可量化的提升方向在哪里。明天应该去招聘会继续寻找机会和建议,争取本月找到可观的实习。 对于项目的理解貌似还是不够深刻 并且思考要不要光速换个更难的业务项目 MySQL的八股已经熟悉了很多,Redis的差一点,Kafka我是一点一点按着教程看完的,自认为差不多理解了它的许多特点,应该会再看一遍RocketMQ比较这两者的差别,其实感觉同为所谓消息队列 本质上两者差别还是挺大的。并且尝试在这台服务器上跑了个单机三节点伪集群也跑通了,也验证了一部分特性,也记录下来了,后续可以写一下。感觉自己其实过去一两个月浪费了很多时间,然后这段时间的成长是很快的,也许因为知道自己再不加把劲就完蛋了。只有晚上躺在床上的时候

By XnLemon