电商项目缓存问题的解决方案(初步)

news2024/10/2 12:18:38

内容分类

容量规化

架构设计

数据库设计

缓存设计

框架选型

数据迁移方案

性能压测

监控报警

领域模型

回滚方案

高并发

分库分表

优化策略

负载均衡

软件负载

nginx:它自身的高可用是用lvs去保证。

下单需要登录 > 需要Session > 分布式Session的会话问题 >

​ 解决方案:

​ 1、ip哈希进行路由,某个人的ip只会路由到一台服务器,从而避免了分布式session的问题。【但是某台单点故障了呢?或者是某台容量上限了呢?】

​ 2、web服务器的tomcat之间的session相互同步(tomcat内含广播机制)问题【耗内存,耗宽带】

​ 3、session放入到redis中即可【引入缓存技术,同时需要维护redis的高可用】

硬件负载

数据库优化

1、换数据库 mysql oracle tidb redis

​ 读多写少(访问商品时) 》 redis搜索 》 es内存搜索(全量同步和增量同步)

2、分库分表(读写分离)

​ jdbc应用层:shardingSphere 、 tddl 【java应用层-》请求哪台数据库。性能较好,毕竟少了一次通讯;但是依赖于jar包,依赖于java语言】

​ proxy代理层:mycat、mysql-proxy 【中间代理层-》请求哪台数据库。与语言无关】

3、内部本身优化(字段、视图、索引、参数调优等)

应用程序优化

拆分服务

​ 目前都是通过增加机器来解决的。但是有的应用程序模块不需要这么多的机器,造成了机器的浪费。但是有个应用程序这些增加的机器又不够,所以得把应用程序拆开,拆分为服务后独立部署,最大效率的利用资源。

​ 会员部门新增需求上线成功,商品部门修改的bug上线失败,需要进行回滚。

​ 【1个服务变成了多个服务之后,服务之间的依赖调用就会增多。】

​ 【使用RPC框架远程异步(mq,异步,解耦,削峰)调用,如何保证分布式事务(系统之间的分布式事务和不同种类数据库之间的分布式事务),如何保证幂等性?????????????????】

​ 【如果引入了mq,如何保证消息重复,消息丢失,消息积压】

接口幂等性解决方案:

1、全局唯一ID,数据库

2、去重表,redis

3、状态机,0,1

那么数据积累如何解决呢??????????????

商品模块的难点(压测商品详情)

1、表的设计

1、从上往下分析:

商品分类:category 【手机】

商品品牌:brand 【苹果】

商品规格:product 【SPU】 iphone14

物品属性:attr

---------直至找到SKU【可以定位到库存的实际物品】----------------------------

2、页面静态化

技术:FreeMarker 提前准备好模板,然后动态生产html,然后放到CDN服务器上,美名其曰“静态页面比动态页面加载快”。

适用于:商品量较少的网站;

缺陷:

​ 1、小米的商品只有1000个,那么它只需要几个模板,动态生成的静态页面就是1000个,可适用于小型网站。一个模板改了,那么用其生成的页面都得改。

​ 2、它是根据服务器进行生成的,那么不同的服务器之间如何进行同步呢?【SCP命令??】

3、磁盘IO+网络IO优化(引入redis)

redis它是基于内存的数据库,基本不涉及磁盘的iO;

商品详情页:网络IO【通信3306】+磁盘IO【mysql做了持久化】,但是它有读多写少的特点;所以可以将其放入到缓存里边去。

4、数据一致性解决方案

1、最终一致性解决方案

​ 给缓存设置失效时间;

2、实时一致性解决方案

​ 1、高并发下的线程安全问题;

并发情况下,为什么还是走那么多次数据库,而不是走一次数据库,然后走缓存呢?

​ 原因:在并发过程中,在查询数据库,还没有设置缓存的时候,有很多其余的线程继续执行该方法,导致查询数据库。所以要加锁。

​ 关于加锁:

​ 1、单实例加锁没问题;

​ 2、多实例加锁:要加分布式锁;

​ 2、缓存问题

​ 缓存穿透(查询永不存在的数据)

​ 指的是查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数据库也没有此条记录,我们没有将这次查询的null写入到缓存,这将 导致这个不存在的数据每次请求都要到存储层去查询,这就使得缓存失去了意义。
​ 风险:利用不存在的数据进行攻击,数据库瞬时压力增大,最终导致崩溃。
​ 解决:null结果缓存,并加入短暂过期时间。(如果一直对空结果进行缓存,那么如果数据库中有数据,缓存中会一直获取到数据为null,所以不会加 载到新的不为null的数据,所以要设置过期时间。)
​ 缓存雪崩(集体失效)
​ 指的是我们在设置缓存key时候,采用了相同的过期时间,导致缓存在某一个时间同时失效,
​ 请求全部转发到db,db瞬时压力过重崩溃。
​ 解决:原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每个缓存的重复率就会降低,很难引发集体失效的事件。
​ 缓存击穿(过期的热点数据)
​ 指的是对于一些设置了过期时间的key,这些key过于热点,在某一时刻失效后,同时有大并发来请求数据,此时要请求数据库,造成数据库崩溃。
​ 解决:加锁,大量并发只让一个人去请求,其他人等待,查询到以后释放锁,其他人获取锁。先查询缓存,获取数据,就不会去请求数据库了。

