前言
今天进行了第一次面试,还是比较紧张的,面试官比较严肃,感觉面试官就是抓着一个点不断的问,问到你不会,这次面试算是很失败了,不仅前面的问题很多都不清楚,而且最后的coding题都没写完成,明明知道怎么写,缺敲不动键盘,但是也知道了自己的不足,主要是数据库方面,然后还有代码方面需要加强一下。再努力吧。
那文章还是得继续写,今天聊聊面试中提到的一个关于数据库修改数据的问题。
正文
有这么一个场景,假设user表的结构为id,user_data,其中这个user_data是个json数据,存放着用户的年龄,名字等等的数据,如果一个用户并发修改这个字段中的user_age与user_name会出现什么样的情况?
首先这个问题,先考虑单个字段修改的情况,即我修改其中的某一个数据该怎么改?
我在面试中的回答是先取出这个json数据存为对象,然后对这个对象进行修改。但其实mysql5.7.8开始,就提供了对json字段进行操作的函数,有JSON_SET(), JSON_INSERT()还有JSON_REPLACE(),其中JSON_REPLACE()是用来修改json数据的某个字段。那我个人是不太清楚这个,上网查找才知道如果需要对json字段进行update,那可以使用以下代码:
1 | update user set user_data = json_replace(user_data,'$.user_name','abc','$.user_age',18) where id = 1; |
其实就是这么简单。
但我当时回答的是,先取出数据,再去修改。那问题来了,如果在读过后,被其他线程修改过了数据该怎么办?
例如原始数据为1 {'user_name':'abc','user_age':10}
。
那么同时过来两个请求a,b:
其中a请求是把user_name改为aaa,而b请求是把user_age改为20。
那这时候如果请求时间十分接近,在a读取原始数据过后,还没来得及修改,b请求就已经将age改为20了,但此时a读取的数据就是旧的了,如果a继续写入,那age的值就会变为未修改前的。最终导致只有一个字段修改成功。
这个问题就是数据库并发时会出现的问题,其实就是不可重复读的问题,即一个事务在一段时间内读取的数据不一致,那解决办法我个人认为使用事务加锁进行解决。
排它锁与共享锁
mysql的innodb当中,有一种共享锁的存在,在读取数据前添加共享锁,其他事务只能添加共享锁。那排它锁就是在获取排它锁之后,其他事务不能添加任何锁。
那问题在于添加了读的共享锁之后,再添加排它锁会导致死锁,两个事务都没法获得锁,没法更新。所以我们可以在读取的时候就添加排它锁,事务结束后再释放就可以了。
那还有一种解决方案就是直接把数据库的隔离级别设为可串行化,事务排队执行,但可能导致请求的阻塞。
后言
以上就是这个问题的后续思考。
在写这篇文章时,没想到就收到了下一面的预约电话,感觉不可思议,明明表现不怎样。但学习永远都是停不下脚步的,就这样共勉吧。