上善若水

水善利万物而不争
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

架构 | 笔记4

Posted on 2022-04-30 10:02  董锡振  阅读(48)  评论(0编辑  收藏  举报

如下内容来之https://time.geekbang.org/column/article/6463 学习笔记:

14 | 高性能数据库集群:读写分离

    

 

    需要注意的是,这里用的是“主从集群”,而不是“主备集群”。

    “从机”的“从”可以理解为“仆从”,仆从是要帮主人干活的,“从机”是需要提供读数据的功能的;而“备机”一般被认为仅仅提供备份功能,不提供访问功能。

    所以使用“主从”还是“主备”,是要看场景的,这两个词并不是完全等同的。

    读写分离的基本实现是:

    1、数据库服务器搭建主从集群,一主一从、一主多从都可以。

    2、数据库主机负责读写操作,从机只负责读操作。

    3、数据库主机通过复制将数据同步到从机,每台数据库服务器都存储了所有的业务数据

    4、业务服务器将写操作发给数据库主机,将读操作发给数据库从机。

    读写分离的实现逻辑并不复杂,但有两个细节点将引入设计复杂度:主从复制延迟和分配机制

    解决主从复制延迟有几种常见的方法:

      1. 操作操作指定发给数据库服务器;例如,注册账号完成后,登录时读取账号的读操作也发给数据库主服务器。

      2. 读从机失败后再读一次主机这就是通常所说的“二次读取”,二次读取和业务无绑定,只需要对底层数据库访问的 API 进行封装即可

      3. 关键业务读写操作全部指向主机,非关键业务采用读写分离

      例如,注册 + 登录的业务读写操作全部访问主机,用户的介绍、爱好、等级等业务,可以采用读写分离,因为即使改了自我介绍,查时还是旧的,业务影响与不能登录相比就小很多,可以忍受。

    分配机制将读写操作区分开来,然后访问不同的数据库服务器,一般有两种方式:程序代码封装和中间件封装

           

15 | 高性能数据库集群:分库分表

  如何取舍?如下问题: 1.join 操作问题 2. 事务问题   3. 成本问题 

  按照我前面提到的“架构设计三原则”,简单分析一下。

    首先,这里的“如果”事实上发生的概率比较低,做 10 个业务有 1 个业务能活下去就很不错了。如果我们每个业务上来就按照淘宝、微信的规模去做架构设计,不但会累死自己,还会害死业务。

    其次,如果业务真的发展很快,后面进行业务分库也不迟。因为业务发展好,那业务分库带来的代码和业务复杂的问题就可以通过增加人来解决,成本问题也可以通过增加资金来解决。

    第三,单台数据库服务器的性能其实也没有想象的那么弱,一般来说,单台数据库服务器能够支撑 10 万用户量量级的业务,初创业务从 0 发展到 10 万级用户,并不是想象得那么快。

 

 

 

  关于分表: 单表数据拆分有两种方式:垂直分表和水平分表  

    1、垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。

    2、水平分表适合表行数特别大的表,有的公司要求单表行数超过 5000 万就必须进行分表 

      水平分表后,某条数据具体属于哪个切分后的子表,需要增加路由算法进行计算(范围路由、Hash 路由、配置路由),这个算法会引入一定的复杂性。

      其他常见处理方式

      1、count() 相加  水平分表后切分为 20 张表,则要进行 20 次 count(*) 操作,如果串行的话,可能需要几秒钟才能得到结果。

      2、记录数表 具体做法是新建一张表,假如表名为“记录数表”,包含 table_name、row_count 两个字段,每次插入或者删除子表数据成功后,都更新“记录数表”。

  分库分表具体的实现方式也是“程序代码封装”和“中间件封装”,和数据库读写分离类似,但实现会更复杂。

    读写分离实现时只要识别 SQL 操作是读操作还是写操作,通过简单的判断 SELECT、UPDATE、INSERT、DELETE 几个关键字就可以做到,

    而分库分表的实现除了要判断操作类型外,还要判断 SQL 中具体需要操作的表、操作函数(例如 count 函数)、order by、group by 操作等,然后再根据不同的操作进行不同的处理。

    例如 order by 操作,需要先从多个库查询到各个库的数据,然后再重新 order by 才能得到最终的结果。

 

   NoSQL 作为 SQL 的一个有力补充,NoSQL != No SQL,而是 NoSQL = Not Only SQL。

  常见的 NoSQL 方案分为 4 类。

    K-V 存储:解决关系数据库无法存储数据结构的问题,以 Redis 为代表。

    文档数据库:解决关系数据库强 schema 约束的问题,以 MongoDB 为代表。

    列式数据库:解决关系数据库大数据场景下的 I/O 问题,以 HBase 为代表。

    全文搜索引擎:解决关系数据库的全文搜索性能问题,以 Elasticsearch 为代表。

 

17 | 高性能缓存架构

  缓存就是为了弥补存储系统在这些复杂业务场景下的不足,其基本原理是将可能重复使用的数据放到内存中,一次生成、多次使用,避免每次使用都去访问存储系统。

  缓存穿透 是指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统需要再次去存储系统查询数据

  缓存雪崩 是指当缓存失效(过期)后引起系统性能急剧下降的情况

    缓存雪崩的常见解决方法有两种:

    1. 更新锁:分布式集群的业务系统要实现更新锁机制,需要用到分布式锁,如 ZooKeeper。

    2. 后台更新: 由后台线程来更新缓存,而不是由业务线程来更新缓存,缓存本身的有效期设置为永久,后台线程定时更新缓存。

  缓存热点 是指数据热点数据的缓存,解决方案就是复制多份缓存副本,将请求分散到多个缓存服务器上,减轻缓存热点导致的单台缓存服务器压力

 

 

  提升性能小结:
  先单机,有压力后优先考虑sql优化、db参数调优,还有硬件性能(32核/16G/SSD)优化,不行再考虑业务逻辑优化、缓存。不要一上来就读写分离、集群等,能单库搞定的就毫不犹豫的单库。

  主从读写分离
    适用于单机无法应付所有请求,且读比写多时,读写分离还可以分别针对读写节点建索引来优化。
    对实时性要求不高:刚写入就读会有延迟,同步数据特别大时,延迟可能达到分钟级(可用缓存解决:2-8原则,挑选占访问量80%的前20%来缓存) 

  分库分表(甚用,增加很多复杂度)
    几千万或上亿
    分库时机:单机性能瓶颈,1业务不复杂,但整体数据量影响到数据库性能;2业务复杂,需要分系统由不同团队开发,使用分库减少团队耦合。 
    分表时机:单表数据量大拖慢了sql性能,做垂直(将常用和不常用字段分开)或水平拆分(id分段、hash路由、添加路由表等)提高速度。(那么join、count、分页排序等就变得复杂)

  nosql——not noly sql 本质上是牺牲ACID中的某个或某几个属性,以解决关系数据库某些复杂的问题 

  缓存(千万千万不要设计复杂的缓存,到时候各种不一致问题烦死你)
    cdn、nginx缓存、网关缓存、数据层缓存redis、db本身也有缓存(sql结果缓存、读取的磁盘分页缓存)
    缓存穿透:1本身无数据(添加默认值缓存/布隆过滤器 ) 2未生成缓存(识别爬虫并禁止 但可能影响seo)
    缓存雪崩:缓存实效后大家都在更新缓存导致系统性能急剧下降(1消息队列通知后台更新、2使用分布式更新锁)
    缓存热点:大部分业务都会命中的同一份缓存,比如1000w+粉丝的微博消息,复制多分缓存副本,key里面加副本编号将请求分散,且设置过期范围,而不是所有副本固定同一过期时间。
    缓存框架看一下设计思路: 分享https://github.com/qiujiayu/AutoLoadCache