​ 3、压缩的问题(减少内存-以数据进行压缩,以最小的内存做最多的事情);

redis解决的是磁盘io,所以网络io还是存在的。所以网速和数据传输数据量还是影响很大的。

5、缓存通过什么样的方式可以避免网络IO

本地缓存:hashMap,有两个问题?

​ 1、线程安全问题:用concurrentHashMap,也会有一些问题;

​ 比如数据会一直增加,但是内存是有容量大小的。对缓存做一个过期时间,

那么引入guava缓存

<dependency>
	<groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>22</version>
</dependency>
@Component
public class GuavaCacheUtil {
    private Cache<String, SkuInfoEntity> localCache = null;

    @PostConstruct
    private void init(){
        localCache = CacheBuilder.newBuilder().
                initialCapacity(10) //初始容量
                .maximumSize(500) //最大容量
                .expireAfterWrite(50, TimeUnit.MILLISECONDS) //过期时间
                .build();
    }

    public void setLocalCache(String key,SkuInfoEntity obj){
        localCache.put(key,obj);
    }

    public SkuInfoEntity get(String key){
        return localCache.getIfPresent(key);
    }

}

优先级

1、先查询本地缓存;【没有磁盘IO和网络IO】

2、查询redis;【有网络IO】

3、查询数据库;【有网络IO和磁盘IO】

public PmsProductParam getProductInfo4(Long id){
    PmsProductParam productInfo = null;
    productInfo = cache.get(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE + id);
    if(null != productInfo){
        return productInfo;
    }
    productInfo = redisOpsUtil.get(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE +  id,PmsProductParam.class);
    if(productInfo != null){
        cache.setLocalCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE +  id,productInfo); //设置本地缓存;
        return productInfo;
    }
    RLock lock = redisson.getLock(lockPath + id);
    try{
        if(lock.tryLock(0,10,TimeUnit.SECONDS)){ //等待时间,持续时间
            productInfo = portalProductDao.getProductInfo(id);
            if(null == productInfo){
                return null;
            }
            checkFlash(id,productInfo);
            redisOpsUtil.set(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE +  id,productInfo,3600,TimeUtil.SECONDS);
            cache.setLocalCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE +  id,productInfo); //设置本地缓存;
            log.info("set cache productId");
        }else{
            productInfo = redisOpsUtil.get(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE +  id,PmsProductParam.class);
            if(productInfo != null){
        		cache.setLocalCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE +  id,productInfo); //设置本地缓存;
    		}
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
      	if(lock.isLocked()){
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }  
    }
}

布隆过滤器(原理+代码)

布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

原理:提前将所有的key值放入到过滤器中,如果新来的id没有击中key,那么就判断该id是非法数据,直接返回,不用经过数据库和缓存。

@Slf4j
@Configuration
public class BloomFilterConfig implements InitializingBean{

    @Autowired
    private PmsProductService productService;

    @Autowired
    private RedisTemplate template;

    @Bean
    public BloomFilterHelper<String> initBloomFilterHelper() {
        return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8)
                .putString(from, Charsets.UTF_8), 1000000, 0.01);
    }

    /**
     * 布隆过滤器bean注入
     * @return
     */
    @Bean
    public BloomRedisService bloomRedisService(){
        BloomRedisService bloomRedisService = new BloomRedisService();
        bloomRedisService.setBloomFilterHelper(initBloomFilterHelper());
        bloomRedisService.setRedisTemplate(template);
        return bloomRedisService;
    }

    //重点!!!!!!!!!!!!!!!!!!!!!!!!!
    @Override
    public void afterPropertiesSet() throws Exception {
        List<Long> list = productService.getAllProductId();
        log.info("加载产品到布隆过滤器当中,size:{}",list.size());
        if(!CollectionUtils.isEmpty(list)){
            list.stream().forEach(item->{
                //LocalBloomFilter.put(item);
                bloomRedisService().addByBloomFilter(RedisKeyPrefixConst.PRODUCT_REDIS_BLOOM_FILTER,item+"");
            });
        }
    }
}
@Slf4j
public class BloomFilterInterceptor implements HandlerInterceptor {

