第十一节:分布式文件系统

本文详细介绍了分布式文件系统FastDFS的架构、上传下载流程及其Java客户端的使用。FastDFS由Tracker Server和Storage Server组成,提供文件存储、同步和访问功能。商品规格数据结构中,SPU代表标准产品单位,SKU代表库存量单位,两者在数据库设计中涉及规格参数表的构建,包括通用属性、特有属性和搜索过滤条件。FastDFS适用于大容量存储需求,而商品规格设计则需考虑不同商品类别的属性差异。
摘要由CSDN通过智能技术生成

此博客用于个人学习,来源于网上,对知识点进行一个整理。

1. FastDFS:

分布式文件系统(Distributed File System)是指文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网络与节点相连。

  • 传统文件系统管理的文件就存储在本机
  • 分布式文件系统管理的文件存储在很多机器,这些机器通过网络连接,要被统一管理。无论是上传或者访问文件,都需要通过管理中心来访问

FastDFS 是一个轻量级、高性能的开源分布式文件系统。用纯C语言开发,功能丰富:

  • 文件存储
  • 文件同步
  • 文件访问(上传、下载)
  • 存取负载均衡
  • 在线扩容

适合有大容量存储需求的应用或系统。同类的分布式文件系统有谷歌的GFS、HDFS(Hadoop)、TFS(淘宝)等。

1.1 FastDFS 的架构:

在这里插入图片描述
FastDFS 两个主要的角色:Tracker Server 和 Storage Server 。

  • Tracker Server:跟踪服务器,主要负责调度 storage 节点与 client 通信,在访问上起负载均衡的作用,和记录 storage 节点的运行状态,是连接 client 和 storage 节点的枢纽
  • Storage Server:存储服务器,保存文件和文件的 meta data(元数据),每个 storage server 会启动一个单独的线程主动向 Tracker cluster 中每个 tracker server 报告其状态信息,包括磁盘使用情况,文件同步情况及文件上传下载次数统计等信息
  • Group:文件组,多台 Storage Server 的集群。上传一个文件到同组内的一台机器上后,FastDFS 会将该文件即时同步到同组内的其它所有机器上,起到备份的作用。不同组的服务器,保存的数据不同,而且相互独立,不进行通信
  • Tracker Cluster:跟踪服务器的集群,有一组 Tracker Server(跟踪服务器)组成
  • Storage Cluster :存储集群,有多个 Group 组成

1.2 FastDFS 上传和下载流程:

上传:

在这里插入图片描述

  1. Client 通过 Tracker server 查找可用的 Storage server
  2. Tracker server 向 Client 返回一台可用的 Storage server 的 IP 地址和端口号
  3. Client 直接通过 Tracker server 返回的 IP 地址和端口与其中一台 Storage server 建立连接并进行文件上传
  4. 上传完成,Storage server 返回 Client 一个文件 ID,文件上传结束

下载:

在这里插入图片描述

  1. Client 通过 Tracker server 查找要下载文件所在的 Storage server
  2. Tracker server 向 Client 返回包含指定文件的某个 Storage server 的 IP 地址和端口号
  3. Client 直接通过 Tracker server 返回的 IP 地址和端口与其中一台 Storage server 建立连接并指定要下载文件
  4. 下载文件成功

1.3 java 客户端:

就用 FastDFS 改造 leyou-upload 工程:

1)引入依赖:

<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
</dependency>

2)引入配置类:

@Configuration
@Import(FdfsClientConfig.class)
//解决jmx重复注册bean的问题
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class FastClientImporter {
}

3)编写 FastDFS 属性:

在 application.yml 配置文件中添加如下内容:

fdfs:
  so-timeout: 1501 # 超时时间
  connect-timeout: 601 # 连接超时时间
  thumb-image: # 缩略图
    width: 60
    height: 60
  tracker-list: # tracker地址:你的虚拟机服务器地址+端口(默认是22122)
    - 192.168.56.101:22122

4)配置 hosts:

将来通过域名:image.leyou.com 这个域名访问 fastDFS 服务器上的图片资源。所以,需要代理到虚拟机地址:

配置 hosts 文件,使 image.leyou.com 可以访问 fastDFS 服务器

