扩容成本直降2000万!山东移动精华实践分享!

朱祥磊 2016-05-26 10:11:39
本文根据第59期线上分享整理而成。

 

本次分享的这个项目通过引入可横向扩展的分布式缓存架构,针对访问频繁的相关表如字典表和三户资料数据,通过应用改造,放到分布式缓存中进行管理,并和数据库互动,大幅降低数据库访问次数,从而达到提高应用效率、降低扩容压力的目的。

 

项目上线后,实现了应用效率提升和扩容投资降低的目的,实际上线运行情况表明,针对访问最频繁的top 30的表进行缓存处理后,整个数据库访问次数下降30%,关键业务处理性能提升15%。

 

项目实施背景

 

随着各类业务的发展,对运营商核心OLTP数据库处理能力提出了越来越高的要求,尤其承载核心用户资料数据的crm系统,压力越来越大。传统的性能优化方法,如主机扩容、数据库优化跟不上业务需求压力增长速度,且扩容成本很高,投资巨大。

 

传统性能优化方法基本基于主机和数据库,但数据库如Oracle方面难以实现横向扩展,扩展能力受限。应用层,可以通过增加服务器数量实现扩容,但是在核心数据库层,由于采取Oracle rac构建,其基于共享存储的机制造成系统io压力越来越集中,io的瓶颈难以通过简单扩容实现,节点数扩展越多,性能下降越明显,同时单台数据库服务器的处理能力也是有限度的,难以无限制的增加,只能通过拆分数据库来进行性能的扩展,但是又增加了数据迁移的难度。

 

上述背景要求我们寻找一种新的性能优化方法,可以降低核心数据库压力,同时又能实现可扩展的灵活架构方案,从而更好的实现高效低成本。

 

方案做法

 

1
技术方案

 

通过在应用层引入分布式缓存技术Memcache,实现可灵活横向扩展的基于x86的分布式缓存架构,针对访问频繁的相关表如字典表和三户资料数据,通过应用改造,放到分布式缓存中进行管理,并和数据库互动,大大降低数据库访问次数,从而达到提高应用效率、降低扩容压力的目的。

 

整体架构如下(简单示意):

 

图1 应用架构图

 

即在现有三层架构,web层->cics层->database层基础上,增加Memcache缓存层(上图红色部分),使在应用访问数据库时,可以将数据缓存在Memcache集群中。

 

针对TOP N业务进行改造,以山东公司为例,top 10业务占总业务量的80%以上,即仅仅改造10个业务,即可实现性能效果大大大提升。

 

业务

一天业务量(笔数)

补卡

20686

产品变更

1337704

活动管理

60750

集团成员管理

162785

缴费

2643038

开户

377973

梦网业务订购

184958

提卡激活

140381

提取发票

2611

资料变更

24470

 

2
Memcache介绍

 

Memcached是一个高性能的分布式内存对象缓存系统,用于减轻数据库负载,目前在互联网行业应用较为广泛。它通过在内存中缓存数据和对象来减少读取数据库的次数。本项目采用Memcache实现缓存机制,也可以采用其他缓存软件如redis等实现。

 

Memcached基于一个存储键/值对的hashmap。其守护进程(daemon)是用C写的,但是客户端可以用任何语言来编写,并通过Memcached协议与守护进程通信。

 

图2 Memcache原理图

 

3
应用访问逻辑改造

 

增加缓存层后,应用的访问逻辑需要改变,主要表现在,增加缓存前通过直接访问数据库实现,直接写SQL即可,但增加缓存后,需要增加针对缓存的处理,通过应用逻辑实现数据的获取。

 

