首页 > 试题广场 >

为“天猫魔盒”项目设计数据库。

[问答题]
天猫双十一有个积分换墨盒的活动,总共有50万台天猫魔盒(box),每个用户(user)可以用99个天猫积分(point)兑换一台魔盒,且每人限换一台。 请设计一套java接口并实现下单(order)逻辑。 参考(但不局限于)下面的下单逻辑: 
1、创建订单  
2、扣减用户积分 
3、扣减魔盒库存 
4、下单成功 
同时请回答:  
1、数据库表结构如何设计,有哪些表,分别有什么作用? 
2、下单过程中哪些地方可能成为瓶颈?如何解决或改善?  
3、是否会用到数据库事务,哪些地方会用到?如果不用数据库事务,如何保证数据的一致性?

推荐
下单逻辑就不写了,感觉有点麻烦。主要回答后面的三个问题。

数据库表结构如何设计,有哪些表,分别有什么作用?
答案:三个表,分别是: 
用户信息表,有字段:id(用户编号),name(用户名),point(现有积分),box_num(拥有魔盒个数)
魔盒信息表,有字段:all_num(所有魔盒个数),remain_num(剩余魔盒个数)
订单表,有字段:id(订单编号),user_id(用户 id),order_time(下单时间)

下单过程中哪些地方可能成为瓶颈?如何解决或改善?
答案:所有的用户下单的时候需要对魔盒信息表进行写入,需要对其进行加锁,这是瓶颈。改善方法:可以把魔盒信息表划分为多个表(如 10 个),每个表的魔盒个数平分(如每个表 5 万个),然后访问表的时候进行负载均衡,如用用户的 IP 计算出一个数,然后进行 mod10 运算,决定分配到哪个表中。但是这种情况下,如果一个表中的魔盒分配完了,就需要记录其为不可用,下次负载均衡的时候,就需要对其进行考虑。

是否会用到数据库事务,哪些地方会用到?如果不用数据库事务,如何保证数据的一致性?
答案:会用到数据库事务,用户下单的时候,需要对用户现有积分,拥有魔盒个数,剩余魔盒个数这些字段进行修改,另外,还要在订单表中添加一条新的记录,所有这些都包含在一个事务中。
编辑于 2016-04-13 20:47:28 回复(7)
看了楼上的回答,感觉都挺不错,不过个人觉得user表中不应该有魔盒数这个字段吧?我们试想一下,淘宝有那么多产品,然后很多产品可能都有这样类似的限制,但不可能将这些字段都加到user表中吧?
发表于 2015-08-17 18:22:14 回复(4)
1、表的设计:
    ①用户表usertable ,字段id,name,point,以及记录该用户是否曾经兑换过魔盒的标志字段hasdown,用0表示没有下过,1表示下过
    ②魔盒表boxtable,字段allnumber表示总数,remainnumber表示所省的魔盒数量
    ③订单表ordertable,字段id,userid,ordertime表示下单时间
2、并发过程,当所有用户同时访问数据库,操作魔盒表和用户表时为了保证事务的一致性,有必要在业务逻辑处理的地方,即更改魔盒表中魔盒数量的代码块或接口和对用户表信息的处理地方处都要加上同步机制,事务回滚等措施
3、用到的事务
    判断用户信息:积分是否大于99,是否曾经兑换过魔盒(hasdown是否为0),如果积分够又没兑换过,那么在兑换时用户表里的积分数要减去99,并且置hasdown为1
    操作魔盒表:判断魔盒剩余数是否大于0,如果大于0就进行兑换,并使魔盒数减1,
    订单表:当以上两个事务均完成时再向订单表里插入一条记录,包括用户id,下单时间
