C++ 全栈知识体系C++ 全栈知识体系
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
  • 开源项目

    • libco

      • libco - 协程学习
    • ButtonRPC

      • ButtonRPC - rpc基础
      • ButtonRPC - 框架解析
      • ButtonRPC - 编程实现
    • Tars

      • Tars - 简介
      • Tars - 框架学习
      • Tars - Cpp开发
      • Tars - Go开发
      • Tars - Docker部署
      • Tars - Gateway部署
  • 学习项目

    • hmdp(Redis实战项目)

      • hmdp - 概览
      • hmdp - 短信登录
      • hmdp - 商户查询缓存
      • hmdp - 秒杀优化
      • hmdp - 分布式锁
      • hmdp - Redission
      • hmdp - Redis消息队列
      • hmdp - 优惠卷秒杀
      • hmdp - 附近商户
      • hmdp - UV统计
      • hmdp - 用户签到
      • hmdp - 好友关注
      • hmdp - 达人探店
    • SSM(Spring+SpringMVC+MyBatis)

      • SSM - Spring框架学习
      • SSM - IOC/DI配置管理第三方bean
      • SSM - Spring整合
      • SSM - SpringAOP
      • SSM - SpringMVC 基础
      • SSM - SpringMVC 应用
      • SSM - Maven高级
      • SSM - SpringBoot
      • SSM - MyBatisPlus
  • 个人项目

    • person - 概述
  • 项目开发

    • 项目开发 - C++开源项目推荐
    • 项目开发 - 整体开发流程
    • 项目开发 - 优化项目内容
    • 项目开发 - 注意事项
    • 项目开发 - 统一建模语言UML类图
  • Idea

    • Idea - 思维框架图
    • Idea - 常用技术检索
    • Idea - 小技巧tips
    • Idea - 编程中的tips

Idea - 编程中的tips

github网页查看方式

首先进入 https://github.com/klc407073648/cpp-notes

然后,键盘输入句号 。

编程注意事项

  • 引入一个库时,先写测试类,即小模块功能验证后使用。例如,使用POCO库的LocalDateTime等
  • 缓存的使用,一定要设置过期时间,不能无限期地使用,得到资源释放,重复利用的功能。
  • 分布式锁等复杂逻辑,尽量采用现有的框架实现,不要自己写!!!
  • 工作规划 ———— 写文档,列出需求背景、需求分析、设计方案选择、具体实现(库表设计、增删改查、业务逻辑(PO,重点需求))、以及各部分工作时间的安排
  • 为什么需要请求参数包装类(DTO)?
    1. 请求参数名称 / 类型和实体类不一样
    2. 有一些参数用不到,如果要自动生成接口文档,会增加理解成本
    3. 对个实体类映射到同一个对象
  • 为什么需要包装类?
    • 可能有些字段需要隐藏,不能返回给前端。或者有些字段某些方法是不关心的

数据查询慢

用缓存:提前把数据取出来保存好(通常保存到读写更快的介质,比如内存),就可以更快地读写。

缓存

作用

提前把数据取出来保存好(通常保存到读写更快的介质,比如内存),就可以更快地读写。

分类

  • Redis(分布式缓存)———— 本质**:key-value存储系统**
  • memcached(分布式)
  • Etcd(云原生架构的一个分布式存储,存储配置,扩容能力)
  • 本地缓存(内存 Map)

设计缓存key

在项目使用过程中,往往多个模块都会用到缓存功能,可以使用 systemId:moduleId:func:options 来区分。

举例:

  • yupao:user:recommed:userId
  • hmdp:user:login:token

redis 内存不能无限增加,一定要设置过期时间!!!

缓存预热

问题:第一个用户访问还是很慢,也能一定程度上保护数据库

优点:

  1. 解决上面的问题,可以让用户始终访问很快

缺点:

  1. 增加开发成本(需要额外的开发、设计)
  2. 预热的时机和时间如果错了,有可能你缓存的数据不对或者太老
  3. 需要占用额外空间

怎么缓存预热?

  1. 定时
  2. 模拟触发(手动触发)

实现

用定时任务,每天刷新所有用户的推荐列表

注意点:

  1. 缓存预热的意义(新增少、总用户多)
  2. 缓存的空间不能太大,要预留给其他缓存空间
  3. 缓存数据的周期(此处每天一次)

分析优缺点的时候,要打开思路,从整个项目从 0 到 1 的链路上去分析

控制定时任务的执行

前文提及定时任务的执行,需要进行额外的控制

  1. 浪费资源,想象 10000 台服务器同时 “打鸣”
  2. 脏数据,比如,定时任务用于插入数据,会引发重复插入