192.168.56.101 image.leyou.com

5)改造上传逻辑:

@Service
public class UploadService {

    private static final List<String> content_types = Arrays.asList("image/gif","image/jpeg");

    private static final Logger LOGGER = LoggerFactory.getLogger(UploadService.class);

    @Autowired
    private FastFileStorageClient storageClient;

    public String uploadImage(MultipartFile file) {

        String originalFilename = file.getOriginalFilename();
        //校验文件类型
        String contentType = file.getContentType();
        if (!content_types.contains(contentType)){
            LOGGER.info("文件类型不合法:{}",originalFilename);
            return null;
        }

        try {
            //校验文件内容
            BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
            if (bufferedImage == null){
                LOGGER.info("文件内容不合法:{}",originalFilename);
                return null;
            }

            //保存到文件的服务器
            //file.transferTo(new File("D:\\java\\images\\"+originalFilename));
            String ext = StringUtils.substringAfterLast(originalFilename, ".");
            StorePath storePath = this.storageClient.uploadFile(file.getInputStream(), file.getSize(), ext, null);

            //返回url,进行回写
            //return "http://image.leyou.com/" + originalFilename;
            return "http://image.leyou.com/" + storePath.getFullPath();
        } catch (IOException e) {
            LOGGER.info("服务器内部错误:"+originalFilename);
            e.printStackTrace();
        }
        return null;
    }
}

2. 商品规格数据结构:

商城是一个全品类的电商网站,因此商品的种类繁多,每一件商品,其属性又有差别。为了更准确描述商品及细分差别,抽象出两个概念:SPU 和 SKU。

2.1 SPU 和 SKU:

SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集

SKU:Stock Keeping Unit(库存量单位),SPU 商品集因具体特性不同而细分的每个商品

在这里插入图片描述

  • 本页的 华为 Mate10 就是一个商品集(SPU)
  • 因为颜色、内存等不同,而细分出不同的 Mate10,如亮黑色128G版。(SKU)

可以看出:

  • SPU 是一个抽象的商品集概念,为了方便后台的管理。
  • SKU 才是具体要销售的商品,每一个 SKU 的价格、库存可能会不一样,用户购买的是 SKU 而不是 SPU

2.2 数据库设计分析:

1)初步设计,发现问题:

SPU 应该考虑的字段:

id:主键
title:标题
description:描述
specification:规格
packaging_list:包装
after_service:售后服务
comment:评价
category_id:商品分类
brand_id:品牌

难点是商品的规格字段怎么进行设计

SKU 应该考虑的字段:

id:主键
spu_id:关联的spu
price:价格
images:图片
stock:库存

不同的商品分类,可能属性是不一样的,比如手机有内存,衣服有尺码,我们是全品类的电商网站,这些不同的商品的不同属性。其实颜色、内存、硬盘属性都是规格参数中的字段。所以,要解决这个问题,首先要能清楚规格参数。

2)分析规格参数:

虽然商品规格千变万化,但是同一类商品(如手机)的规格是统一的。

3)SKU 的特有属性:

SPU 中会有一些特殊属性,用来区分不同的 SKU,我们称为 SKU 特有属性。如华为 META10 的颜色、内存属性。不同种类的商品,一个手机,一个衣服,其 SKU 属性不相同。同一种类的商品,比如都是衣服,SKU 属性基本是一样的,都是颜色、尺码等。

SKU 的特有属性是商品规格参数的一部分

没必要单独对 SKU 的特有属性进行设计,它可以看做是规格参数中的一部分,这样规格参数中的属性可以标记成两部分:

  • spu 下所有 sku 共享的规格属性(称为全局属性)
  • 每个 sku 不同的规格属性(称为特有属性)

4)搜索属性:

打开一个搜索页,我们来看看过滤的条件,过滤条件中的屏幕尺寸、运行内存、网路、机身内存、电池容量、CPU 核数等,在规格参数中都能找到。也就是说,规格参数中的数据,将来会有一部分作为搜索条件来使用。我们可以在设计时,将这部分属性标记出来,将来做搜索的时候,作为过滤条件。要注意的是,无论是SPU的全局属性,还是SKU的特有属性,都有可能作为搜索过滤条件的,并不冲突,而是有一个交集。

