CRUD基本素养
开发业务代码最核心东西概括起来就是数据的CRUD,虽然这么说,但不同需求、不同场景下的CRUD却不是一件简单的事情,需要考虑的因素有很多。CRUD即数据的Create、Read、Update、Delete,以下分别总结以下这四个操作在不同场景下需要注意的地方。
CREATE
即数据的插入,
- 并发场景下数据写入,需要加分布式锁,防止多条插入,或者做好幂等校验。比如:订单创建的时候,用户没有添加备注信息,当支付成功后进行备注修改。这个场景下,备注信息存储在order_attr属性表中,需要提前判断订单备注是否存在,如果存在,就更新,否则,插入。极端场景下,存在A、B两个线程,当A线程select没有查到数据,开始执行insert时,B线程也执行了select,在A线程前插入了数据,这样A线程也会执行一次插入,就会插入两条数据。解决手段有两种,一是在select开始时根据订单id加分布式锁,这样当A执行插入时,B线程需要等待,获取到锁后,执行的就是更新操作了;或者建表时设计order_id + key的唯一键,并发插入时,抛出异常,事务回滚。
- 批量插入mysql语句设计,for循环批量插入为什么很慢,jdbc批量插入底层实现原理
READ
数据查询,看似简单但是场景最为复杂、核心的功能
- 表索引的设计,如果是新表,上线前要根据写的sql梳理要建立的索引,旧表就需要考虑覆盖索引,上线不出现慢sql,至少达到range级别,
- 多条件组合查询接口设计,b端管理界面常见,一堆下拉框或者输入框条件
- 分库分表情况下的数据查询需要带路由键(数据库中间件根据路由键定位到具体的库表,update也是一样,需要带上路由键)
- 分页查询基本流程,sql语句,分页查询total含义(是的,有一个需求兄弟侧提供一个分页接口,没注意total含义,更新offset时候,offset += total,上线后直接把别人接口调崩了,疯狂告警报错,最近一个需求,返回的时候,也忘记了把total设置到返回数据中….)
- 深分页如何优化(怎么更快的定位到id)
- 分布式场景下,尽量使用异步批次的方式从其他服务获取数据,不要在循环中进行rpc调用
- 尽量不要在事务中进行select操作或者rpc调用等和数据更新无关的操作,容易导致大事务
UPDATE
数据更新
- 批量更新mybatis sql语句
- 涉及到数据写入要考虑事务,spring事务失效场景,底层原理
- update操作天然幂等,但多个线程操作同一份数据仍然需要靠锁来同步操作顺序,避免丢失更新
- 防止并发更新,导致的丢失修改。这个和sql有关,如果sql写的是那种自动生成的,还是那种updateSelective的(set的时候判断对象的值是不是null)就容易出现。比如A线程和B线程需要分别修改数据1的name、status字段,A根据id查出来,设置po对象的name,写入;B同样的设置po对象的status字段,写入。这种场景下,无论谁先执行,都会有一个更新丢失。当然这个也可以加锁解决,select前就加锁,保证查出来的时候都是最新值,但是一分析没有必要,这两个更新操作没有对同一个字段更新,加锁会导致资源竞争,可能存在获取锁失败的情况,用户体验较差,避免的方法就是自己写sql,更新哪个字段就set哪个字段。这样改造成本较低,
DELETE
数据删除
暂时想到这么多,想到再补充。