新版的Sql Server 提供了AlwaysOn技术为高可用性系统提供了极大的便利,而且可以实现我们以前需要花很多工作实现的读写分离机制,我们当然受不了诱惑,去尝试这种新技术。开始就像所有的故事一样,平稳甜蜜,没啥问题,数据库在负载不大的情况下,完美的实现的负载均衡,我们的代码也几乎无改动,虽然有一些奇怪的小问题(这些奇怪的小问题其实是一个巨大问题显露出来的一丢丢),总体上可用,也简单。这些小问题是啥呢?我们的系统会有一些非常复杂的Sql查询视图,我们不得不用一些中间结果表来加速查询,中间结果表会有来源的关联表,这些关联表发生变动后,我们会有一个服务去更新这些中间结果表,类似于SSAS的Cube吧,不过比那个节省很多。这个更新中间表有时会有莫名其妙的失败,日志都是记录超时,我们在测试环境和模拟的生产环境数据量下都无法捕捉到,搞不定就算了,无非再重试一下就完了。另外我们还有一些公用的主数据,用数据库作业来同步到各个用户的业务数据库,在测试环境和模拟的环境中,这种同步都是以百毫秒级完成的,可是就会有这么几下会同步上十几分钟,这个也太奇怪了。我们怀疑数据库中有一些锁导致无法更新主数据表,可是无论我们怎么努力就是无法解决偶尔的同步上十几分钟,那就算了吧,反正主数据都是缓变数据,别那么频繁的去同步就好了呗。
在我们的数据库服务器承载了500+的用户数据库的时候,事情开始变的非常坏。某个周一早上,本来是开开心心的,蓝瘦,香菇,AlwaysOn的副本失效,全部数据库都无法访问,还无法分离副本,显示在同步中,不是说好的高可用吗?怎么回事啊?连切掉副本都不行?没办法,windows的最后办法,重启了服务器,然后一切都平静了,20分钟后恢复了服务,我们反复的查找原因,为什么副本会显示同步中?网络断了吗?然后检查交换机的日志,没有任何记录说网络断过,网络风暴?检查了日志依然没有发现。大概是偶发的事故,大概是谁在机房不小心踢断了网线又立刻接回去了吧,呵呵。然后悲剧了,第二天上午又发生了,第三天上午又发生了.....。无奈之下只好关闭了副本服务器,结果又是平稳安静,好像什么都没发生。数据库运维不干了,他说没有副本,服务器坏了,他无法保证数据完整,必须要打开副本。怎么办?到底发生了什么?
查阅了AlwaysOn的各种文档,还问了微软的工程师,最后说AlwaysOn只测过在同一个实例上承载100个数据库,超过太多怕是不行,我们去买数据库服务器吗?运维最后说把同步改为异步吧,什么?我惊呆了,我说你再说一遍,他说AlwaysOn现在是设置为事务同步传输的,性能损耗可能比较大,测试的时候没观察到过性能问题,只能先试试。我再次惊呆了,当然要改成异步方式了,否则不变成准分布式事务了嘛,这怎么能接受啊!居然漏掉了这个部分,我想当然的认为他是异步事务传输,否则也太耗费性能了,关键是在高写入环境下会有大量的阻塞。一切问题似乎都有解释了,比如为什么主数据会偶尔同步十几分钟,因为他在等待另很多事务完成,这个事务在等待副本更新完成,然后数据库会有大量的这样的等待。同步主数据算法本身也有问题,就是他总是会去更新而不管数据是否真的变化,这种偷懒的写法在遇到AlwaysOn同步事务传输的时候就麻烦了,想想看,主数据大概2000条,500个数据库更新,然后.....。还有缓存的中间结果表的更新为什么会偶尔超时,因为被主数据更新卡住了,主数据更新被其他事务同步卡住了。
找到问题后,改进了算法,所有的数据都要比对不同后才更新,相同的就不要更新了,AlwaysOn改为异步事务传输,修改了Sql Server的线程池线程数量上限(AlwaysOn使用线程池线程做同步),数据库太多需要更多的线程去给AlwaysOn使用。然后又平稳了。
总结:
1、AlwaysOn运行在大量小数据库的实例上需要特别小心。
2、有大量的突发的写入的时候,AlwaysOn可能是无法承受的,特别是结合1的前提下
3、不要使用AlwaysOn的同步事务传输模式,特别是结合了2和1的前提下。