Go在后端开发中的实际思路2 | 青训营笔记

106 阅读7分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记

MySQL

后端项目,离不开的就是数据的增删改查。通常接触到最多的就是MySQL了

MySQL常用的版本有5.7和8.0,通常为了向前兼容,大部分公司使用的MySQL版本都是5.7。在这个版本中,MySQL默认支持InnoDB存储引擎,这个引擎的特点就是支持事务,也就是我们常说的ACID

一般来说,如果需要对多张表进行增、改、删等操作的时候,为了防止多阶段操作的成功失败不一致问题,需要用到事务特性。如果操作不完全失败,就进行事务回滚,将所有操作都取消。

事务有四种隔离级别,分别是读未提交、读已提交、可重复读和序列化。MySQL中InnoDB默认支持的事务隔离级别是可重复读。

需要注意的是,对于事务的每一种隔离级别,存储引擎内部都会提供对应的锁机制实现。在对数据进行操作的平时,需要注意出现死锁的情况。在数据读取和操作中,支持读写锁,读锁也就是共享锁,多把读锁可以同时拥有。写锁也叫排它锁,同一时刻只允许一把写锁对数据进行操作。不同的存储引擎,有不同的锁级别,有表锁、行锁、间隙锁。注意在执行delete或者update操作的时候,最好带上where条件,防止全表删除或者更新的情况,或者因为触发表锁导致死锁的情况。

数据的查询通过索引查找的方式和全表扫描的方式效率差距很大,本质的原因是在InnoDB引擎内部,会对添加了索引的表字段建立B+树以提高查询效率。在查询语句的编写过程中,尽量表明需要查询的字段,这样在查询的字段如果已经创建了联合索引的情况下InnoDB查找不需要进行回表。B+树的叶子节点通常存储的是表的主键,通过查询条件在索引B+树中查询到对应主键,再到以主键为查询条件建立的B+树中查找整行数据的方式我们称为回表,回表会进行两次B+树查询。

联合索引的支持查询方式是最左匹配原则,如果查询语句中的where条件没有按照联合索引的最左匹配原则进行查询,InnoDB将会全表扫描。索引优化使用 Explain 语句。

在表设计上,一张表的字段不应设计过多,一般不超过20个字段。每个字段的字段类型应该按照实际情况尽量缩减,比如uuid默认是32位,那么定义varchar(32)即可,定义varchar(255)会造成空间浪费。

分页查询中,limit支持的page和pageSize两个字段,当page越大,查询的效率越低。因此尽量设计一个自动递增的整型字段,在page过大的时候,通过添加过滤自动递增的整型字段的where条件提高查询效率。

MySQL默认是单机存储,对于读多写少的业务场景,可以主从部署,支持读写分离,减轻写服务器的压力。

MySQL最多只能支持几k的并发,对于大量的并发查询数据的场景,建议在上游添加缓存服务比如Redis、Memcached等。

MySQL在操作数据的时候会提供binlog日志,通常会使用cancal等组件服务将数据进行导出到消息队列,进行分析、特定搜索、用户推荐等其他场景。如果MySQL服务器数据丢失,也可以使用binlog日志进行数据恢复,但是因为数据操作会在一段时间内存在系统内存中,定期flush到硬盘,所以通过binlog日志也不一定能完全恢复出所有数据。


Redis

当用户量剧增,访问频繁的时候,在MySQL上游添加一个缓存服务,同步一部分热点数据,可以减轻数据库的访问压力。常见的缓存服务有Redis。

redis是由c语言编写的内存型分布式缓存组件。特点是支持大量读写场景,查询数据高效。

虽然redis是分布式缓存,但是为了防止服务宕机,通常会使用持久化机制将数据保存到硬盘中。redis支持的持久化机制包括AOF和RDB两种。

AOF通过记录每一次写、改、删操作的日志,在服务宕机后,通过操作日志进行命令重新执行的方式恢复数据。

RDB通过记录数据快照的方式,在服务宕机后,通过数据快照恢复该时间段以前的所有数据。

通常来说,两者都有各自的缺点,AOF的缺点是数据恢复慢,RDB的缺点是数据快照是定时执行的,那么在宕机时刻与上一次数据快照记录时刻的中间这一段时间的数据操作,将会丢失。所以我们会两者兼用同步执行。建议RDB的时间间隔不要设置的太短,因为RDB快照的时候执行内部的bgsave命令会导致redis在短暂的时间内无法提供服务。

虽然redis能有效的减轻数据库的访问压力,但是redis也不是银弹(解决软件领域有共识且客观存在的显著问题的方法)。如果数据最终还是以数据库中为准,那么在对数据进行读写操作的时候,需要考虑缓存与数据库不一致的问题。

redis与mysql数据一致性的解决方案

读取操作: 如果redis 某个数据过期了,直接从Mysql中查询数据.
写操作: 先更新Mysql, 然后再更新Redis即可;如果更新 redis失败,可以考虑重试,

对于上述操作,如果还存在不一致的情况,考虑加一个兜底方案, 监听 mysql binlog日志,然后binlog 日志发送到 kafka 队列中消费解决。

引入redis,除了数据不一致的问题之外,还有可能出现缓存雪崩、缓存穿透,缓存击穿的情况。在添加缓存的时候,尽量设置不一样的缓存失效时间,防止同一时间内大量缓存数据失效,数据访问db造成db访问压力过大的问题; 缓存穿透可以考虑布隆过滤器, 缓存击穿考虑分布式锁解决。

redis之所以读取效率快,是因为大量数据存在内存中,如果需要大量的缓存数据存储,单机内存容量有限,redis需要进行集群部署。redis的集群部署存储方式是将拆分的一万多个slot槽位均匀分布在各个redis服务器中,redis的key通过一致性哈希,将数据存储在某个slot槽位对应的redis服务器中。redis的扩容和缩容操作会引起比较大数据迁移,这个时候尽量对外停止服务,否则可能会导致缓存数据失效的问题。

redis通过哨兵机制发现服务上下线的问题。通常的部署模式是一主二从三哨兵

redis的应用场景有很多,比如利用zsort实现排行榜,利用list实现轻量级消息队列,利用hash set实现微博点赞等等。

在redis存储的时候需要注意,key值尽量不要使用中文,value值尽量不要过大。在设计key的时候,应该根据业务统一key的设计规范。

虽然redis有16个db库,但是只是逻辑隔离,缓存数据都是存储在一个地方,不同的db库的读写是竞争关系