附:接口
Public void order(User user, Box box);
发表于 2015-08-12 15:00:30 回复(2)
1、数据库表设计如下:
用户表:id、用户id、用户名、用户密码、删除标记deletein、创建时间cretime、更新时间updtime
积分表:id、用户id、积分
库存表:id、产品名称、库存量(初始值:50万)、产品参数、产品价格
订单表:订单id、订单号、创建时间、用户id、产品id、购买数量、删除标记deletein
用户表用于登录网站用户兑换凭证;积分表用于进行产品兑换时兑换条件;库存表用于限定兑换产品数量;订单表用户产生兑换信息的统计
2、下单时,可能并发对数据库进行操作,加大了数据库的负担,这是一个瓶颈;比如多个同时兑换,对积分表、库存表进行了读写操作,可能对数据库
造成影响。
改善:安全性方面,增加锁机制,对积分表和库存操作同一时间只可以有一个用户进行操作。
      效率方面:积分表和库存表并发性操作较多,在积分、库存量两个字段上,增加索引,提高检索效率。
3、会用到数据库事务
在兑换阶段时,需要同时对积分表和库存表进行操作,积分表减去积分的同时,要对库存量进行减一操作,需要在事务中进行处理。
java接口:
public interface exchange{
    //获取所有产品信息
    List<Product> getAllPro();
    //获取用户积分
    Integer getScore(Integer userid);
   //下单操作
   void saveOrder(Order order);
   //下单操作中需要进行3方面操作
    //1、校验用户积分是否符合兑换要求,如果符合,校验兑换产品库存量是否大于0,如果大于,则扣除用户兑换积分99分
    //2、库存表,兑换兑换商品数量减一
    //3、根据用户信息进行下单操作。
}

