• 多级缓存、


    多级缓存

    案例导入说明

    为了演示多级缓存,我们先导入一个商品管理的案例,其中包含商品的CRUD功能。我们将来会给查询商品添加多级缓存。

    1.安装MySQL

    后期做数据同步需要用到MySQL的主从功能,所以需要在虚拟机中,利用Docker来运行一个MySQL容器。

    1.1.准备目录

    为了方便后期配置MySQL,我们先准备两个目录,用于挂载容器的数据和配置文件目录:

    # 进入/tmp目录
    cd /tmp
    # 创建文件夹
    mkdir mysql
    # 进入mysql目录
    cd mysql
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.2.运行命令

    进入mysql目录后,执行下面的Docker命令:

    docker run \
     -p 3306:3306 \
     --name mysql \
     -v $PWD/conf:/etc/mysql/conf.d \
     -v $PWD/logs:/logs \
     -v $PWD/data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=20020630 \
     --privileged \
     -d \
     mysql:5.7.25
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.3.修改配置

    在/tmp/mysql/conf目录添加一个my.cnf文件,作为mysql的配置文件:

    # 创建文件
    touch /tmp/mysql/conf/my.cnf
    
    • 1
    • 2

    文件的内容如下:

    [mysqld]
    skip-name-resolve
    character_set_server=utf8
    datadir=/var/lib/mysql
    server-id=1000
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.4.重启

    配置修改后,必须重启容器:

    docker restart mysql
    
    • 1

    2.导入SQL

    /*
     Navicat Premium Data Transfer
    
     Source Server         : 192.168.150.101
     Source Server Type    : MySQL
     Source Server Version : 50725
     Source Host           : 192.168.150.101:3306
     Source Schema         : heima
    
     Target Server Type    : MySQL
     Target Server Version : 50725
     File Encoding         : 65001
    
     Date: 16/08/2021 14:45:07
    */
    
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    
    -- Table structure for tb_item
    
    -- ----------------------------
    
    DROP TABLE IF EXISTS `tb_item`;
    CREATE TABLE `tb_item`  (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品id',
      `title` varchar(264) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品标题',
      `name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '商品名称',
      `price` bigint(20) NOT NULL COMMENT '价格(分)',
      `image` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品图片',
      `category` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '类目名称',
      `brand` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌名称',
      `spec` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '规格',
      `status` int(1) NULL DEFAULT 1 COMMENT '商品状态 1-正常,2-下架,3-删除',
      `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
      `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`) USING BTREE,
      INDEX `status`(`status`) USING BTREE,
      INDEX `updated`(`update_time`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 50002 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '商品表' ROW_FORMAT = COMPACT;
    
    -- ----------------------------
    
    -- Records of tb_item
    
    -- ----------------------------
    
    INSERT INTO `tb_item` VALUES (10001, 'RIMOWA 21寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4', 'SALSA AIR', 16900, 'https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp', '拉杆箱', 'RIMOWA', '{\"颜色\": \"红色\", \"尺码\": \"26寸\"}', 1, '2019-05-01 00:00:00', '2019-05-01 00:00:00');
    INSERT INTO `tb_item` VALUES (10002, '安佳脱脂牛奶 新西兰进口轻欣脱脂250ml*24整箱装*2', '脱脂牛奶', 68600, 'https://m.360buyimg.com/mobilecms/s720x720_jfs/t25552/261/1180671662/383855/33da8faa/5b8cf792Neda8550c.jpg!q70.jpg.webp', '牛奶', '安佳', '{\"数量\": 24}', 1, '2019-05-01 00:00:00', '2019-05-01 00:00:00');
    INSERT INTO `tb_item` VALUES (10003, '唐狮新品牛仔裤女学生韩版宽松裤子 A款/中牛仔蓝(无绒款) 26', '韩版牛仔裤', 84600, 'https://m.360buyimg.com/mobilecms/s720x720_jfs/t26989/116/124520860/644643/173643ea/5b860864N6bfd95db.jpg!q70.jpg.webp', '牛仔裤', '唐狮', '{\"颜色\": \"蓝色\", \"尺码\": \"26\"}', 1, '2019-05-01 00:00:00', '2019-05-01 00:00:00');
    INSERT INTO `tb_item` VALUES (10004, '森马(senma)休闲鞋女2019春季新款韩版系带板鞋学生百搭平底女鞋 黄色 36', '休闲板鞋', 10400, 'https://m.360buyimg.com/mobilecms/s720x720_jfs/t1/29976/8/2947/65074/5c22dad6Ef54f0505/0b5fe8c5d9bf6c47.jpg!q70.jpg.webp', '休闲鞋', '森马', '{\"颜色\": \"白色\", \"尺码\": \"36\"}', 1, '2019-05-01 00:00:00', '2019-05-01 00:00:00');
    INSERT INTO `tb_item` VALUES (10005, '花王(Merries)拉拉裤 M58片 中号尿不湿(6-11kg)(日本原装进口)', '拉拉裤', 38900, 'https://m.360buyimg.com/mobilecms/s720x720_jfs/t24370/119/1282321183/267273/b4be9a80/5b595759N7d92f931.jpg!q70.jpg.webp', '拉拉裤', '花王', '{\"型号\": \"XL\"}', 1, '2019-05-01 00:00:00', '2019-05-01 00:00:00');
    
    -- ----------------------------
    
    -- Table structure for tb_item_stock
    
    -- ----------------------------
    
    DROP TABLE IF EXISTS `tb_item_stock`;
    CREATE TABLE `tb_item_stock`  (
      `item_id` bigint(20) NOT NULL COMMENT '商品id,关联tb_item表',
      `stock` int(10) NOT NULL DEFAULT 9999 COMMENT '商品库存',
      `sold` int(10) NOT NULL DEFAULT 0 COMMENT '商品销量',
      PRIMARY KEY (`item_id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;
    
    -- ----------------------------
    
    -- Records of tb_item_stock
    
    -- ----------------------------
    
    INSERT INTO `tb_item_stock` VALUES (10001, 99996, 3219);
    INSERT INTO `tb_item_stock` VALUES (10002, 99999, 54981);
    INSERT INTO `tb_item_stock` VALUES (10003, 99999, 189);
    INSERT INTO `tb_item_stock` VALUES (10004, 99999, 974);
    INSERT INTO `tb_item_stock` VALUES (10005, 99999, 18649);
    
    SET FOREIGN_KEY_CHECKS = 1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    其中包含两张表:

    • tb_item:商品表,包含商品的基本信息
    • tb_item_stock:商品库存表,包含商品的库存信息

    之所以将库存分离出来,是因为库存是更新比较频繁的信息,写操作较多。而其他信息修改的频率非常低。

    3.导入Demo工程

    项目结构如图所示:

    在这里插入图片描述

    其中的业务包括:

    • 分页查询商品
    • 新增商品
    • 修改商品
    • 修改库存
    • 删除商品
    • 根据id查询商品
    • 根据id查询库存

    业务全部使用mybatis-plus来实现,如有需要请自行修改业务逻辑。

    3.1.分页查询商品

    com.heima.item.web包的ItemController中可以看到接口定义:
    在这里插入图片描述

    3.2.新增商品

    com.heima.item.web包的ItemController中可以看到接口定义:

    在这里插入图片描述

    3.3.修改商品

    com.heima.item.web包的ItemController中可以看到接口定义:
    在这里插入图片描述

    3.4.修改库存

    com.heima.item.web包的ItemController中可以看到接口定义:

    在这里插入图片描述

    3.5.删除商品

    com.heima.item.web包的ItemController中可以看到接口定义:

    在这里插入图片描述

    这里是采用了逻辑删除,将商品状态修改为3

    3.6.根据id查询商品

    com.heima.item.web包的ItemController中可以看到接口定义:

    在这里插入图片描述

    这里只返回了商品信息,不包含库存

    3.7.根据id查询库存

    com.heima.item.web包的ItemController中可以看到接口定义:

    在这里插入图片描述

    3.8.启动

    注意修改application.yml文件中配置的mysql地址信息:

    在这里插入图片描述

    需要修改为自己的虚拟机地址信息、还有账号和密码。

    修改后,启动服务,访问:http://localhost:8081/item/10001即可查询数据

    4.导入商品查询页面

    商品查询是购物页面,与商品管理的页面是分离的。

    部署方式如图:
    在这里插入图片描述

    我们需要准备一个反向代理的nginx服务器,如上图红框所示,将静态的商品页面放到nginx目录中。

    页面需要的数据通过ajax向服务端(nginx业务集群)查询。

    4.1.运行nginx服务

    运行这个nginx1.18.0服务。

    运行命令:

    start nginx.exe
    
    • 1

    然后访问 http://localhost/item.html?id=10001即可:

    在这里插入图片描述

    4.2.反向代理

    现在,页面是假数据展示的。我们需要向服务器发送ajax请求,查询商品数据。

    打开控制台,可以看到页面有发起ajax查询数据:
    在这里插入图片描述

    而这个请求地址同样是80端口,所以被当前的nginx反向代理了。

    查看nginx的conf目录下的nginx.conf文件:

    在这里插入图片描述

    其中的关键配置如下:

    在这里插入图片描述

    其中的192.168.150.101是我的虚拟机IP,也就是我的Nginx业务集群要部署的地方:

    在这里插入图片描述

    完整内容如下:

    #user  nobody;
    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        sendfile        on;
        #tcp_nopush     on;
        keepalive_timeout  65;
    
        upstream nginx-cluster{
            server 192.168.150.101:8081;
        }
        server {
            listen       80;
            server_name  localhost;
    
    	location /api {
                proxy_pass http://nginx-cluster;
            }
    
            location / {
                root   html;
                index  index.html index.htm;
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    本地进程缓存

    Caffeine是一个基于Java8开发的,提供了近乎最佳命中率的高性能的本地缓存库。目前Spring内部的缓存使用的就是Caffeine。GitHub地址:https://github.com/ben-manes/caffeine

    1、导入依赖

    
        com.github.ben-manes.caffeine
        caffeine
    
    
    • 1
    • 2
    • 3
    • 4

    2、

    /*
      基本用法测试
     */
    @Test
    void testBasicOps() {
        // 创建缓存对象
        Cache cache = Caffeine.newBuilder().build();
    
        // 存数据
        cache.put("gf", "迪丽热巴");
    
        // 取数据,不存在则返回null
        String gf = cache.getIfPresent("gf");
        System.out.println("gf = " + gf);
    
        // 取数据,不存在则去数据库查询
        String defaultGF = cache.get("defaultGF", key -> {
            // 这里可以去数据库根据 key查询value
            return "柳岩";
        });
        System.out.println("defaultGF = " + defaultGF);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    /*
     基于大小设置驱逐策略:
     */
    @Test
    void testEvictByNum() throws InterruptedException {
        // 创建缓存对象
        Cache cache = Caffeine.newBuilder()
                // 设置缓存大小上限为 1
                .maximumSize(1)
                .build();
        // 存数据
        cache.put("gf1", "柳岩");
        cache.put("gf2", "范冰冰");
        cache.put("gf3", "迪丽热巴");
        // 延迟10ms,给清理线程一点时间
        Thread.sleep(10L);
        // 获取数据
        System.out.println("gf1: " + cache.getIfPresent("gf1"));
        System.out.println("gf2: " + cache.getIfPresent("gf2"));
        System.out.println("gf3: " + cache.getIfPresent("gf3"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
        /*
         基于时间设置驱逐策略:
         */
        @Test
        void testEvictByTime() throws InterruptedException {
            // 创建缓存对象
            , String> cache = Caffeine.newBuilder()
                    .expireAfterWrite(Duration.ofSeconds(1)) // 设置缓存有效期为 1秒
                    .build();
            // 存数据
            cache.put("gf", "柳岩");
            // 获取数据
            System.out.println("gf: " + cache.getIfPresent("gf"));
            // 休眠一会儿
            Thread.sleep(1200L);
            System.out.println("gf: " + cache.getIfPresent("gf"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    实现商品的查询本地进程缓存

    利用Caffeine实现下列需求:

    • 给根据id查询商品的业务添加缓存,缓存未命中时查询数据库
    • 给根据id查询商品库存的业务添加缓存,缓存未命中时查询数据库
    • 缓存初始大小为100
    • 缓存上限为10000

    1.将缓存设置为bean添加到容器中

    @Configuration
    public class CaffeineConfig {
        @Bean
        public Cache<Long, Item> itemCache(){
            return Caffeine.newBuilder()
                    .initialCapacity(100) //缓存初始大小为100
                    .maximumSize(10000).build();//缓存上限为10000
        }  
        @Bean
        public Cache<Long, ItemStock> itemStockCache(){
            return Caffeine.newBuilder()
                    .initialCapacity(100) //缓存初始大小为100
                    .maximumSize(10000).build();//缓存上限为10000
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、在ItemControler中

    @Autowired
    private Cache itemCache;
    @Autowired
    private Cache itemStockCache;
    
    • 1
    • 2
    • 3
    • 4
    @GetMapping("/{id}")
    public Item findById(@PathVariable("id") Long id){
        //先根据id查缓存,如果未命中在查询数据库
        return  itemCache.get(id,key->itemService.query()
                .ne("status", 3).eq("id", key)
                .one());
       
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    【数据结构Java版】LinkedList与链表
    深入理解强化学习——强化学习的历史:试错学习
    flutter产物以aar形式嵌入android原生工程
    vue3上传文件组件方法封装
    福建厦门航空飞机发动机零部件检测3D测量尺寸偏差比对-CASAIM中科广电
    python+pytest接口自动化(12)-自动化用例编写思路 (使用pytest编写一个测试脚本)
    Java 内存溢出(一)原因、复现、排查
    你听说过苹果的搜索引擎吗?
    5(6)-羧基-X-罗丹明,CAS号:198978-94-8
    代码随想录算法训练营第23期day28|491.递增子序列 46.全排列 47.全排列 II
  • 原文地址:https://blog.csdn.net/qq_57907966/article/details/126816561