要控制定时任务在同一时间只有 1 个服务器能执行

  1. 分离定时任务程序和主程序,只在 1 个服务器运行定时任务。————成本太大

  2. 写死配置,每个服务器都执行定时任务,但是只有 ip 符合配置的服务器才真实执行业务逻辑,其他的直接返回。————成本最低;但是服务器的 IP 可能是不固定的,把 IP 写的太死了。

  3. 动态配置,配置是可以轻松的、很方便地更新的(代码无需重启),但是只有 ip 符合配置的服务器才真实执行业务逻辑。

    • 可以采用
      • 数据库
      • Redis
      • 配置中心(Nacos、Apollo、Spring Cloud Config)
    • 问题:服务器多了、IP 不可控还是很麻烦,还是要人工修改
  4. 分布式锁,只有抢到锁的服务器才能执行业务逻辑。

    • 坏处:增加成本,例如Redis服务器成本,人工编码成本;
    • 好处:不用手动配置,多少个服务器都一样。
  5. Zookeeper 实现(不推荐)

注意事项**:单机就会存在单点故障。**

锁

有限资源的情况下,控制同一时间(段)只有某些线程(用户 / 服务器)能访问到资源。

  • Java 实现锁:synchronized 关键字、并发包的类 ———— 问题:只对单个 JVM 有效
  • C++ 实现锁:进程中启动多线程,通过标志位控制 ———— 问题:只对单个服务进程有效

分布式锁

为什么需要分布式锁?

  1. 有限资源的情况下,控制同一时间(段)只有某些线程(用户 / 服务器)能访问到资源。
  2. 单个锁只对单个 JVM / 单个进程 有效

分布式锁实现的关键

抢锁机制

如何保证同一时间只有 1 个服务器能抢到锁?

核心思想 :先来的人先把数据改成自己的标识(服务器 ip),后来的人发现标识已存在,就抢锁失败,继续等待。等先来的人执行方法结束,把标识清空,其他的人继续抢锁。

实现的几种方式

  • MySQL 数据库:select for update 行级锁(最简单),但是数据库操作时间会慢一些

*(乐观锁)

  • ✔ Redis 实现:内存数据库,读写速度快 。支持 setnx、lua 脚本,比较方便实现分布式锁。
    • setnx:set if not exists 如果不存在,则设置;只有设置成功才会返回 true,否则返回 false

注意事项

  1. 用完锁要释放(腾地方)

  2. 锁一定要加过期时间

  3. 如果方法执行时间过长,锁提前过期了?

    • 问题:
      • 连锁效应:释放掉别人的锁
      • 这样还是会存在多个方法同时执行的情况 ​ * 解决方案:续期
boolean end = false;

new Thread(() -> {
    if (!end)}{
    续期
})

end = true;
  1. 释放锁的时候,有可能先判断出是自己的锁,但这时锁过期了,最后还是释放了别人的锁

    // 原子操作
    if(get lock == A) {
        // set lock B
        del lock
    }
    

    Redis + lua 脚本实现

  2. Redis 如果是集群(而不是只有一个 Redis),如果分布式锁的数据不同步怎么办?

    • 参考文章Redisson--红锁(Redlock)--使用/原理

分布式锁的实现

  • 拒绝从0到1自己写,一般而言不够高效
  • 采用现成的库,功能齐全,而且即使有BUG,也会持续修复

Redisson 实现分布式锁

特点:

  • Java 客户端,数据网格
  • 实现了很多 Java 里支持的接口和数据结构

Redisson 是一个 java 操作 Redis 的客户端,提供了大量的分布式数据集来简化对 Redis 的操作和使用,可以让开发者像使用本地集合一样使用 Redis,完全感知不到 Redis 的存在。

种引入方式

  1. spring boot starter 引入(不推荐,版本迭代太快,容易冲突)
    • redisson-spring-boot-starter
  2. 直接引入:
    • [redisson]https://github.com/redisson/redisson#quick-start

定时任务 + 锁

  1. waitTime 设置为 0,只抢一次,抢不到就放弃
  2. 注意释放锁要写在 finally 中

看门狗机制

redisson 中提供的续期机制

开一个监听线程,如果方法还没执行完,就帮你重置 redis 锁的过期时间。

原理:

  1. 监听当前线程,默认过期时间是 30 秒,每 10 秒续期一次(补到 30 秒)
  2. 如果线程挂掉(注意 debug 模式也会被它当成服务器宕机),则不会续期
  • Redisson 分布式锁的watch dog自动续期机制
Last Updated:
Contributors: klc407073648
Prev
Idea - 小技巧tips