【问题标题】:Database design : preferred field length for file paths数据库设计:文件路径的首选字段长度
【发布时间】:2011-05-21 15:14:42
【问题描述】:

我必须将文件路径存储在 DB 字段中(/tmp/aaa/bbbC:\temp\xxx\yyy 等)。我真的不知道它们会持续多久。

鉴于这个http://en.wikipedia.org/wiki/Comparison_of_file_systems 和那个http://msdn.microsoft.com/en-us/library/aa365247.aspx,根据文件系统,理论上可能没有路径长度限制。

我想将此字段定义为LONGBLOBVARCHAR(very high value) 并不明智。我考虑过像VARCHAR(1024) 这样的东西,它应该适用于最常见(即使不是全部)的情况,而且不像数据库字段那么大。你会推荐什么?

谢谢。

【问题讨论】:

  • 另请参阅this answer,以更好地了解 varchar(max) 的幕后工作原理。

标签: sql-server database database-design


【解决方案1】:

为您打算支持的数据使用适当的长度。由于您使用的是 SQL Server,因此您应该使用 nvarchar(260) 作为存储路径名的上限,因为 that is the specification limit 用于典型的 Windows 机器。在某些情况下,您可以创建比这更长的路径,但是 Windows 资源管理器在处理它们时往往会出现问题。 SQL Server 无法处理超过 260 个字符的文件名。这包括 Linux 上的 SQL Server。

我可以证明SQL Server 在内部使用nvarchar(260) 列来存储SQL Server 数据库文件名,包括路径。检查sys.master_files视图的定义,我们看到如下T-SQL:

 CREATE VIEW sys.master_files AS
    SELECT
        database_id     = f.dbid,
        file_id         = f.fileid,
        file_guid       = f.fileguid,
        type            = f.filetype,
        type_desc       = ft.name,
        data_space_id   = f.grpid,
        name            = f.lname,
        physical_name   = f.pname,
        state           = convert(tinyint, case f.filestate     -- Map enum EMDFileState to AvailablityStates
                                when 0 then 0 when 10 then 0    -- ONLINE
                                when 4 then 7   -- DEFUNCT
                                when 5 then 3 when 9 then 3 -- RECOVERY_PENDING
                                when 7 then 1 when 8 then 1 when 11 then 1  -- RESTORING
                                when 12 then 4  -- SUSPECT
                                else 6 end),    -- OFFLINE
        state_desc      = st.name,
        f.size,
        max_size            = f.maxsize,
        f.growth,
        is_media_read_only  = sysconv(bit, f.status & 8),       -- FIL_READONLY_MEDIA
        is_read_only            = sysconv(bit, f.status & 16),  -- FIL_READONLY
        is_sparse           = sysconv(bit, f.status & 256), -- FIL_SPARSE_FILE
        is_percent_growth   = sysconv(bit, f.status & 32),  -- FIL_PERCENT_GROWTH
        is_name_reserved        = sysconv(bit, case f.filestate when 3 then 1 else 0 end), -- x_efs_DroppedReusePending
        create_lsn          = GetNumericLsn(f.createlsn),
        drop_lsn                = GetNumericLsn(f.droplsn),
        read_only_lsn           = GetNumericLsn(f.readonlylsn),
        read_write_lsn      = GetNumericLsn(f.readwritelsn),
        differential_base_lsn   = GetNumericLsn(f.diffbaselsn),
        differential_base_guid  = f.diffbaseguid,
        differential_base_time  = nullif(f.diffbasetime, 0),
        redo_start_lsn          = GetNumericLsn(f.redostartlsn),
        redo_start_fork_guid    = f.redostartforkguid,
        redo_target_lsn     = GetNumericLsn(f.redotargetlsn),
        redo_target_fork_guid   = f.forkguid,
        backup_lsn          = GetNumericLsn(f.backuplsn),
        credential_id       = cr.credential_id
    FROM sys.sysbrickfiles f
    LEFT JOIN sys.syspalvalues st ON st.class = 'DBFS' AND st.value = f.filestate
    LEFT JOIN sys.syspalvalues ft ON ft.class = 'DBFT' AND ft.value = f.filetype
    LEFT JOIN sys.credentials cr ON f.pname LIKE cr.name + N'%' COLLATE database_default
    WHERE f.dbid < 0x7fff -- consistent with sys.databases
        AND f.pruid = 0
        AND f.filestate NOT IN (1, 2)   -- x_efs_Dummy, x_efs_Dropped
        AND has_access('MF', 1) = 1

Microsoft Docs for sys.master_files 谈到了physical_name 专栏:

physical_name nvarchar(260) 操作系统文件名。

