目录
批量、异构索引、产品规则、简化SQL、加索引、异步写、pipeline
缓存化适用于依赖服务的性能不满足自身服务SLA的场景。
当我们引入缓存后,需要通过一定策略对缓存数据进行更新,有以下三种更新方式:
①定期更新,以job的形式定期对全量数据进行更新。
②读时更新:在读服务时更新缓存结果。
③通知更新:以消息的形式对特定数据进行更新。
解决依赖服务的性能不满足自身性能要求的问题。
使用限制条件:一致性时间要求低于计算时间
通过提前计算远程读服务,存储后(存储介质包括:缓存、数据库),下次请求进来时,可以直接读取到预先计算的数据。
缓存相比数据库,简单方便,但由于数据是放在内存中,因此存在数据丢失的风险。如果能接受数据短时间内(时间长短取决于计算时间)拿不到,可以采用缓存,相反,则应该用数据库。
异步调用适用于多个服务之间无依赖的场景。
在异步调用上有线程池异步调用和pigeon异步调用两种实现方式。在异步调用远程读服务这个场景上,推荐使用pigeon异步调用方式。
超时控制适用于缓存需要更新的场景。
当我们引入缓存后,为了保持缓存数据的时效性,需要对缓存数据设置合适的过期时间并进行更新。对于在过期时间之后数据能否再次使用有以下两种情形:
①数据在过期后不能再使用
这个场景对过期数据的管理较为严格,数据一旦过期就不能再次使用。这种场景的超时控制可以使用redis的过期功能,redis可以对键设置过期时间,当在过期时间之后获取键,会得到null或者空。
②数据在过期后可以使用
这个场景对过期数据并不敏感,数据过期后可以再次使用。这种场景的超时控制可以使用自己实现的过期功能,在过期时间之后可以再次获取到旧值。
多条SQL写入场景,可采用合并写SQL的方式来提高SQL操作性能。具体方法如下:
高并发的插入和更新数据,采用异步同步数据,将数据放到消息队列中,然后再写多个线程去消耗队列。
预处理的意思是先提交sql语句到mysql服务端,执行预编译,客户端执行sql语句时,只需上传输入参数即可,这点和存储过程有点相似。
*预处理工作原理
预处理:创建 SQL 语句模板并发送到数据库。预留的值使用参数 "?" 标记 。例如:INSERT INTO MyGuests VALUES(?, ?, ?)
数据库解析,编译,对SQL语句模板执行查询优化,并保存起来
执行:最后,将应用绑定的值传递给参数("?" 标记),数据库执行语句。应用可以多次执行语句,如果参数的值不一样。
* 预处理优点
预处理的执行效率相对于一般的sql执行操作,效率比较高,因为第二次执行只需要发送查询的参数,而不是整个语句
预处理可以防止sql注入,因为预处理将sql语句与数据分开发送。
预先将多个写入流程分成独立写流程操作集合和非独立写流程操作集合。针对独立写流程操作集合可采用合并写方式(优先)或消息队列方式提高性能;非独立写流程操作集合采用串行方式。合并写方式如下:
INSERT语句里面插入多个值
INSERT INTO example
VALUES
(100, 'Name 1', 'Value 1', 'Other 1'),
(101, 'Name 2', 'Value 2', 'Other 2');
insert into ...on duplicate key update批量更新
使用mysql 自带的语句构建批量更新
UPDATE mytable
SET myfield = CASE id
WHEN 1 THEN 'value'
WHEN 2 THEN 'value'
WHEN 3 THEN 'value'
END WHERE id IN (1,2,3)
批量删除多条记录
高并发写场景,可采用比较更新策略(CAS),过滤不符合条件的SQL,保证数据一致性的同时,也提高了性能,避免死锁带来的死等待。具体方法如下:
可以借助Redis的SETNX(set if not exist,原来的key不存在时返回1,存在返回0)来作为插入资格的一个判断条件。由于Redis是单线程的,SETNX可以保证是原子操作。做法是让业务操作拼接成唯一key,进行SETNX操作。操作成功就表示获得锁,失败的就表示获取失败。这样只有一个请求能通过判断,其它的请求获取锁失败就退出。
解决重复请求或过时请求的并发场景。
在现有表中增加一个冗余字段,version版本号字段(long类型,例如毫秒级的long型时间等)。原理:1)只有当前版本号 > 数据库表版本号,才能提交;2)提交成功后,版本号version更新为当前版本号。
过滤不合状态扭转的并发请求操作数据库,保证数据一致性的同时,也提高了性能。
解决离线任务执行速率不满足一致性要求(如t+1小时)的问题。
将任务拆分成多个小任务,通过并行化处理。