应用针对缓存访问方法和步骤如下:

  1. 每个会话访问表时,先判断数据是否在缓存中,如是则从缓存取,否则从数据库取,并同时写入缓存;

  2. 如有多个缓存,则在缓存前置分发器,根据一定规则分发到不同的缓存处理;

  3. 缓存取不到或缓存故障下,直接从数据库中取,前台返回无任何影响;

  4. 数据更新时直接更新数据库,并同步触发缓存更新逻辑;

  5. 更新缓存时第一份缓存和数据库同步更新,然后触发其他缓存间的同步更新,由于是在缓存间的数据同步操作,性能影响几乎可以忽略不计。

 

详细数据访问流程如下图引入缓存后的数据读写流程图所示:

 

图3 引入缓存后的数据读写流程图

 

下面分场景介绍缓存访问逻辑:

 

数据加载场景:

 

  1. 先到分发器分配的一份缓存中查询,数据是否在该缓存中,如果在直接返回;

  2. 如果不在,从物理库读数据;

  3. 将读取的数据装载到缓存1,同步触发对缓存2…n进行加载。任何一个缓存操作失败,则标记为不可用,后续不再分发业务;

  4. 同时把数据结果返回给业务。

 

数据更新场景:

 

  1. 先到分发器分配的一份缓存查询,更新前的数据是否在缓存中;

  2. 先更新物理库;

  3. 根据第一步判断如果在缓存,则把物理库的更新变化刷新到缓存,否则启用装载流程,装载到缓存,如有多个缓存同步进行更新,任何一个缓存操作失败,则标记为不可用,后续不再分发业务;

  4. 把数据结果返回给业务。

 

业务连续性保护机制:

 

IT系统不可避免会出现故障,当Memcache  server不可用时,异常处理机制如下,将不影响业务运行:

 

图4 缓存高可用措施流程

 

  1. 当某一台server不可用时,根据key查询MemCached中数据,返回server不可用错误,则执行数据库查询,在配置多套集群情况下,也可从其他集群访问。

  2. 当故障的server恢复后,自动按照正常流程访问 。

  3. 集群中其他可用的server按照正常流程访问。

 

运行效果

 

通过将top 10的业务涉及访问最频繁的30个表改造为Memcache访问机制,数据库整体压力降低30%,top 10关键业务平均响应时间降低15%。

 

1、应用效率提升明显

 

 

2、成本压力大大降低

 

数据库压力降低40%,去掉计划内的增长15%,相当于整体下降核心系统25%的扩容能力,经计算可降低扩容成本2000万元。

 

附:统计的各表访问次数下降情况

 

业务

单笔业务

数据库访问次数

一天业务量

表访问下降次数

补卡

49

20686

709529.8

产品变更

185

1337704

173232668

活动管理

134

60750

5698350

集团成员管理

889

162785

101301105.5

缴费

181

2643038

334872914.6

开户

203

377973

53709963.3

梦网业务订购

55

184958

7120883

提卡激活

49

140381

4815068.3

提取发票

29

2611

53003.3

资料变更

46

24470

787934

合计

1820

4955356

682301419.8

 

Q & A
 

 

Q1:对于缓存数据是不是需要业务改造?

A1:是的,需要应用做比较大的改造,以前我们的CRM应用都是直接sql获取数据,现在通过key-value方式,几乎关键业务全部修改了一遍。

 

Q2:刚才提到“针对访问频繁的相关表如字典表和三户资料数据,通过应用改造,放到分布式缓存中进行管理”,针对更新频繁的表(如受理日志等)是不是不适合通过此类方式改造优化?

A2:针对读多写少的表,非常适合,但是对读写差不多或写多读少的表,效果要差。我们统计过,我们正在改造的核心表,读写比例在8:2以上。

 

Q3:Memcache选择的什么版本?

A3:版本1.4,比较老。

 

Q4:根据第一步判断如果在缓存,则把物理库的更新变化刷新到缓存,否则启用装载流程,装载到缓存,如有多个缓存同步进行更新,任何一个缓存操作失败,则标记为不可用,后续不再分发业务。 如何恢复这个故障的缓存节点?

A4:目前还是监控,然后人工恢复。

 

Q5:能否透露一下Memcache的集群规模?

