博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
企业高并发的成熟解决方案(二) ----数据库高并发解决方案
阅读量:6948 次
发布时间:2019-06-27

本文共 8519 字,大约阅读时间需要 28 分钟。

hot3.png

1.数据库高并发解决方案演示及说明

140239_VrW8_867113.png

我们要解决的是数据库层面的高并发 ,App服务器上安装Tomcat,访问后端的Mysql集群

一台Tomcat连接多台数据库是如果连接的? 连接Mysql/Orcal的问题:

    1:一台Tomcat 连接 多台数据库是如果连接的?

    2:连接之后,是怎么解决高并发的?

下面我们用Mysql数据库举例说明解决数据库高并发的方案。

补充说明:为什么现在更多的互联网公司使用Mysql集群而不使用Oracle数据库?

①马云目前旗下:淘宝 天猫 支付宝 余额宝 阿里巴巴,统统不使用Oracle,使用自己公司开发的MyFox,MyFox就是以Mysql为基础开发而来的,Oracle 是非常贵的,Oracle收费是按照CPU收费的 无限期使用 32万美元 ~ 200万美元,紧紧是一台Oracle数据库的费用;  

②互联网企业开发一般并发量比较大,要使用到数据库集群,单独一台Oracle是没什么意义的,N多台Oracle数据库的费用是相当昂贵的,并且Oracle本身不是开源的.一旦出现问题,必须找Oracle公司的在当地服务商代表来解决,需要收取服务费,别是中小企业或是创业型公司是使用不起的;

2.这里主要用Mysql数据库举例,演示的一套Java写的项目(Spring+ibatis框架),此项目的作用:

 1)可以连接N多台Mysql;   

 2)可以检查后边Mysql的状态,并支持主Mysql与备Mysql切换;

 3)如果其中一台Mysql数据库宕机,可以让程序代码自动访问另外一台备机;

 4)此项目支持水平拆分,库表散列  

现在有六个数据库,分别为itcast01-itcast06,用来做演示:

142445_5urP_867113.png

项目应用场景:

Linux服务器上安装有一Mysql实例,对实例进行分库,分表操作,给大家分成六个库itcast01-itcast06作演示:

itcast01  itcast04   是一组    partition1 

itcast02  itcast05   是一组    partition2

itcast03 itcast06    是一组    partition3 

要求:1:存数据到6个库中; 

      2:取数据从6个库中;

Java写的项目架构(spring+ibatis,SI框架),开启eclipse,代码结构如下:

144021_wfxD_867113.png

icast01-itcast06数据库中都有一张user表,使用到的user表的表结构如下:

144216_zEpU_867113.png

应用的测试类的代码如下:

package com;import java.util.List;import org.junit.Ignore;import org.junit.Test;import org.springframework.beans.factory.annotation.Autowired;import com.caland.common.junit.AbstractSpringJunitTest;import com.caland.core.bean.User;import com.caland.core.query.UserQuery;import com.caland.core.service.UserService;/** * junit */public class TestUser extends AbstractSpringJunitTest{	//测试用户添加	//看看能不能添加到指定库去	//写好了Service  Dao  	//直接使用 自动装配	@Autowired	private UserService userService;	//开始运行了  		@Test	@Ignore   //忽略	public void testAddUser() throws Exception {		//用户对象是跟数据库一一对应的JavaBean		User user = new User();		user.setUsername("赵六");		user.setAge(36);		user.setPhone(138888888);		user.setEmail("888888@qq.com");		//把此数据添加到数据库去		userService.addUser(user);	}	//测试取数据  	@Test	//@Ignore	public void testGetUser() throws Exception {				//使用用户的Service层来取		//知道只有一条		//实际当中  我们是不知道有多少条的		//返回的一定是List集合		//创建用户条件对象		UserQuery  userQuery = new UserQuery();		//设置用户名  为  赵六  或李四		userQuery.setUsername("李四");		//返回结果		List
users = userService.getUserList(userQuery); for(User user : users){ //输出结果 System.out.println(user.toString()); } }}

其中testAddUser方法是向数据库中增加用户记录,testGetUser方法是从数据库中取出用户信息,

分别测试将'李四','赵六'两个用户存储到相应的数据库中,并从相应的数据库中取出这两个人的用户信息,结果如下:

150423_qNHi_867113.png

此项目就是支持水平拆分,库表散列,只演示了用户表User,新建一个测试类TestUser来进行存取用户信息的演示: 

第一项:支持数据水平拆分和读取吗?

①测试用户'李四'的用户信息保存到itcast02库中,验证是否在02库中有数据,只有itcast02库有数据提示;  

② 测试用户'赵六'的用户信息保存到itcast03库中,验证是否在03库中有数据, 只有itcast03库有数据提示;经过二次用户信息的保存,证明用户名路由规则是正确的; 

验证:用户信息保存进去了,是不是能取出来呢?    

③李四的用户信息在第2个库中,可以正常的取出李四的用户信息;

④赵六的用户信息在第3个库中,可以正常的取出赵六的用户信息;

总结:能存能取 功能是没问题的

第二项:支持数据容灾吗?    

心跳检查数据,主库挂了,备库接管  

①把李四保存到备库中呢?  

李四是存在02中,如果把itcast02库删除了的话,那么李四还能保存吗?

证明:02库挂了,05库接管它的工作,保存了李四的用户信息;

②如果02库和05库都挂了,那就真的不能保存用户信息了,只能备份还原数据库服务器了;

注意:本项目演示是以单元Junit测试的,所有的对象都需要实例化,所以有会些慢。如果是以Tomcat启动的 ,启动的速度就会非常快了。

代码配置分析:

151012_iyTi_867113.png

151109_6NlK_867113.png

application-context.xml的配置信息如下:

classpath:/jdbc.properties
classpath:/dbRule/sharding-rules-on-namespace.xml
classpath:/sqlmap-config.xml

sharding-rules-on-namespace.xml的配置信息如下:

Order
hash.applyOrder(userId) == 1
partition1
Order
hash.applyOrder(userId) == 2
partition2
Order
hash.applyOrder(userId) == 3
partition3
User
hash.applyUser(username) == 1
partition1
User
hash.applyUser(username) == 2
partition2
User
hash.applyUser(username) == 3
partition3

HashFunction哈希算法的代码如下:

package com.caland.core.dao.router;/** * 根据某种自定义的hash算法来进行散列,并根据散列的值进行路由 *  常见的水平切分规则有:	基于范围的切分, 比如 memberId > 10000 and memberId < 20000	基于模数的切分, 比如 memberId%128==1 或者 memberId%128==2 或者...	基于哈希(hashing)的切分, 比如hashing(memberId)==someValue等 * @author lixu * */public class HashFunction{	/**	 * 对三个数据库进行散列分布	 * 1、返回其他值,没有在配置文件中配置的,如负数等,在默认数据库中查找	 * 2、比如现在配置文件中配置有三个结果进行散列,如果返回为0,那么apply方法只调用一次,如果返回为2,	 *   那么apply方法就会被调用三次,也就是每次是按照配置文件的顺序依次的调用方法进行判断结果,而不会缓存方法返回值进行判断	 * @param id	 * @return	 */	public int applyOrder(Integer userId) {		//先从缓存获取 没有则查询数据库		//input 可能是id,拿id到缓存里去查用户的DB坐标信息。然后把库的编号输出		int result = (userId % 1024);		System.out.println("hash:" + result);		if(0 <= result && result < 256){			result = 0;			System.out.println("在第1个数据库中");		}		if(256 <= result && result < 512){			result = 1;			System.out.println("在第2个数据库中");		}		if(512 <= result && result < 1024){			result = 2;			System.out.println("在第3个数据库中");		}		return result;	}	/**	 * 对三个数据库进行散列分布	 * 1、返回其他值,没有在配置文件中配置的,如负数等,在默认数据库中查找	 * 2、比如现在配置文件中配置有三个结果进行散列,如果返回为0,那么apply方法只调用一次,如果返回为2,	 *   那么apply方法就会被调用三次,也就是每次是按照配置文件的顺序依次的调用方法进行判断结果,而不会缓存方法返回值进行判断	 * @param id	 * @return	 */	public int applyUser(String username) {		//先从缓存获取 没有则查询数据库		//input 可能是id,拿id到缓存里去查用户的DB坐标信息。然后把库的编号输出		int result = Math.abs(username.hashCode() % 1024);//0---1023		System.out.println("hash:" + result);//333		if(0 <= result && result < 256){			result = 1;			System.out.println("在第1个数据库中");		}		if(256 <= result && result < 512){			result = 2;			System.out.println("在第2个数据库中");		}		if(512 <= result && result < 1024){			result = 3;			System.out.println("在第3个数据库中");		}		return result;	}}

注意:本示例只是为了演示一下分库而已,只有主机有数据,没有同步备机,没有搭建主从复制和双主结构。

----------------------------------------------------------------------------------------------

现在,软件架构变得越来越复杂了,好多技术层出不穷,令人眼花缭乱,解决这个问题呢,就是要把复杂问题简单化,核心就是要把握本质。

软件刚开始的时候是为了实现功能,随着信息量和用户的增多,大数据和高并发成了软件设计必须考虑的问题,那么大数据和高并发本质是什么呢?

本质很简单,一个是慢,一个是等。两者是相互关联的,因为慢,所以要等,因为等,所以慢,解决了慢,也就解决了等,解决了等,也就解决了慢。

 

关键是如何解决慢和等,核心一个是,一个是,一个是分流

是指路径要短。典型的mvc结构是请求->controller->model->dao->view,然后把页面返回给用户。要想短的话,

1,页面静态化- 用户可以直接获取页面,不用走那么多流程,比较适用于页面不频繁更新。

2,使用缓存- 第一次获取数据从数据库准提取,然后保存在缓存中,以后就可以直接从缓存提取数据。不过需要有机制维持缓存和数据库的一致性,像使用redis、memcache和mongodb等NoSQL进行数据存储。

3,使用储存过程-那些处理一次请求需要多次访问数据库的操作,可以把操作整合到储存过程,这样只要一次数据库访问就可以了。

4,批量读取 - 高并发情况下,可以把多个请求的查询合并到一次进行,以减少数据库的访问次数

5,延迟修改 - 高并发情况下,可以把多次修改请求,先保存在缓存中,然后定时将缓存中的数据保存到数据库中,风险是可能会断电丢失缓存中的数据,

6,  使用索引 - 索引可以看作是特殊的缓存,尽量使用索引就要求where字句中精确的给出索引列的值。

是指查询的数据要少:

1,分表 - 把本来同一张表的内容,可以按照地区,类别等分成多张表,很简单的一个思路,但是要尽量避免分出来的多表关联查询。

2,分离活跃数据 - 例如登录用户业务,注册用户很多,但是活跃的登录用户很少,可以把活跃用户专门保存一张表,查询是先查询活跃表,没有的话再查总表,这也类似与缓存啦。

3, 分块 - 数据库层面的优化,对程序是透明的,查询大数据只用找到相应块就行。

分流分为三种:

1,集群 - 将并发请求分配到不同的服务器上,可以是业务服务器,也可以是数据库服务器。

2,分布式 - 分布式是把单次请求的多项业务逻辑分配到多个服务器上,这样可以同步处理很多逻辑,一般使用与特别复杂的业务请求。

3,CDN - 在域名解析层面的分流,例如将华南地区的用户请求分配到华南的服务器,华中地区的用户请求分配到华中的服务器。

暂时总结这么多的方案,随着技术的进步,会有更多的方案出现,一起成长进步中。。。。。

转载于:https://my.oschina.net/bigdataer/blog/486772

你可能感兴趣的文章
06 - C语言的预处理指令和关键字static|extern|typedef
查看>>
SpringCloud学习系列之四-----配置中心(Config)使用详解
查看>>
基于CSS属性display:table的表格布局的使用
查看>>
redis (四)散列类型
查看>>
openresty 前端开发入门一
查看>>
jQuery validate中添加失去焦点验证事件
查看>>
PHP面向对象关键知识点回顾
查看>>
SpringMVC Controller 介绍
查看>>
虚拟机的类加载机制
查看>>
登录判断跳转页面
查看>>
多线程IO操作(扫描文件夹并计算总大小)
查看>>
读UNIX编程艺术(一)
查看>>
oracle存储过程获取异常信息码和异常信息
查看>>
大系统小做培训总结
查看>>
javascript闭包与函数式编程
查看>>
基于ceph-rbd的文件共享
查看>>
Web Service 那点事儿(3)—— SOAP 及其安全控制
查看>>
自定义按钮
查看>>
Android手机摄像头作为PC摄像头开发
查看>>
Docker
查看>>