发表于 2016-09-19 10:41:32 回复(0)
void PlaceOrder (user_id)
{
    user = GetUserInfoById(user_id);
    box_left = GetBoxLeft();
    if user.check == True
            alert("You have get one Magic Box before.");
            break;
    end
    if box_left<=0
        alert("Sorry, We don't have Magic Box any more.");
        break;
    end
    if user.point<99
        alert('You don't have enough point for Magic Box');
        break;
    else
        orderID=CreateOrderID();
        user.point -=99;
        box_left-=1;
        user.check = True;
        try{
            UpdateMagicBoxExchangeEvent(user, box_left, order_id);
        }
        catch e
            {throw("e"); break;}
        alert("Echange success!")
    end
}
数据库表包括:
userInfo: <Key: int userID>,<Bool userCheck>,<int point> ;    % 记录用户信息    
Warehouse: <Key: int recordID>,<long int nuBox>,<int orderID>,<Time outTime>;        %记录仓库信息和货物变更记录
OrderInfo: <Key: int orderID>,<int UserID>,<varchar[20] orderDetail>,<Time orderTime>        %记录订单详情
多人同时操作会导致更新数据库失败,解决的办法是在多个服务器上配置数据库副本。
在更新订单时需要构建数据库事务,(订单,用户信息,仓库三者的更新为一个事务)。
不用事务怎么更新……不造哎
发表于 2016-09-08 10:39:50 回复(0)

用户积分表、魔盒表、订单表。

用户积分表在积分扣除时,对单表的访问量是瓶颈。

更大的问题是,魔盒表的库存扣除存在行锁导致单行级别的访问瓶颈。采用分表方式进行负载均衡是比较好方式。

扣除积分、扣除库存、插入订单三者事务化。扣除时可以基于数据库非零异常取消事务保证,也可以预检查加乐观锁保证。

以上!


发表于 2018-10-01 12:16:57 回复(0)
挺好的高并发场景
发表于 2018-05-11 23:41:10 回复(0)
user_id box_number user_points user_exchange
发表于 2016-07-25 09:22:47 回复(0)
答:(1)数据库表一共需要设计3个,分别是:用户表、魔盒表、兑换表,
用户表字段分别为:用户ID,用户名,用户积分,该表用来记录所有用户信息
魔盒表字段分别为:魔盒ID,磨合兑换所需积分,该表用来记录所有魔盒信息
兑换表字段分别为:用户ID,魔盒ID,兑换时间,兑换所花积分数等,该表用来记录所有用户兑换魔盒的信息
(2)有可能成为瓶颈:1.兑换活动刚开始的时间段内访问用户量过大,服务器压力较大 2.兑换过程中兑换表中记录的更新及同步,需要满足时效性
(3)会用到数据库事务,比如说用户兑换后积分的变化,兑换过程中库存魔盒数量的及时更新,如果兑换不成功,需要及时回滚
设计数据库访问的锁机制,保证数据的一致性
 
发表于 2015-12-27 14:31:22 回复(0)
<pre class="prettyprint lang-java"> </pre> pulic<br />
发表于 2015-08-22 16:18:41 回复(0)
<pre class="prettyprint lang-java">public int order(userId){ userPoint = queryUserPointByUserId(userId) //查询用户积分 if (userPoint &lt; 99) { &nbsp; return 0; //积分不足,失败 } else { updateUserPoint(userId, -99); //从积分表中扣除99分,加入数据库事务锁 } res = queryBoxByUserId(userId); //查询用户是否已经兑换过魔盒,或者已经下单了 if (res==true){ rollBack(); //所有事务回滚 return 0; //已兑换过,失败 } leftBox = queryBoxNumber(); //查询剩余的魔盒数量 if (leftBox &lt; 1) { <span></span> &nbsp; rollBack(); //所有事务回滚 return 0; //魔盒数量不足,失败 } else { updateBox(userId,1);//更新魔盒表,记录兑换用户ID信息,将魔盒存量减掉1,加入数据库事务锁 } res = commitAll(); //提交所有事务,释放数据库事务锁 if (res == true){ return 1;//下单成功 } else { return 0;//下单失败 } } </pre> <div> 答案: </div> <div> 1.天猫魔盒表,魔盒库存数量; </div> <div> &nbsp; &nbsp;用户积分表,用户ID,用户积分; </div> <div> &nbsp; &nbsp;魔盒订单表,兑换用户ID,兑换时间,兑换积分,兑换状态; </div> <div> 2.多个用户同时下单。使用多线程并发操作; </div> <div> 3.会用到数据库事务。在减去用户积分,减去魔盒库存量,订单表信息更新时需要用户数据库事务。如果不用数据库事务,为了保证数据一致性把多个数据表合成为一个表。 </div>
发表于 2015-08-22 10:46:03 回复(0)
上面说得差不多了,我补充一个个人看法:
积分最好是放在积分表中,专门存储各种积分,例如
积分ID  积分名称 用户ID 积分 --
如果系统中只有一种积分的话,直接放在user表中也是可以的,仍然可以满足2范式
编辑于 2015-08-21 09:50:34 回复(0)
<pre class="prettyprint lang-java"> </pre> <br />
发表于 2015-08-20 17:06:18 回复(0)
舞头像
关于前两点大家都回答过了,如果不使用数据库事务,如何保证数据的一致性:
可以在扣除用户积分,墨盒数量减一,添加订单记录里各增加一个标志位,初始值设为0,如果操作成功就将标志位置为一,当三个操作都完成以后,判断一下,只有三个标志都为1的情况下,整体操作成功,返回,否则判断哪些标志位为1,将所作操作撤销
发表于 2015-08-17 11:25:13 回复(0)
class Order{

public int main(String[] args)   {
        
    }
}

发表于 2015-07-31 20:11:13 回复(0)
1.用户信息表,用户信息表用来记录用户的一些信息:用户ID,用户姓名,用户积分,魔盒数量
2.当多个用户同时访问一个魔盒时会出现资源冲突,可以采用同步块(synchronized)或者同步方法来解决
3.会用到数据库事务,当用户下单时,魔盒库存数量减一个,同时用户积分减少99
发表于 2015-03-29 20:34:17 回复(2)