A5:目前40个x86节点。

 

Q6:缓存层到DB层需要做哪些设置呢?DB层的缓存是否还有作用?

A6:不需要设置,通过应用负责链接,数据库的缓存仍然起作用,但只是在通过sql获取数据时,然后程序写到Memcache,之后再发生读,则从缓存,不从数据库。

 

Q7:怎样保持缓存和数据库中的数据实时一致?

A7:主要采取这个逻辑实现:数据更新时直接更新数据库,并同步触发缓存更新逻辑。都需要应用来实现。

 

Q8:单个节点容量在多少?文中提到的同步,40个节点数据是冗余还是分片的?

A8:单个节点在30G内存,40个节点分了4个集群,集群内是分片。

 

Q9:缓存前置分发器依据规则分发数据,如果增加新的业务和服务器,这个规则是能自定义并能指向新增加的服务器?

A9:是通过客户端应用来分发的,hash算法,新增服务器,需要进行缓存迁移,因版本较低,还没用到集群,但可以通过预分片设置了大量的分片,新增服务器时可以方便把一些分片迁移到新机器。

 

Q10:除了Memcache,redis还有其他竞争性方案吗?

A10:其他如MongoDB,也可部分代替缓存功能,还有可以采用内存库,但感觉效率比缓存差一些。

 

Q11:Oracle的TT能否实现类似的缓存功能?有无可能,因为缓存和数据库的更新没立即同步,导致应用读取了老的缓存里的数据?

A11:TT是关系型内存数据库,不是缓存,适合替代Oracle物理库,二者比较难以实现一致性的同步,我们的场景是由应用同步写物理库和缓存,使二者一致。

 

Q12:Memcache的集群规模是如何评估的?应用如何选择查询哪个缓存节点?会不会数据存放在节点1,而应用访问节点5以致没找到数据而访问数据库?

A12:目前是按照放到内存的表的实际占用量评估的,目前数据存放在哪个节点是由应用控制的,根据模数放在不同的节点,后续查询也按照这个定位节点。新版本支持集群后会更灵活。

 

Q13:如何选择要缓存的表?除了根据读多写少,还有哪些评判?

A13:选择要缓存的表,一是需要读多写少,还有要在整个数据库个系统中的读量处于前面的位置,否则优化了用处不大,还有就是尽量delete操作不要太频繁。

 

Q14:能否举例说明,在实现某个查询功能时,原先没使用缓存时,SQL是怎么写得的?改用缓存功能后?代码改成怎么样了?

A14:没使用缓存:SQL语句即可:select a from tab where b=1; 使用缓存:java语言和数据库语言结合: function get_data($key)   {     $data = $this->cache->get($key);     if($data != null)       return $data;     else     {       if($this->cache->getresultcode() == memcached::res_notfound)       {         //do the databse query here and fetch data         $this->cache->set($key,$data_returned_from_database);       }       else       {         error_log('no data for key '.$key);       }     }

 

Q15:怎样保持缓存和数据库中的数据实时一致?

A15:我们是通过应用逻辑实现,比如在更新数据库时,同步更新缓存,任何时候数据的变化都要先更新数据库,再更新缓存。

 

Q16:对于缓存数据的业务改造,为什么不考虑直接使用redis?

A16:redis也可以,也是选择之一,我们只是因为有人熟悉Memcache所以才选取了现在的方案。

 

作者介绍  朱祥磊

  • 山东移动BOSS系统架构师;

  • 负责业务支撑系统架构规划和建设;

  • 获国家级创新奖1项、通信行业级科技进步奖2项、移动集团级业务服务创新奖3项;

  • 申请发明专利13项。

     

 
 
近期热文(点击标题可阅读全文)

 

近期活动:

Gdevops全球敏捷运维峰会北京站

峰会官网:www.gdevops.com

 

DAMS第二届中国数据资产管理峰会

峰会官网:www.dams.org.cn