REST 设计困惑?Mysql 是直接存字符串好还是数字好?

现在正在做 REST API 的设计,在设计过程中遇到了一些困惑,问题是这样的:

比如我有一个订单表

order_id products status
1 笔记本电脑 canceled
2 华为手机 finished
3 小米手环 delivering

获取所有订单的接口设计如下,其中有一个可选参数是 status 可选值为 canceledfinisheddelivering

GET /order?status=canceled //已取消的订单

GET /order?status=finished//已结束的订单

GET /order?status=delivering //配送中的订单

这样设计API,可读性还是很好的。但对这里的 status 字段有一些疑问,该字段在数据库中,是直接存储为字符串?还是存储为数字?

由于需要保证API可读性好,所以如果用 数字来存储 status ,那么就需要管理字符串与数字之间的对应关系。

比如说:canceled => 0finished => 1, delivering => 2

但是这样子,就需要在程序上,对status做一些转换,会无形增加一些程序的复杂性。

我的问题是:能不能直接把 status 字段直接存储为字符型?,就像上面的设计一样。如果数据量大会不会造成性能问题?或者其他一些问题? 谢谢!

阅读 13.2k
评论
    18 个回答
    freewolf
    • 4.6k

    一般是这样的 status一般是按顺序来排的数字
    比如
    0 - 订单生成
    1 - 出库
    2 - 已发货
    3 - 派送中
    4 - 已签收
    5 - 已评价
    6 - 已经取消
    这样 你可以 通过status<4 查看没完成的订单
    上面只是举例 你还应该有别的状态 退货 付款 退款啥的
    这样是比较好的

    评论 赞赏
      RunSN
      • 1.5k

      设计过些数据库, 类似于这种状态, 最早使用的是使用 int 来存储的, 因为相对于 varchar int 的效能是高的, 不过随之而来的问题是, 因此业务的问题, 会经常有查询, 但是在写 sql 的时候经常会忘记相应数字所对应的状态, 每次又要去找, 很烦, 加之又从互联网的某个角落得 知, 在 postgreSQL 中其实 int 和 varchar 的效能问题其实是没差别的(未经验证, 至少是我未验证过), 后一想就算是存储 varchar 状态最长把字符控制在十个字符内, 不会有很大的问题, 而且类似于状态这样的经常查询字段也会建立一个索引, 基本上已经可以了.
      之前也听一个前辈说过, 他那有个库, 数据很多, 状态是 int 的查询速度是大于 varchar 的.
      目前了, 状态字段又换成 int 型了, varchar 的便于识别的所带来的坏处就是在写 sql 语句的时候状态的条件需要多加 ' (where stat='open') , 对的 我就是这么懒.. 人都会偷懒, 而且也许会带来性能上的提升 何乐而不为了, 关于之前说的记不住状态, 有所得就有所失.
      而且类似于这样的状态 如果存储的 int 型, 转换的话, 在 java 中可以定义隔枚举类作为对应的状态值, 这样程序判断会相对简单些.
      关于说的数据大的问题, 如果你的表数据不会达到几百上千万的量, int varchar 的差别或许并不是很大, 当数据达到或者超出这个量的时候, 或许数据库都不一定能做到高效的操作了, 会考虑更多高效的方式, 像 nosql solr 等.
      结论的话:
      为了便于自己认识使用 varchar
      为了高效(或者说专业设计)使用 int
      如果确定用数字存储的话可以考虑用 tinyint 这个相对来说效能会高于 int 这个数值范围是小于 int 的, 不过是 postgresql 的话就不行了这数据库不支持 tinyint 类型

      评论 赞赏

        其实这个用orm处理就好了

        clipboard.png

        查询一般是这样的

         Order.where(status: Order.statuses[:shipped])
         
        
        评论 赞赏
          twoyao
          • 307

          MySQL 支持enum类型,实际存储得是tinyint。可读性和效率兼具。

          针对你的情况我举个例子:

          首先看mysql这边的设置

          CREATE TABLE t_order (
              order_id int,
              products varchar(64),
              status ENUM('canceled', 'finished', 'delivering')
          );    
          
          insert into t_order(order_id, products, status)
          values (1, "笔记本电脑", "canceled"), (2, "华为手机", "finished"), (3, "小米手环", "delivering");
          mysql> select * from t_order;
          +----------+-----------------+------------+
          | order_id | products        | status     |
          +----------+-----------------+------------+
          |        1 | 笔记本电脑      | canceled   |
          |        2 | 华为手机        | finished   |
          |        3 | 小米手环        | delivering |
          +----------+-----------------+------------+
          3 rows in set (0.00 sec)

          程序端只要按字符串访问就可以。ORM对枚举也有很好的支持,以java+jpa为例子。

          public enum OrderStatus { canceled, finished, delivering };
          
          @Enumerated(EnumType.STRING)
          private OrderStatus status;
          评论 赞赏

            int虽然不是那么容易看明白,但效率高很多,而且也有助于楼上说的做比较。可读性,可以使用类似枚举的手段来解决。前端传递来字符串参数可以试用哈希表转换成int的数值再做数据库查询。

            数值和状态之间的对应关系一定需要用文档规范出来,写代码的时候查文档就行,方便不同开发人员之间协同开发。这方面没有规范的文档,即便你用字符型也不见得所有人都能看懂。

            提一点,考虑将来状态可能会增加,建议不要取连续值(即“1 2 3 4 5”),类似“10 20 30 40 50”这样能够允许你以后增加状态的时候可以插入。

            评论 赞赏

              这种情况,最最最合适的方法就是使用枚举类型

              评论 赞赏
                ssy
                • 268

                既然用mysql,为什么没人提enum类型?
                enum表现出来是字符串但是底层存储是用数字,还天然带输入check,三全其美的方案啊

                评论 赞赏
                  cevin
                  • 7k

                  我才不会告诉你淘宝存的是字符串

                  评论 赞赏

                    我也是用1楼的方式,只是最后一个取消或作废的订单状态我会定义为负数,做查询有效订单会方便一些。至于编码方面可以使用枚举来使程序易读。

                    评论 赞赏
                      oven
                      • 452

                      我认为定义一个枚举是比较好的,数据库直接存字符串类型

                      public enum Status {
                          CANCELED, FINISHED, DELIVERING
                      }

                      这样做,你的restful API 可以设计成这样:

                      GET /order/{status}
                      评论 赞赏
                        leftstick
                        • 26.5k

                        建议直接存字符串,和数字没什么区别,对应用来说都是枚举,但可读性高了,也不必维护什么mapping关系。

                        评论 赞赏
                          dryyun
                          • 5.4k

                          我现在是觉得存字符串比较好。
                          如果存的是数字,根据你的业务流程下来,存0,1,2,3,4,5等代表不同状态,前期这么定义是没问题的。
                          但是如果业务流程变了,需要在4,5两个状态间增加两个个新状态,那么就是要新增一个状态6 ,7。
                          其实你的流程状态是0,1,2,3,4,6,7,5,我看着变扭。

                          这里使用字符串就不存在这个问题了。

                          评论 赞赏
                            Peab10g
                            • 2
                            • 新人请关照

                            我是存tinyint ,然后在程序中用一个公共方法做映射

                            评论 赞赏

                              我建议存int比存string从各方面(除了你说的转换)来说都会好很多。

                              然后是转换的问题的,其实这不是个问题,所有和db接触之前的操作对于这些都可以使用你业务的名词(canceled,finished, delivering),只有当和db接触时才会转成对应的数字,其实转换的部分只写在一个地方就好了(比如这个部分就时java 设计模式中的dao层),当然为了便于之后名词的修改或是统一每个人的拼写(大小写?)名词也要定义成常量。

                              评论 赞赏

                                不推荐使用枚举,一旦存在历史业务量再修改枚举项,会有很多意料之外的影响。
                                其实使用int保存状态码的缺点——难记,是数据库管理工作的疏漏,首先comment自不用说,其次代码中应当设置配置文件指定状态码对应状态。
                                而使用字符串保存状态,我相信如果做索引的话性能问题就不算突出问题了。状态的字符串一般都是少量有限少变,所以做索引很合适。

                                评论 赞赏

                                  网络上有许多似是而非的“谣言”,当然都不是恶意,绝大部分都是开发者不愿意自己主动研究,反而轻信其他人的信口之言。
                                  关于数据库的谣言也有不少,典型如int性能比char高很多

                                  我最近针对int、long、char、varchar的性能,简单测试了一下,发现它们其实并没有太大的性能差距:

                                  备注:c8=char(8), s8=varchar(8), i8=(bigint), c4=char(4), s4=varchar(4), i4=char(4)

                                  100w行无索引情况下查询:
                                  执行[c8查询]20次, 平均耗时312.0ms
                                  执行[s8查询]20次, 平均耗时334.3ms
                                  执行[i8查询]20次, 平均耗时276.95ms
                                  执行[c4查询]20次, 平均耗时354.95ms
                                  执行[s4查询]20次, 平均耗时340.45ms
                                  执行[i4查询]20次, 平均耗时291.1ms

                                  创建索引:
                                  c8索引耗时2439ms
                                  s8索引耗时2442ms
                                  i8索引耗时1645ms
                                  c4索引耗时2296ms
                                  s4索引耗时2303ms
                                  i4索引耗时1403ms

                                  有索引情况下查询:
                                  执行[c8查询]10000次, 平均耗时0.271ms
                                  执行[s8查询]10000次, 平均耗时0.2354ms
                                  执行[i8查询]10000次, 平均耗时0.2189ms
                                  执行[c4查询]10000次, 平均耗时0.303ms
                                  执行[s4查询]10000次, 平均耗时0.3094ms
                                  执行[i4查询]10000次, 平均耗时0.25ms

                                  结论:

                                  • 无索引情况下,全表扫描不会因为数据较小就变快,而是整体速度相同,int/bigint作为原生类型稍快12%。
                                  • 有索引情况下,char与varchar性能差不多,int速度稍快18%
                                  评论 赞赏
                                    jadedrip
                                    • -2
                                    • 新人请关照

                                    存字符串的话,某天有人写代码的时候,不小心写了 DELlVERING(l 是小写,看出来没?) 之类的,你就蛋疼了。

                                    一般我的编码习惯是,“别相信别人不会犯错,别相信自己不会犯错,给最小的选择。”

                                    评论 赞赏
                                      飞龙
                                      • 7.4k

                                      存字符串是作死。

                                      可读性可以在代码中用常量或者枚举代替硬编码。

                                      评论 赞赏
                                        撰写回答

                                        登录后参与交流、获取后续更新提醒