    @Autowired
    private BloomRedisService bloomRedisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String currentUrl = request.getRequestURI();
        PathMatcher matcher = new AntPathMatcher();
        //解析出pathvariable
        Map<String, String> pathVariable = matcher.extractUriTemplateVariables("/pms/productInfo/{id}", currentUrl);
        //布隆过滤器存储在redis中
        if(bloomRedisService.includeByBloomFilter(RedisKeyPrefixConst.PRODUCT_REDIS_BLOOM_FILTER,pathVariable.get("id"))){
            return true;
        }

        /**
         * 存储在本地jvm布隆过滤器中
         */
        /*if(LocalBloomFilter.match(pathVariable.get("id"))){
            return true;
        }*/

        /*
         * 不在本地布隆过滤器当中,直接返回验证失败
         * 设置响应头
         */
        response.setHeader("Content-Type","application/json");
        response.setCharacterEncoding("UTF-8");
        String result = new ObjectMapper().writeValueAsString(CommonResult.validateFailed("产品不存在!"));
        response.getWriter().print(result);
        return false;

    }

}

如果:后边以后增量数据,怎么办??通过消息中间件,去解决。

6、缓存的终极方案

小流量架构图:https://www.processon.com/view/link/5e5774dae4b0cb56daac5a80

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R3PyqSSh-1668502850638)(F:\面试准备\项目准备\小流量架构图.png)]

大型网站的架构:https://www.processon.com/view/link/5e57735ce4b0362765065cf3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vioRJuim-1668502850639)(F:\面试准备\项目准备\大型网站架构图.png)]

1、OpenResty框架

​ 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 等都进行一致的高性能响应。
http://openresty.org/cn/

项目一共需要3个nginx,一个用来做流量分发,其余两个用来做静态文件的缓存。

流量分发的nginx,会发送http请求到后端的应用层nginx上去,所以要先引入lua http lib包
wget https://raw.githubusercontent.com/pintsized/lua‐resty‐http/master/lib/resty/http_headers.lua
wget https://raw.githubusercontent.com/pintsized/lua‐resty‐http/master/lib/resty/http.lua
最近网络不稳定,也可以从:https://github.com/bungle/lua-resty-template 去下载这两个lua脚本。

nginx.conf

在这里插入图片描述

在这里插入图片描述

2、三层缓存存储分配

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zw9MwhXs-1668502850640)(F:\面试准备\项目准备\三层缓存商品分配.png)]

2-8原则即可。2成商品是最热的,其余商品放到redis中。

lua+nginx:数据量小,访问量很高;

JVM本地缓存:数据量很大,访问量比较高;

redis:数据量相对来说比较大,访问量相对不高;

LRU算法:最热的数据进入缓存;

LRU-K算法:最近最热的数据放入到缓存,连续访问三次以上。

【电商互联网架构演化:https://note.youdao.com/ynoteshare/index.html?id=63d31b96ca60b59374650cec2e6f4b3e&type=note&_time=1668501407455】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/7491.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

好书赠送丨海伦·尼森鲍姆著:《场景中的隐私——技术、政治和社会生活中的和谐》,王苑等译

开放隐私计算 收录于合集#书籍分享1个 开放隐私计算 开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神&#xff0c;专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播&#xff0c;愿成为中国 “隐私计算最后一公里的服…

[附源码]java毕业设计基于javaweb电影购票系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

公众号运营建议与反思分享,建议收藏

正所谓有总结才会有成长&#xff0c;公众号运营也是如此。 公众号运营不是一朝一夕的事情&#xff0c;经过岁月的洗礼和千锤百炼&#xff0c;也总归是有了自己的一套经验和技巧。 对于公众号运营有什么建议&#xff1f;值得大家反思什么&#xff1f;今天伯乐网络传媒就来给大…

Boost升压电路调试

背景&#xff1a; 项目用到了一款升压电路&#xff0c;将12V升压到32V&#xff0c;电流要求有12A&#xff0c;最大18A。 设计的方案是使用Boost Controller 外置MOS来实现。 选定的Controller芯片为Maxim的MAX25203。 问题&#xff1a; 回板后进行调试&#xff0c;在不使能…

活动预告|“构建新安全格局”专家研讨会即将开幕

应急管理承担着防范化解重大风险、及时应对处置各类突发事件的重要职责&#xff0c;担负保护人民群众生命财产安全和维护社会稳定的重要使命。过去一年是我国应急管理体系和能力建设经受严峻考验的一年&#xff0c;也是实现大发展的一年。 11月17日&#xff0c;由中央党校科研部…

Python简单实现人脸识别检测, 对某平台美女主播照片进行评分排名

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 开发环境: Python 3.8 Pycharm 2021.2 模块使用: 第三方模块 requests >>> pip install requests tqdm >>> pip install tqdm 简单实现进度条效果 自带模块 os base64 采集代码 导入模块 # 数…

