V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liyafe1997
V2EX  ›  PostgreSQL

用 PostgreSQL 存图片等 binary 有什么坑吗

  •  1
     
  •   liyafe1997 · 2 天前 · 5162 次点击

    如题,大概每个主条目下面关联有几张到一两百张不等的图片/文档之类的附件,每张图最大 500KB (入库时超大会自动压缩),文档也是不超过 500KB 。

    目前测试数据(几十/百来个主条目)跑起来感觉挺香的,也没见什么性能问题。特别是设置好外键和 cascade delete 之后,主条目删除会自动把存 binary 的表中关联的附件一并干掉,比放文件系统好维护。文件系统还要自己处理数据关联和清理逻辑。

    就是不知道以后数据量大了会有什么坑,预计之后主条目有数万个这样的量级。

    89 条回复    2025-09-25 09:44:46 +08:00
    KongLiu
        1
    KongLiu  
       2 天前   ❤️ 2
    数据库占用 IO 太高了,高并发就废了,而且以后配合 CDN 做加速也不方便,还有数据库存储一般都比对象云存储贵把
    wangtian2020
        2
    wangtian2020  
       2 天前
    我 sqlite 都不敢这么存
    liyafe1997
        3
    liyafe1997  
    OP
       2 天前
    @KongLiu 没有什么太高的并发需求,正常用户应该没考虑到高并发这个量级,但是怕的是 Internet 上乱跑的爬虫/搜索引擎 Bot 之类的。
    「以后配合 CDN 做加速也不方便」,这个为什么呢?我还想着以后用 CDN 来帮缓存一些图,不用每次都进我的后端/数据库 IO 。
    zhengfan2016
        4
    zhengfan2016  
       2 天前   ❤️ 4
    后面接手项目的开发:安卓架构(bushi
    JYii
        5
    JYii  
       2 天前
    @liyafe1997 #3 你自己都说出隐患了,一个查询 api 就能把你 DB 打死。
    Vegetable
        6
    Vegetable  
       2 天前
    IO 很难受,这么弄也很难做到正常文件服务器的能力,本来如空气一般自然的功能你都得自己实现,比如 http 304 。
    你如果一开始选了这个架构,后边会越来越难受,只有一开始能爽点,与其后边难受,不如一开始做好资源管理,把删除的逻辑弄好。
    KongLiu
        7
    KongLiu  
       2 天前
    @liyafe1997 用 OSS 配置一下就好了,你这种方式全靠自己控制。搞不到 OSS 配置本身就提供的功能,你自己要写一大堆
    liyafe1997
        8
    liyafe1997  
    OP
       2 天前
    @JYii 感觉在 HTTP Server 或者套一层 sqiud 做缓存能解决?甚至最外面还有一层 CDN 也能缓存?总之就是不用每次 HTTP GET 都去查 DB 。
    liyafe1997
        9
    liyafe1997  
    OP
       2 天前
    @KongLiu 那还得装一套 OSS
    xtreme1
        10
    xtreme1  
       2 天前
    "条目"具体是什么东西?
    zephyru
        11
    zephyru  
       2 天前
    CDN 一般不都是通过图片的文件路径地址去访问的?
    存文件系统里直接上传对应文件保持路径,项目里替换下请求的地址就好了。
    你这种模式也并不是不能用 CDN 但确实不方便。
    小文件存数据库主要问题还是开销大吧,基本没见过正经系统把文件直接存数据库里的。
    liyafe1997
        12
    liyafe1997  
    OP
       2 天前
    @xtreme1 你就理解成类似 V2EX 的帖子就好了,一条帖子下面可能挂有很多图片甚至文档等附件。
    AutumnVerse
        13
    AutumnVerse  
       2 天前 via iPhone
    以前自己玩具项目这样搞过

    就像楼上说的,数据库 io 扛不住,而且没法优化
    liyafe1997
        14
    liyafe1997  
    OP
       2 天前
    @zephyru CDN 请求我的 Server 也是 HTTP GET 哇,我的后端访问数据库中的图片也是 HTTP GET ( RESTful API ),查 db 再把图 binary data 通过 http response 弹回给你
    liyafe1997
        15
    liyafe1997  
    OP
       2 天前
    @AutumnVerse 现在想到的解决方案是在 HTTP Server 上做缓存,不知道加上这个之后,还有什么坑
    AutumnVerse
        16
    AutumnVerse  
       2 天前 via iPhone
    正经做法是数据库存 oos 地址,后端返回 oos 地址,前端直接请求 oos ,这样减少服务端 io
    AutumnVerse
        17
    AutumnVerse  
       2 天前 via iPhone
    @liyafe1997 缓存里面放图片?那你缓存的多大啊,你不怕缓存被撑爆吗
    liyafe1997
        18
    liyafe1997  
    OP
       2 天前
    @AutumnVerse 不想(懒得)再引入一套 OSS 了🤣
    AutumnVerse
        19
    AutumnVerse  
       2 天前 via iPhone
    直接放磁盘路径都比放数据库好,直接放磁盘有一些文件系统,操作系统相关的优化方案,而且可以挂载远程磁盘的方式做分布式
    liyafe1997
        20
    liyafe1997  
    OP
       2 天前
    @AutumnVerse 直接放磁盘的话...我还是考虑 OSS 吧🤣
    感谢解答
    zephyru
        21
    zephyru  
       2 天前
    @liyafe1997
    一般来说,大家说图片 CDN 可能是 OSS 那种模式的。
    所有的流量全部走 CDN 不会再打到服务器上。
    你这种情况,可能只适合使用套在域名上那种作为前置缓存的 CDN (缓存规则,失效诸如此类的先另说)。
    你想使用传统使用 CDN 的方式,得先从 DB 里下载出来再上传,这就是麻烦的点。
    这么看来主要的坑点,除了开销大,就是传统的方案需要你自己再另外实现。
    xtreme1
        22
    xtreme1  
       2 天前
    额, 就是富文本吗. 现在方案都很成熟了啊, 文本存 pg, 附件上 oss, 索引上 es.
    KongLiu
        23
    KongLiu  
       2 天前
    @liyafe1997 能用和好用是两码事,你这个只能算是能用水平,远远谈不上好用。OSS 配合 CDN 能很容易的改变缓存策略,你这个每一点改动都要改代码。除非你这个项目是玩具项目做出来不用考虑维护的,不然早晚有坑等着呢,你现在的想象中的美好都基于没几个人用的情况。
    chambered
        24
    chambered  
       2 天前
    我觉得 pg 的 bytea 就是用来存这些二进制数据的,所以几十 kb 应该没什么问题,太大肯定不行。还是看文档怎么介绍的吧
    skallz
        25
    skallz  
       2 天前   ❤️ 1
    之前只有富文本这么干过,当时是为了图省事把里面所有图片资源转成 base64 嵌在富文本里,但是后面也是相当难受,最后把富文本生成一个 json 文件传 oss 了
    ytmsdy
        26
    ytmsdy  
       2 天前   ❤️ 1
    数据库备份的时候,你就会知道这是一个噩梦,备份文件动不动就是上百 G 。
    另外数据还原也是一个问题,很慢,很烦。
    我们之前把身份证照片转成了 base64 ,存到了数据库里,然后自动备份服务器的空间动不动就满了。贼烦
    donaldturinglee
        27
    donaldturinglee  
       2 天前
    正经做法还是上 OSS
    SoviaPhilo
        28
    SoviaPhilo  
       2 天前
    除非硬件资源限制了, 最好是能分离, 毕竟如楼上说的方案很成熟了。
    而方案成熟那是无数前人拿血和泪换来的。

    另外, 数据量大了, 你现在的设计绝大多数都会是坑, 不止是图片。毕竟大数据量和小数据量在设计上肯定是不一样的
    kenilalexandra
        29
    kenilalexandra  
       2 天前
    OSS 的出现就是为了解决你不知道的“有什么坑吗”的
    geminikingfall
        30
    geminikingfall  
       2 天前
    这是个什么操作?这不上块存储吗?
    billbob
        31
    billbob  
       2 天前
    上 OSS
    liyafe1997
        32
    liyafe1997  
    OP
       2 天前
    @ytmsdy base64 那会增大很多吧,正确不应该用 bytea?
    jjx
        33
    jjx  
       2 天前
    500k 就是 5m 带宽了

    两个人并发就带宽报警了

    这种还是用 7 牛云之类的吧
    june4
        34
    june4  
       2 天前
    楼上都没给出实锤证据啊,说明可行?
    存成文件也是要 IO 的,且存数据库前面也可以配缓存,不管是文件缓存还是 CDN 缓存

    可能就是备份比文件方式不方便一点,如果图片量是巨大的话。
    sagnitude
        35
    sagnitude  
       2 天前   ❤️ 9
    没什么太大问题,不要被吓到了。
    平时这种应用场景很少,所以大家没做过,可能第一反应就是反对。
    我就举一个例子,GIS 地图服务器,比如卫星图的图片,一般都是存数据库里的,几十 TB 照样存,我见过的,sqlite ,postgresql ,mongodb ,都有。比如 postgresql 直接就有一个 PostGIS 扩展来做这个。
    全国地图总数据量应该是 10 亿图片量级,存数据库也没问题。
    才几万个根本无所谓的。

    而且 sqlite 也可以的,关键是做好分库分表,我自己就做过 sqlite 地图服务,每个 sqlite 数据库最多存储大概 1-2GB 的数据,没什么问题。

    为什么不用 OSS 或者文件系统?因为他扛不住哈哈哈,我一次性导 20 亿文件进去,OSS 要几个月,文件系统也要等一个月,RAID 也没什么用,存储最佳方式就是数据库,可以解决小文件碎片化的问题。
    irrigate2554
        36
    irrigate2554  
       2 天前
    规模小的话应该没啥问题,memos 就支持存储附件到数据库,我也是这么用的。就怕规模越来越大,加上动不动 select * , 这样总有一天要炸。
    irrigate2554
        37
    irrigate2554  
       2 天前
    有一个明显的劣势就是存视频的话流式播放功能就无法实现了。
    CEBBCAT
        38
    CEBBCAT  
       2 天前
    @sagnitude #35 请教下,是这个逻辑吗:OSS 是网络 IO(耗时),FS 是系统调用,SQLite 跑在用户空间,众所周知有时候可以比硬盘快
    sagnitude
        39
    sagnitude  
       2 天前
    @CEBBCAT
    1. OSS 每一个操作都是网络,overhead 超级大,每个 stat 或者 mv cp 耗时增加哪怕 1 毫秒,乘以 20 亿的量级就是 23 天。几个月过去,项目都不要做了
    2. 数据库可以用内存加速。
    3. 文件系统对于巨量小文件的支持比较烂,碎片大,索引效率低,inode 就要占好几分之一的空间。
    对数据库来说,读写是在单个文件内部完成的,不需要文件系统操作 open() close()
    xiangyuecn
        40
    xiangyuecn  
       2 天前
    这么丁点数据量随便存,简单粗暴的存储架构,不要听楼上的,那些都是没卵用的提前优化

    说不定项目黄了,数据库压力都不一定有上来
    moen
        41
    moen  
       2 天前
    pg 有个内置扩展叫 lo ,用来存大对象
    iyaozhen
        42
    iyaozhen  
       2 天前
    可以这样吧,没啥问题。

    而且你又不是经常 select * ,比如帖子附件,你可以只展示名称。下载再查出内容字段

    当然 OSS 确实是标准做法
    laminux29
        43
    laminux29  
       2 天前
    全存数据库,最大的优势是开发与调试都极其方便,节约了程序员大量时间。就算后期性能不足,直接堆硬件也能解决。

    大部分中小项目,这么跑一点问题都没有,而且数据库天生做负载均衡与 HA 都更方便。

    当然,非常特殊的追求性能的项目,以及用户或数据量众多的大厂项目,可能没办法这么玩。
    KagurazakaNyaa
        44
    KagurazakaNyaa  
       2 天前
    要存这种数据其实可以考虑用 mongo 这种文档数据库了,有 gridfs
    xjzshttps
        45
    xjzshttps  
       2 天前
    玩没问题,
    但是数据库库扩展成本比文件系统大太多了。
    whoosy
        46
    whoosy  
       2 天前
    @sagnitude #35 你这个有很大的参考价值
    delacey
        47
    delacey  
       1 天前
    到时候给你塞两张 1G 的图片,你的并发数不就炸了
    yplam
        48
    yplam  
       1 天前 via Android
    存文件系统的话通过 io copy 或者 zero copy (譬如 NGINX 的 sendfile )基本上可以忽略静态文件的内存开销,存数据库你是不是要整个文件内容都加载到内存然后再返回?或者你又要在前面加个 proxy cache ?
    liyafe1997
        49
    liyafe1997  
    OP
       1 天前
    @sagnitude 我也觉得,左想右想都觉得没什么不妥,IO 问题就是在 HTTP Server 上加缓存的事。
    liyafe1997
        50
    liyafe1997  
    OP
       1 天前
    @delacey 看题,不超过 500K
    liyafe1997
        51
    liyafe1997  
    OP
       1 天前
    @laminux29
    @xiangyuecn
    是的,我的心态也是这样,前期不想操什么高并发这些心。如果我这个项目以后真的有高并发那一天,那这些问题都不是问题了。
    xiaohupro
        53
    xiaohupro  
       1 天前
    还是建议最好使用单独的文件系统,而且现在很多开源的都很不错,也有对应的 API 提供,很方便,就像我自己用的就是 Zipline: https://zipline.diced.sh/docs/get-started
    wenning
        54
    wenning  
       1 天前
    没什么问题, 但是你参考一下云服务数据库磁盘的价格, 如果合适倒是可以的; 参考一下阿里云 rds 存储包,5t1 个月 6k 人民币
    Huelse
        55
    Huelse  
       1 天前
    关系数据库还是老老实实存数值字符串吧,其他类型的都有对应存储方案
    DesmondCobb
        56
    DesmondCobb  
       1 天前
    @sagnitude #35 狠狠赞同了,之前搞医学数据库,数据集、影像、就诊记录等等文件又多又碎,用 SQLite 来存效率比文件显著提高,进行检索、关联也更容易
    kirory
        57
    kirory  
       1 天前
    没什么问题,CDN 基本能全命中
    最大问题就是数据库需要手动扩容
    FS ,OSS 实际上也是 KV 数据库,这里对性能没有要求到要单独开服务器的程度,我不觉得为了*可能*的性能优势放弃单数据库的事务和一致性有好处
    kirory
        58
    kirory  
       1 天前
    @irrigate2554 BLOB 是支持 stream 和 seek 的,肯定能实现流式播放,总不会直接直接把上 G 的文件直接全读内存里吧
    thealert
        59
    thealert  
       1 天前
    @sagnitude #35 网络地图,2 进制存数据库根本不合理,你是查出数据库 2 进制返回给客户端,多个地图区块一次性查询数据库,并发用户量请求数据量一大接口直接爆掉了,每个区块需要做异步请求 cdn 才合适
    thealert
        60
    thealert  
       1 天前
    @thealert 而且数据库主从同步会造成巨大的系统开销和延迟
    thealert
        61
    thealert  
       1 天前
    @thealert #59 单机地图你甚至都不需要数据库,自定义文件数据结构和解析,参考游戏客户端即可
    sagnitude
        62
    sagnitude  
       1 天前   ❤️ 1
    @thealert 1. 基于 sqlite 分库分表,数据库根本不需要主从同步,平均 1GB 一个数据库文件,直接拷贝数据库文件分发到其他服务器就行,增加并发量可以简单的通过增加物理磁盘数量实现
    2. 并发访问地图数据有内存缓存池子,sqlite 可以自己把部分数据加载到内存中读取,为何并发量会把接口爆掉?直接从内存返回数据还能被爆掉,那这台服务器上跑任何服务都会被爆掉
    3. 地图请求天生都是连续的,因为用户看地图一定是一片连着一片看的,我收到第一个请求,直接把附近的数据加载到内存里,大概率马上就会被请求到,这一段读性能可以得到大幅优化
    4. 不是单机地图,我都说了是 GIS 服务,提供标准 OGC 接口,WMS ,WMTS ,的 tile 服务
    5. 为什么优先存数据库?地图数据不仅仅只有卫星图这种静态图像,还有高程图这种要随时读取内容并且用于计算的图像数据,还有矢量地图,架构肯定要统一
    sagnitude
        63
    sagnitude  
       1 天前
    @thealert 而且最重要的原因上面也说了,磁盘存储和 OSS 存储和读写效率太低,我部署一次就要一个月,现在我的数据库导入、导出、更新大部分都可以在内存里做,最低最低也可以达到硬盘的读写极限,比读写小文件性能高太多了
    yh7gdiaYW
        64
    yh7gdiaYW  
       1 天前
    @sagnitude 我猜你的这些场景不需要频繁删改老数据,会有惊喜
    yh7gdiaYW
        65
    yh7gdiaYW  
       1 天前
    说白了这个存数据库这个方案至少在初期可行,但除了给你开发省点力气,与存对象存储的方案相比没有任何好处。看你对现在这份干多久的预期了,过两年就跑就怎么省事怎么来
    thealert
        66
    thealert  
       1 天前
    @sagnitude #62
    1 有些业务需要主从同步,跨机房延迟很高,分库分表解决不了高延迟问题
    2 本质你是加载了缓存,和数据库就没关系了,但是图片是 ugc 的如何保证实时同步到缓存,我说的是用户请求大你每个用户都返回二进制图片网络接口带宽直接爆了
    3 还是缓存问题其实就是 cdn
    billbob
        67
    billbob  
       1 天前
    没坑啊! PostgreSQL 就支持啊,只要支持,就能用.

    业务增长,是否合理,那是未来你要考虑的,你前期设计个罗马出来,你也造不出来啊.

    开发时间允许,可以放 OSS
    sagnitude
        68
    sagnitude  
       1 天前   ❤️ 1
    @thealert 如果抵达后端服务器的请求能把带宽撑爆,后端实现方式是数据库还是文件有区别吗; CDN 实际上就是把重复的直接拦截在前面了,我用数据库也可以用 CDN ,这个不影响
    julyclyde
        69
    julyclyde  
       1 天前
    文件系统也不过是(多级索引的)数据库
    KV 存储也不过是数据库


    用关系型数据库保存,其“不好”其实是取决于这个数据库的设计开发水平不好,而不是天生这个形式就不适用于这个用途
    yannxia
        70
    yannxia  
       1 天前
    就没什么优点吧?
    thealert
        71
    thealert  
       1 天前
    @sagnitude #68 存在数据库的话不需要同步数据么,而且你这种查库服务的算接口请求把,直接返回大字节的接口数据会给其他业务接口造成压力,具体建议参考下百度,高度地图的存储方案就知道了
    thealert
        72
    thealert  
       1 天前
    @sagnitude #68 因为服务器对客户端的 IP 有连接数量的限制,所以地图瓦片的服务器会提供几个子域,浏览器均匀请求这些子域,来绕过 IP 限制,例如 OSM 地图服务有三个子域,a.tile 、b.tile 、c.tile ,它们指向的是同一个 CDN (内容传送网络)。
    gooin
        73
    gooin  
       1 天前
    @sagnitude 附议。 刚好这两天做了一个存储到 Mbtiles(sqlite)的功能, 看一下它的 Schema
    ```
    CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);
    ```

    > MBTiles is a specification for storing tiled map data in SQLite databases for immediate usage and for transfer. MBTiles files, known as tilesets, MUST implement the specification below to ensure compatibility with devices.

    https://github.com/mapbox/mbtiles-spec/blob/master/1.3/spec.md
    sagnitude
        74
    sagnitude  
       1 天前   ❤️ 1
    @thealert 浏览器到服务器的 TCP 连接是有限制数量,但 http/2 建立的是长连接,已经没这个问题了;用多个子域名去做这个是以前的解决办法

    如果数据实时性要求很高,我会改用 postgres 或者 oracle 或者 sqlserver ,我选型就是因为我的更新频率低
    sagnitude
        75
    sagnitude  
       1 天前
    @gooin 对的,就是 mbtiles ,基于 sqlite 做的扩展,在需要存储 metadata 的情况下,只能选择数据库存储,表很简单,迁移到其他数据库也一样
    sagnitude
        76
    sagnitude  
       1 天前
    @thealert 地图数据和业务数据肯定不是一个数据库,也不会和业务逻辑跑在同一个带宽、同一台服务器上,没有这个挤占问题,给地图服务的服务器就纯粹为了提供地图 API ,所以他的吞吐量就完完全全是给 tile 服务的。
    adoal
        77
    adoal  
       1 天前   ❤️ 1
    没必要哄着 5 个并发访问的客户,写着 100 个并发余量的程序,操着 100 万个并发梦的心思。
    thealert
        78
    thealert  
       1 天前
    @sagnitude #74 路况瓦片这种频繁更新的情况,方案是频繁写入数据库然后从库读取,阁下如何对敌?
    sagnitude
        79
    sagnitude  
       1 天前
    @thealert 路况是矢量图,不会存成图片,矢量图本身数据量就很小,同步压力很小,且不说数据库实时同步,就算用文件我也可以做比如三轮文件轮流切换更新
    sagnitude
        80
    sagnitude  
       1 天前
    如果有一天我有机会做全国的路况图部署,我肯定是在业务服务器上把道路数据直接切成矢量切片,然后准备一个大内存的机器,直接把 sqlite 或者其他数据库做全内存存储,比如 sqlite 支持 memory 模式。全国卫星图才 12TB ,矢量图最多一百 G ,内存全缓存绰绰有余
    thealert
        81
    thealert  
       1 天前
    @sagnitude #79 那估计你是没做过百度,高德的这种地图服务,如果他们用的是你这种模式当我没说就好
    sagnitude
        82
    sagnitude  
       1 天前
    @thealert 你也知道他们是面对十几亿人的服务,我可没说我这套服务可以无缝 scale 到全国十几亿客户端,我只是告诉楼主,至少在他的公司做到百度高德的级别之前,他这样做没问题
    cookgo
        83
    cookgo  
       1 天前   ❤️ 1
    我发现我的某个项目和楼主的应用场景很像,但是我用的 MySQL 。我是用 blob 存储二进制数据,这个二进制数据就是用户在前端手写签名后,把签名图片转成二进制存在一个独立的中间表。
    为啥这么做?离线部署的业务,服务器环境不可靠,所有数据能存数据库就存数据库,出问题了检查数据库数据就可以了,防止背锅。
    thealert
        84
    thealert  
       1 天前
    @sagnitude #82 好的兄弟,明白
    bronyakaka
        85
    bronyakaka  
       1 天前
    搭建个本地对象存储存图片,数据库存 id 即可;嫌麻烦直接本地建多级目录存储,数据库存路径;
    qqjt
        86
    qqjt  
       1 天前
    用就是了,io 不是问题,PostgreSQL 就是吊,一把梭。
    liyafe1997
        87
    liyafe1997  
    OP
       1 天前
    @adoal 正解,我的心态就是如此
    cj323
        88
    cj323  
       1 天前 via iPhone
    刚毕业第一份工作用 mongo 干过,唯一遇到的坑是 mongo 当时不支持几十 MB 的文件。本来想修,结果当时带头人直接怼了用户不让存大文件。后来也有新同事说改对象存储更正规些,也被带头人怼回去了哈哈。讲究一个不到万不得已不优化。
    cs8425
        89
    cs8425  
       12 小时 51 分钟前
    GIS 相关+1 @sagnitude #35 是正解
    小碎档太多了, 不是塞 db 就是塞 zip 这类的东西聚合成较大的档案来储存跟交换
    尤其是 3D 相关的, 一个图层只包含小小一个区域随便就 700 多 M(700 多 x 百万)个档案, 至少我司这边的资料大多都是
    一般常用 WMTS 图砖也不少, 能算出多少数量
    例如 level 8 这层, 全球范围就是 4^8 = 65536 个档案
    然后一般提供的 level 都是 0 到 18~22 左右
    也就是 4^0 + 4^1 + 4^2 +....+4^22 个档案...
    各位可以自行算一下(狗头
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2793 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 86ms · UTC 14:36 · PVG 22:36 · LAX 07:36 · JFK 10:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.