良好的文件表结构设计

分为3张表,
1.file_dir(包括文件夹和文件),
2.file_userl(用户表,用于同一个文件夹【第二次文件夹,比如项目】的多人协作)
3.file(存储的是文件真实地址的url)

设计的表结构需要解决如下几个方面的性能问题
1.解决批量插入文件夹/文件( id是不能用的)
处理手段:用如下规则生成当前文件夹/文件的唯一值:unique_hash = md5(文件路径(md5_path)+项目创建人ID(salt)+文件类型(文件/文件夹))
使用如上生成方式的确可以解决文件夹/文件的唯一性问题,但有一个致命的问题,是因为文件名/文件路径参与了唯一值计算,当在文件系统中修改文件,会导致当前目录的hash和子目录/子文件的hash都需要变更,这是一个灾难性的,更新性能会很差

那如何设计一个良好的表结构,满足功能(批量插入、批量更新【不用更新当前的唯一hash】)​,以解决如下问题:
2.批量插入(第一条已经解决没问题)
3.更新文件夹名称(不更新hash的化,性能就很高了,只需要更新当前的文件夹名称和子目录的path,)

解决方式:
a.设计冗余的文件夹名称和冗余的文件夹路径,文件信息的展示都是展示冗余的文件名和冗余的路径,之前的文件名和路径只是为了生成唯一的hash
b.如果复制过来的文件夹和当前的文件夹重名,修改待插入的文件夹名称(加时间戳),生成规则按照最新的文件夹+路径生成即可
c.如果目标目录之前的文件夹从a改成了b,另一个目录下的a现在需要拷贝到和当前b目录同级别的,就会导致问题(目标目录的文件夹名字虽然变了,但对应的hash值没有变,copy源a文件夹过来会导致生成的hash和目标b的hash值一样,这里解决方案是直接判断重名的有两种【文件夹重名,hash重名】,给重名的hash加一个时间戳生成就没问题了)

通过以上处理方式可以实现批量插入数据,批量更新数据,不用考虑唯一值hash的问题,为什么我对于这块有这些认识,因为本人当前就是面临着几千万条数据量的问题,之前只设计到第一种手段的程度,没有解决批量更新的性能问题,所以接下来新项目设计考虑到了这个问题。

调用生成的代码:
 

$hashes = generateFileHash(['path' => $file_config['file_root_path'], 'name' => $project_sn, 'salt' => $params['uid'], 'type' => 'd']);


/**
 * 根据一些参数生成文件表需要的hash字段值
 *
 * @param    array
 *
 * @return   array
 **/
function generateFileHash($params)
{
    //该生成hash只适合以下场景:1.工作流返回数据,初始化项目文件夹,为什么不适合其他情况:如果文件目录修改了,对应的hash不会修改,如果使用此方法生成的parent_hash就无法匹配的上父级的unique_hash
    //unique_hash的生成是可以用来插入数据的
    try {
        $verify = new \Custom\Org\Verify;

        //验证必填, path:路径, name:文件/文件夹名字, salt:如果当前是为了生成项目的则传项目创建者ID,如果是属于项目文件夹下的,也是传项目创建者ID, type:d(文件夹),f(文件)
        $verify->required($params, ['path', 'name', 'salt', 'type']);

        if ($params['path'][strlen($params['path'])-1] == '/') {
            $params['path'] = substr($params['path'], 0, strlen($params['path'])-1);
        }

        $count_path = explode('/', $params['path']);

        if ($count_path == 2) {
            return array(
                'unique_hash'    => md5($params['path'].'/'.$params['name'].'/'.$params['salt'].'/'.$params['type']), //当前行的唯一标识
                'privilege_hash' => md5($params['name'].'/'.$params['salt']), //用于第二层级 level:2的权限层级,一方面用于共享项目文件的查看,一方面是
                'parent_hash'    => md5($params['path'].'/'.$params['salt'].'/d'), //父级一定是一个文件夹
            );
        }

        //不是第二层目录的,只返回unique_hash,因为存在目录的变化性,且另两个都是从父目录得到的,所以不提供生产,防止错误使用
        return array(
            'unique_hash'    => md5($params['path'].'/'.$params['name'].'/'.$params['salt'].'/'.$params['type'])
        );

        

    } catch (\Exception $e) {
        throw new \Exception($e->getMessage());
    }
}