在这里插入图片描述

1.3 规格参数表:

1)表结构:

在这里插入图片描述

规格参数是分组的,每一组都有多个参数键值对。不过对于规格参数的模板而言,其值现在是不确定的,不同的商品值肯定不同,模板中只要保存组信息、组内参数信息即可。

因此我们设计了两张表:

  • tb_spec_group:组,与商品分类关联
  • tb_spec_param:参数名,与组关联,一对多

2)规格组:

规格参数分组表:tb_spec_group

CREATE TABLE `tb_spec_group` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `cid` bigint(20) NOT NULL COMMENT '商品分类id,一个分类下有多个规格组',
  `name` varchar(50) NOT NULL COMMENT '规格组的名称',
  PRIMARY KEY (`id`),
  KEY `key_category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='规格参数的分组表,每个商品分类下有多个规格参数组';

规格组有3个字段:

  • id:主键
  • cid:商品分类id,一个分类下有多个模板
  • name:该规格组的名称

3)规格参数:

规格参数表:tb_spec_param

CREATE TABLE `tb_spec_param` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `cid` bigint(20) NOT NULL COMMENT '商品分类id',
  `group_id` bigint(20) NOT NULL,
  `name` varchar(255) NOT NULL COMMENT '参数名',
  `numeric` tinyint(1) NOT NULL COMMENT '是否是数字类型参数,true或false',
  `unit` varchar(255) DEFAULT '' COMMENT '数字类型参数的单位,非数字类型可以为空',
  `generic` tinyint(1) NOT NULL COMMENT '是否是sku通用属性,true或false',
  `searching` tinyint(1) NOT NULL COMMENT '是否用于搜索过滤,true或false',
  `segments` varchar(1000) DEFAULT '' COMMENT '数值类型参数,如果需要搜索,则添加分段间隔值,如CPU频率间隔:0.5-1.0',
  PRIMARY KEY (`id`),
  KEY `key_group` (`group_id`),
  KEY `key_category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COMMENT='规格参数组下的参数名';

其中多了不少新定义的元素,是由于规格参数中有一部分是 SKU 的通用属性,一部分是 SKU 的特有属性,而且其中会有一些将来用作搜索过滤,这些信息都需要标记出来。

  • 通用属性:用一个布尔类型字段来标记是否为通用:

    • generic来标记是否为通用属性:
      • true:代表通用属性
      • false:代表sku特有属性
  • 搜索过滤:与搜索相关的有两个字段:

    • searching:标记是否用作过滤
      • true:用于过滤搜索
      • false:不用于过滤
    • segments:某些数值类型的参数,在搜索时需要按区间划分,这里提前确定好划分区间
      • 比如电池容量,0~2000mAh,2000mAh~3000mAh,3000mAh~4000mAh
  • 数值类型:某些规格参数可能为数值类型,这样的数据才需要划分区间,我们有两个字段来描述:

    • numberic:是否为数值类型
      • true:数值类型
      • false:不是数值类型
    • unit:参数的单位

3. SPU 和 SKU 数据结构:

3.1 SPU 表:

SPU 表:

CREATE TABLE `tb_spu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'spu id',
  `title` varchar(255) NOT NULL DEFAULT '' COMMENT '标题',
  `sub_title` varchar(255) DEFAULT '' COMMENT '子标题',
  `cid1` bigint(20) NOT NULL COMMENT '1级类目id',
  `cid2` bigint(20) NOT NULL COMMENT '2级类目id',
  `cid3` bigint(20) NOT NULL COMMENT '3级类目id',
  `brand_id` bigint(20) NOT NULL COMMENT '商品所属品牌id',
  `saleable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否上架,0下架,1上架',
  `valid` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0已删除,1有效',
  `create_time` datetime DEFAULT NULL COMMENT '添加时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=208 DEFAULT CHARSET=utf8 COMMENT='spu表,该表描述的是一个抽象的商品,比如 iphone8';

其中少了一些字段,比如商品描述,于是做了表的垂直拆分,将SPU的详情放到了另一张表:tb_spu_detail。