vue封装的echarts组件被同一个页面多次引用无法正常显示问题(已解决)

问题&#xff1a;第二张图显示空白&#xff0c;折线图并没有展示出来 当我们在封装了echarts组件之后&#xff0c;需要在同一个页面中引入多次时&#xff0c;会出现数据覆盖等一系列问题 当时我是修改了id也无济于事&#xff0c;达不到我需要的效果 解决方案 将我们封装的组件…

HTML5简明教程系列之HTML5 表格与表单(二)

HTML的第二弹也来了&#xff0c;最近高产似母猪&#xff0c;状态也不错&#xff0c;代码来源为实验课。本期主要内容为&#xff1a;HTML表格与DIV应用、HTML表单。上期基础部分的传送门&#xff1a; HTML5简明教程系列之HTML5基础&#xff08;一&#xff09;_Thomas_Lbw的博客-…

【进程复制】

目录地址偏移量fork函数fork练习地址偏移量 PCB结构体&#xff1a; struct task_struct { PID ststus ; … } 页面的内存大小是固定的&#xff0c;不足一页会给一页&#xff0c;大于一页会给一个整页数 比如一页大小为4K&#xff0c;地址除4K商是页号&#xff0c;余数是在该页…

Vue(六)——使用脚手架(3)

目录 webStorage localStorage sessionStorage todolist案例中使用 组件自定义事件 绑定 解绑 总结 全局事件总线 消息发布与订阅 nextTick 过渡与动画 webStorage 这不是vue团队开发的&#xff0c;不需要写在xx.vue当中&#xff0c;只需写在xx.html当中即可。 什…

Linux下C++开发笔记--g++命令

目录 1--前言 2--开发环境搭建 3--g重要编译参数 4--实例 1--前言 最近学习在linux环境下进行C开发的基础知识&#xff0c;参考的教程是基于VSCode和CMake实现C/C开发 | Linux篇&#xff0c;非常适合小白入门学习。 2--开发环境搭建 ①安装gcc、g和gdb&#xff1a; sud…

深度学习入门(三十七)计算性能——硬件(TBC)

深度学习入门&#xff08;三十七&#xff09;计算性能——硬件&#xff08;CPU、GPU&#xff09;前言计算性能——硬件&#xff08;CPU、GPU&#xff09;课件电脑提升CPU利用率①提升CPU利用率②CPU VS GPU提升GPU利用率CPU/GPU带宽更多的CPU和GPUCPU/GPU高性能计算编程总结教材…

SpringBoot整合dubbo(一)

第一次整合&#xff0c;使用无注册中心方式 一、首先&#xff0c;项目分为三个模块&#xff0c;如下图&#xff0c;dubbo-interface&#xff08;要发布的接口&#xff09;、dubbo-provider&#xff08;接口的具体实现&#xff0c;服务提供者&#xff09;、dubbo-consumer&#…

【LeetCode-中等】63. 不同路径 II(详解)

题目 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上角到…

VScode

VScode 下载 VScode&#xff1a;https://code.visualstudio.com/安装 汉化 Chinese (Simplified) 设置 背景色 Atom One Light Theme Color Theme 护眼色 "workbench.colorCustomizations": { // 设置背景颜色// "foreground": "#75a478",&…

List详解

一、List&#xff08;列表&#xff09; 基本的数据类型&#xff0c;列表 在redis中&#xff0c;通过相应操作可以让list变成栈、队列、阻塞队列&#xff01; 在redis中所有的list命令都是以 l 开头的 添加值 将一个值或多个值&#xff0c;插入到列表尾部&#xff08;右&…

深度学习之语义分割算法(入门学习)

>>>深度学习Tricks&#xff0c;第一时间送达<<< 目录 &#x1f4a1; 写在前面 一、前言 二、深度学习的图像分割分类 1.语义分割 2.实例分割 3.全景分割 三、语义分割的基本原理 四、语义分割的常用运算及评价指标 关于算法改进及论文投稿可关注并留…

【附源码】Python计算机毕业设计寿险公司保险业务管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

指令格式学习

计算机的指令格式与机器的字长、存储器的容量及指令的功能都有很大的关系。从便于程序设计、增加基本操作并行性、提高指令功能的角度来看&#xff0c;指令中应包含多种信息。但在有些指令中&#xff0c;由于部分信息可能无用&#xff0c;这将浪费指令所占的存储空间&#xff0…

python web服务windows管理工具

故事背景是这样的。。。 客户需要一些小工具&#xff0c;作为一名开发者&#xff0c;很容易的就写完了。但是呢&#xff0c;客户没有服务器&#xff0c;只能部署在windows上。由于不再一起&#xff0c;维护一个web程序&#xff0c;有时候会有些折腾&#xff0c;所以&#xff0…