但我们不要相信这一点。我们看到物理文件名被引用为physical_name = f.pname。表别名“f”指向FROM sys.sysbrickfiles f。因此,SQL Server 将文件名存储在 sys.sysbrickfiles 中,这是一个内部表,只能从专用管理员连接或众所周知的 DAC 中看到。连接到 DAC 和 generating a temp table from the outputsys.sysbrickfiles,我们看到以下内容:

CREATE TABLE #sysbrickfiles
(
      brickid           int              NOT NULL
    , dbid              int              NOT NULL
    , pruid             int              NOT NULL
    , fileid            int              NOT NULL
    , grpid             int              NOT NULL
    , status            int              NOT NULL
    , filetype          tinyint          NOT NULL
    , filestate         tinyint          NOT NULL
    , size              int              NOT NULL
    , maxsize           int              NOT NULL
    , growth            int              NOT NULL
    , lname             nvarchar(128)    NOT NULL
    , pname             nvarchar(260)    NOT NULL
    , createlsn         binary(10)       NULL
    , droplsn           binary(10)       NULL
    , fileguid          uniqueidentifier NULL
    , internalstatus    int              NOT NULL
    , readonlylsn       binary(10)       NULL
    , readwritelsn      binary(10)       NULL
    , readonlybaselsn   binary(10)       NULL
    , firstupdatelsn    binary(10)       NULL
    , lastupdatelsn     binary(10)       NULL
    , backuplsn         binary(10)       NULL
    , diffbaselsn       binary(10)       NULL
    , diffbaseguid      uniqueidentifier NULL
    , diffbasetime      datetime         NOT NULL
    , diffbaseseclsn    binary(10)       NULL
    , redostartlsn      binary(10)       NULL
    , redotargetlsn     binary(10)       NULL
    , forkguid          uniqueidentifier NULL
    , forklsn           binary(10)       NULL
    , forkvc            bigint           NOT NULL
    , redostartforkguid uniqueidentifier NULL
);

如您所见,pname 列确实定义为nvarchar(260)

此外,如果我们尝试使用长度超过 260 个字符的文件名创建数据库,我们会看到返回错误:

消息 103,第 15 级,状态 3,第 7 行
该文件与“F:\ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGH.mdf”开始太长。最大长度为 259。

在 SQL Server 中使用除 nvarchar(260) 列之外的任何内容来存储文件名既浪费又产生技术债务。

列的长度在性能方面非常重要。列长直接影响:

  • 为针对该列的查询授予内存。当查询处理器创建查询计划时,它使用查询中存在的每一列的大小作为运行查询所需的内存量的基础。它不使用每列中存在的数据的实际大小,而是“猜测”数据的平均大小将是列最大长度的 50%。
  • 能够有效地索引列。较大的列创建显着更大的索引。与较小的索引相比,较大的索引需要更多的内存和磁盘吞吐量。 SQL Server 的非聚集索引的最大键长度为 1700 字节(自 SQL Server 2016 起),聚集索引的最大键长度为 900 字节。如果您尝试在大于这些最大数量的列上创建索引,则会出现错误,并且可能直到运行时才会出现错误,此时修复成本非常高。
  • 基于字符的主键/外键性能受到较大列长度的严重影响。当通过外键引用主键时,每个外键对内存、磁盘和 I/O 的大小要求都是重复的。以Customer 表为例,其中键是CustomerName 列,定义为varchar(500)。现在,每个引用客户的表都需要一个 500 字节的 CustomerName 列。如果将该列定义为 varchar(100),则每个引用这些列的查询每行将在内存和磁盘 I/O 中节省 200 个字节。
  • Erik Darling 表明 Predicate Pushdown 不适用于 (MAX) 数据类型,这会严重限制性能。

【讨论】:

  • 虽然这是一个非常彻底和详细的答案,但它在很大程度上假定所有存储的路径都是限制为 260 个字符的路径。如果它们是 unix 路径、URL 等,则不一定如此。
  • @CervEd - 这是一个很好的观点,特别是因为 SQL Server 可以在 Linux 上运行。话虽如此,关键是不要仅仅出于我的答案中提到的内存和性能原因而盲目使用 varchar(max) 。选择一个合理且您准备支持的最大长度。
  • 不,我明白了,我认为这在大多数场景和工作负载中都有意义,但在某些情况下 260 是不够的。但在这种情况下,操作会失败,此时可以重新访问 VARCHAR(n) 是否应该更大。
【解决方案2】:

如果您使用 SQL Server,很高兴知道 Microsoft 正在使用 nvarchar(260) 字段在系统表中存储文件路径和名称(如 sys.database_filessys.sysaltfiles,或sys.master_files)。

Column name      Data type        Description
-------------    -------------    ---------------------------
physical_name    nvarchar(260)    Operating-system file name.

好的做法是使用相同的格式来存储您的路径和文件名。

当然,您需要在 UI 中强制执行大小,以确保在 INSERT 或 UPDATE 期间它不会被截断。