CREATE TABLE `tb_spu_detail` (
  `spu_id` bigint(20) NOT NULL,
  `description` text COMMENT '商品描述信息',
  `generic_spec` varchar(10000) NOT NULL DEFAULT '' COMMENT '通用规格参数数据',
  `special_spec` varchar(1000) NOT NULL COMMENT '特有规格参数及可选值信息,json格式',
  `packing_list` varchar(3000) DEFAULT '' COMMENT '包装清单',
  `after_service` varchar(3000) DEFAULT '' COMMENT '售后服务',
  PRIMARY KEY (`spu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

张表中的数据都比较大,为了不影响主表的查询效率我们拆分出这张表。需要注意的是这两个字段:generic_spec和special_spec。

前面讲过规格参数与商品分类绑定,一个分类下的所有 SPU 具有类似的规格参数。SPU 下的 SKU 可能会有不同的规格参数信息,因此可以这样设计:

  • SPUDetail 中保存通用的规格参数信息
  • SKU 中保存特有规格参数

1)generic_spec 字段:

其中保存通用规格参数信息的值,这里为了方便查询,使用了 json 格式,其中都是键值对:

  • key:对应的规格参数的 spec_param 的 id
  • value:对应规格参数的值

2)special_spec 字段:

颜色、内存、机身存储,作为 SKU 特有属性,key 虽然一样,但是 SPU 下的每一个 SKU,其值都不一样,所以值会有很多,形成数组。

我们在 SPU 中,会把特有属性的所有值都记录下来,形成一个数组,每一个数据都是 json 结构:

  • key:规格参数 id
  • value:spu 属性的数组

那么问题来:特有规格参数应该在sku中记录才对,为什么在spu中也要记录一份?

而且有时候需要把所有规格参数都查询出来,而不是只查询1个 sku 的属性。比如,商品详情页展示可选的规格参数时,所以应该在 sku 中记录的特有规格参数在 spu 中也要记录一份。

3.2 SKU 表:

CREATE TABLE `tb_sku` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'sku id',
  `spu_id` bigint(20) NOT NULL COMMENT 'spu id',
  `title` varchar(255) NOT NULL COMMENT '商品标题',
  `images` varchar(1000) DEFAULT '' COMMENT '商品的图片,多个图片以‘,’分割',
  `price` bigint(15) NOT NULL DEFAULT '0' COMMENT '销售价格,单位为分',
  `indexes` varchar(100) COMMENT '特有规格属性在spu属性模板中的对应下标组合',
  `own_spec` varchar(1000) COMMENT 'sku的特有规格参数,json格式',
  `enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0无效,1有效',
  `create_time` datetime NOT NULL COMMENT '添加时间',
  `last_update_time` datetime NOT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`id`),
  KEY `key_spu_id` (`spu_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='sku表,该表表示具体的商品实体,如黑色的64GB的iphone 8';

还有一张表,代表库存:

CREATE TABLE `tb_stock` (
  `sku_id` bigint(20) NOT NULL COMMENT '库存对应的商品sku id',
  `seckill_stock` int(9) DEFAULT '0' COMMENT '可秒杀库存',
  `seckill_total` int(9) DEFAULT '0' COMMENT '秒杀总数量',
  `stock` int(9) NOT NULL COMMENT '库存数量',
  PRIMARY KEY (`sku_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='库存表,代表库存,秒杀库存等信息';

因为库存字段写频率较高,而 SKU 的其它字段以读为主,因此我们将两张表分离,读写不会干扰。

1)indexes 字段:

在 SPU 表中,已经对特有规格参数及可选项进行了保存:

{
    "4": [
        "香槟金",
        "樱花粉",
        "磨砂黑"
    ],
    "12": [
        "2GB",
        "3GB"
    ],
    "13": [
        "16GB",
        "32GB"
    ]
}

这些特有属性如果排列组合,会产生12个不同的 SKU,而不同的 SKU,其属性就是上面备选项中的一个,当用户点击选中一个特有属性,你就能根据 角标快速定位到 sku。

2)own_spec 字段:

保存的是特有属性的键值对。

SPU 中保存的是可选项,但不确定具体的值,而 SKU 中的保存的就是具体的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值