公司数据库使用sql server 2014 AlwaysOn+硬件LB做读写分离,有好几个AlwaysOn集群,此次事故由多个条件共同发生造成的。事故相关有两个集群,这两个集群节点结构如下:
主集群A(核心集群)结点结构如下:
写:DB-01
读:DB-02(同步)
DB-06(异步)
DB-09(异步)
DB-03(异步)
DB-10(异步)
集群A的只读负载均衡节点为DB-02 DB-06 DB-09
集群B(日志、BI分析结果数据)结点结构如下:
写:DB-05
读:DB-03(同步)
DB-10(异步)(事故前在节点在事故中,但不在只读列表中)
注:DB-03和DB-10是所有集群共同的读节点,为的是备份和必不可少的跨集群JOIN以及数据分析(BI),但尽量不做单集群的读操作(除非集群没有多余的读节点,如集群B)
每个AlwaysOn集群本来都做到了≧3个节点的备份,由于DB-10是后来加的,集群B的读就少了一个节点。而开发环境只有一个数据库实例,所有库都在这一个实例上,这是不能在开发时就将问题发现的主要原因。
由于所有集群的帐户都一致,造成可以在集群共同的节点(DB-03和DB-10)可以访问所有数据库,而出事故的Sql竟然是一个跨集群的JOIN,数据库字符串连接的是集群B的只读节点(DB-03)。由于集群B的只读节点包含所有数据库且能访问,因此在正常的情况下SQL不会有任何问题。当DB-03不能服务时这段SQL就会出错(当时DB-10不在集群B的只读列表中,alwayson自动将读节点切换到写节点上)——xxx对象不存在,从而造成核心业务不能访问。
此次事故问题出现在开发环境所有数据库在一个实例上,在生产环境时代码连接的实例也包含所有数据库造成的,当出现问题时就会出错
此次事故的后续解决方案如下:
1.将开发环境的数据库做实例上的隔离
2.每个应用在每个集群有不同的帐户名,在共同节点上对应集群帐户只能访问对应集群的数据库,防止意外的跨集群查询