【讨论】:

  • 我发现这个答案对于定义 Windows 文件名长度(不包括文件夹路径)很有用。
  • @Paul - 实际上,260 个字符的限制是包括路径。
【解决方案3】:

无法预测文件路径的长度。它可能很短,如'C:\',也可能很长,如'C:\Program Files\Microsoft SQL Server\110\LocalDB\Binn\Resources\1033',甚至更长。但是在数据库级别使用VARCHAR(MAX) 之类的东西没有害处

See Maximum size of VARCHAR(MAX)

【讨论】:

  • 其实使用(max) 类型有的危害。原因见我上面的回答。 Windows NTFS 文件系统支持最长为 32768 个字符的路径,但仅在使用 Unicode API 时才支持。使用长路径名时,在路径前加上字符 `\\?`。 SQL Server 不支持长文件名,即使您尝试使用 unicode API 方法。自 2019 年起,SQL Server 仅支持长度不超过 260 个字符的文件名。
【解决方案4】:

该字段的长度应与一盒字符串的长度相同。

正如询问文件名的长度就像询问一个字符串的长度一样,询问路径的长度就像询问一个未知大小的盒子中所有字符串的长度。

因此,在没有其他信息的情况下唯一明智的选择是不限制长度,例如NVARCHAR(MAX)

【讨论】:

  • 其实,使用(max) 类型有的危害。原因见我上面的回答。 Windows NTFS 文件系统支持最长为 32768 个字符的路径,但仅在使用 Unicode API 时才支持。使用长路径名时,在路径前加上字符 `\\?`。 SQL Server 不支持长文件名,即使您尝试使用 unicode API 方法。自 2019 年起,SQL Server 仅支持长度不超过 260 个字符的文件名。
【解决方案5】:

我建议您不要将路径存储在现有表中。创建一个新表,其中包含一个顺序计数器作为聚集主键和一个数据库程序最大长度的字符列。我使用 SQL Server,所以我会使用 varchar(max)。

在您的数据表中创建一个列来保存“路径”表的主键。先插入“路径”表,然后在数据表中使用主键作为外键。

将值存储在另一个表中的好处是它不会影响基表的数据大小。不涉及“路径”的基表查询不会因必须拉入增加 IO 流量的大字符值而受到影响。

【讨论】:

  • 我想应该谨慎使用这种技术,因为每次我们想要主表数据+路径时,它都会涉及额外的连接(及其性能影响)......
  • Frosty Z 你是对的,应该像任何设计一样谨慎使用。我的经验是表空间 (IO) 的减少远远超过任何连接成本。此外,主表将在连接之前通过 where 子句减少,因此再次为连接检索数据的开销最小化
  • 在不了解表结构的情况下很难判断该技术的有效性。如果这是一个仅包含文件详细信息的表,那么将路径保留在此表中可能是有意义的。如果大多数表访问不需要读取路径,那么它可能是值得的,但保存将主要影响全表扫描。当然,它总是会降低路径的检索效率。
【解决方案6】:

您可以使用VARCHAR(MAX)NVARCHAR(MAX)

这些是可变长度字段,这意味着它们旨在存储不同长度的值。较长的值比较短的值没有额外的开销。

定义MAX 表示该字段最大可达2GB。

来自MSDN (varchar),nvarchar 有类似的文档:

当列数据条目的大小变化很大时使用 varchar。

当列数据条目的大小变化很大并且大小可能超过 8,000 字节时使用 varchar(max)。

【讨论】:

  • 其实使用 (max) 类型是有危害的。请参阅我的答案以了解原因,尤其是不必要的大内存分配。 Windows NTFS 文件系统支持最长为 32768 个字符的路径,但仅在使用 Unicode API 时才支持。使用长路径名时,在路径前加上字符 \\?。但是,即使您尝试使用 unicode API 方法,SQL Server 也不支持长文件名。自 2019 年起,SQL Server 仅支持长度不超过 260 个字符的文件名。
【解决方案7】:

我会推荐 VARCHAR(2048) 甚至 VARCHAR(1024) 因为文件路径通常不是 2000 个字符长。

【讨论】:

  • Windows NTFS 文件系统支持最长为 32768 个字符的路径,但仅在使用 Unicode API 时才支持。使用长路径名时,在路径前加上字符 `\\?`。 SQL Server 不支持长文件名,即使您尝试使用 unicode API 方法。自 2019 年起,SQL Server 仅支持长度不超过 260 个字符的文件名。
猜你喜欢
  • 1970-01-01
  • 2014-10-10
  • 1970-01-01
  • 2018-01-08
  • 2011-01-20
  • 2021-08-20
  • 2022-08-14
  • 2011-05-29
  • 2020-11-12
相关资源
最近更新 更多