相关的数据表结构:


 

 

添加、修改 文件夹代码:

 

/**
     * 添加目录
     *
     * @param    string
     *
     * @return   array
     **/
    public function addDir($params)
    {
        \Db::startTrans();
        try {
            if (empty($params)) {
                throw new \Exception('参数有误');
            }

            //权限验证,这块后面还需要添加

            $verify = new \Custom\Org\Verify;

            //验证必填
            $verify->required($params, ['dir_name', 'dir_note', 'parent_hash']);

            $dir_info = $this->getFileDirInfoByUniqueHash($params['parent_hash']);
            if (empty($dir_info)) {
                throw new \Exception('没有对应的父类文件夹');
            }

            
            $repeat_dir_info = $this->getFileDirInfoByName($params['dir_name'], ['parent_hash' => $params['parent_hash'], 'privilege_hash' => $dir_info['privilege_hash'], 'type' => 'd']);

            if (!empty($repeat_dir_info)) {
                throw new \Exception('文件夹已存在');
            }

            $hashes = generateFileHash(['path' => $dir_info['path'], 'name' => $params['dir_name'], 'salt' => $dir_info['salt'], 'type' => 'd']);

            $repeat_dir_info = $this->getFileDirInfoByUniqueHash($hashes['unique_hash']);

            if (!empty($repeat_dir_info)) {
                //如果存在重复的unique_hash,给生成hash的因子加随机数
                $hashes = generateFileHash(['path' => generateUniqidSn().$dir_info['path'], 'name' => $params['dir_name'], 'salt' => $dir_info['salt'], 'type' => 'd']);
            }
                
            $time = time();
            $file_dir_params = array(
                'name'           => $params['dir_name'],    
                'privilege_hash' => $dir_info['privilege_hash'],
                'unique_hash'    => $hashes['unique_hash'],
                'system_dir'     => 'o',
                'type'           => 'd',
                'parent_hash'    => $dir_info['unique_hash'],
                'salt'           => $dir_info['salt'],
                'path'           => $dir_info['path'].$dir_info['name'].'/',
                'level'          => $dir_info['level'] +1,
                'time'           => $time,
                'file_size'      => '',
                'note'           => strval($params['dir_note']),
            );

            $this->addFileDirInfo($file_dir_params);

            \Db::commit();
        } catch (\Exception $e) {
            \Db::rollback();
            throw new \Exception($e->getMessage());
        }

        return true;
            
    }

    /**
     * 修改目录
     *
     * @param    string
     *
     * @return   array
     **/
    public function editDirByUniqueHash($unique_hash, $params)
    {
        \Db::startTrans();
        try {
            if (empty($params)) {
                throw new \Exception('参数有误');
            }

            //权限验证,这块后面还需要添加

            $verify = new \Custom\Org\Verify;

            //验证必填
            $verify->required($params, ['dir_name', 'dir_note', 'unique_hash']);

            $dir_info = $this->getFileDirInfoByUniqueHash($unique_hash);
            if (empty($dir_info)) {
                throw new \Exception('当前文件夹不存在');
            }

            if ($dir_info['level'] == '2') {
                throw new \Exception('根文件夹不允许修改');
            }
            
            $repeat_dir_info = $this->getFileDirInfoByName($params['dir_name'], ['parent_hash' => $params['parent_hash'], 'privilege_hash' => $dir_info['privilege_hash'], 'type' => 'd']);

            if (!empty($repeat_dir_info) && $repeat_dir_info['unique_hash'] != $unique_hash) {
                throw new \Exception('文件夹已存在');
            }
                
            $time = time();
            $file_dir_params = array(
                'name'           => $params['dir_name'],
                'note'           => strval($params['dir_note']),
            );

            $this->updateFileDirInfoByUniqueHash($unique_hash, $file_dir_params);

            // 如果修改了文件夹名字,需要更新它的子路径
            if ($params['dir_name'] != $dir_info['name'] && $dir_info['type'] == 'd') {
                $this->updateFileDirInfosByPath($dir_info['path'].$dir_info['name'].'/', ['path' => $dir_info['path'].$file_dir_params['name'].'/'], ['privilege_hash' => $dir_info['privilege_hash']]);
            }

            \Db::commit();
        } catch (\Exception $e) {
            \Db::rollback();
            throw new \Exception($e->getMessage());
        }

        return true;
            
    }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值