44.大数据之旅——java分布式项目05
架构图
单机Mysql存在的问题及解决办法
1.如果只有一台数据库服务器,存在什么问题?如何解决?
如果这台服务器宕机,在故障时间内无法对外提供数据库服务,但更严重的情况是数据的丢失。
解决的办法是:做主从复制。主从复制本质上是一种数据冗余策略,属于容灾系统。
引入主从复制架构,可以提高DB层的robust。此外,主从复制架构并且也是数据库读写分离的架构基础。
2.Master既要提供读服务,又要提供写服务,这种架构是否存在问题?
如果读写请求都交给Master来处理,则所有的负载压力都会集中到Master上,当Master节点超负荷时,极有可能宕机。
所以思路是想办法降低Master的负载,解决办法是:数据库读写分离。
Master节点只负责写请求,而Slave节点负责读请求。从而达到降低Master的负载压力的目的
读写分离,是一种业务类应用在解决读流量时,单机无无法承受的架构,学名scale out。注意:读写分离是假设在主从复制的基础上的。针对Mysql的读写分离,可以利用amoeba,mycat等技术来实现。
3.当读请求的量继续增大,已经超过一台机器的负载能力时,如何解决?
解决办法:负载均衡
这个概念不是数据库独有的概念,是指在由一个集群提供服务时,怎么把请求分配到集群中的某个服务器,能够最小化延迟,最大化集群的利用率。
Mysql的主从复制
概念介绍
Mysql作为目前世界上使用最广泛的免费数据库,在实际的生产环境中,由单台Mysql作为独立的数据库是完全不能满足实际需求的,无论是在安全性,高可用性以及高并发等各个方面。
因此,一般来说都是通过 主从复制(Master-Slave)的方式来同步数据,再通过读写分离(MySQL-Proxy)来提升数据库的并发负载能力这样的方案来进行部署与实施的。
主从复制原理说明
Mysql默认是不开启主从复制的,我们可以通过配置使其生效。
1.在Master-mysql上开启二进制日志服务(默认是关闭的)。这个服务开启后,Master-mysql会将数据更改类操作(比如增、删、改)记录到Binary log日志里。
2.Slave-mysql 会开启两个线程,一个是I/O线程,一个是SQL线程。
3.从服务器I/O线程读取主服务器Binlog log的内容并将该数据拷贝到本地文件中,即中继日志(Relay log)。
4.SQL线程监听本地中继日志,执行记录的SQL语句,从而完成两个数据库的内容同步。
Linux下配置主从复制
实现步骤
0.准备两台虚拟机,为了便于后续操作,关闭虚拟机防火墙
执行:service iptables stop
或者也可以通过打开3306端口。
1.上传和解压Percona安装包
2.按如下顺序安装Percona组件服务:
①rpm -ivh Percona-Server-56-debuginfo-5.6.24-rel72.2.el6.x86_64.rpm
②rpm -ivh Percona-Server-shared-56-5.6.24-rel72.2.el6.x86_64.rpm
③rpm -ivh Percona-Server-client-56-5.6.24-rel72.2.el6.x86_64.rpm
④rpm -ivh Percona-Server-server-56-5.6.24-rel72.2.el6.x86_64.rpm
3.启动Mysql服务
执行:service mysql start
4.修改数据库初始密码
执行:mysqladmin -u root password “root”;
5.登录验证
执行:mysql -uroot -proot
6.为了后续外部节点访问当前数据库,需要放开外部访问权限
在mysql数据库下,执行:grant all on *.* to 'root'@'%' identified by 'root';
语法说明:
grant [权限] on [数据库名].[表名] to [‘用户名’]@[‘web 服务器的ip地址’] identified by [‘密码’];
配置Master节点
7.如果以上步骤都操作成功,则停掉mysql服务,开始配置mysql-master节点。
执行:service mysql stop
8.进入目录:cd /var/lib/mysql
主从复制注意事项
Mysql的读写分离与amoeba
amoeba安装和配置
实现步骤:
1.关闭虚拟机防火墙(或者打开8066端口)
2.安装和配置jdk
①通过 tar -xvf jdk的jar包 安装JDK
②配置JDK的环境变量 vi /etc/profile
在尾行添加
#set java environment
JAVA_HOME=/home/software/jdk1.7
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME PATH CLASSPATH
保存退出
③source /etc/profile 使更改的配置立即生效
④java -version 查看JDK版本信息。
3.上传和解压amoeba
4.进入conf目录,对dbServer.xml进行配置
配置示例:
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">
<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">
<!--
Each dbServer needs to be configured into a Pool,
If you need to configure multiple dbServer with load balancing that can be simplified by the following configuration:
add attribute with name virtual = "true" in dbServer, but the configuration does not allow the element with name factoryConfig
such as 'multiPool' dbServer
-->
<dbServer name="abstractServer" abstractive="true">
<factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
<property name="manager">${defaultManager}</property>
<property name="sendBufferSize">64</property>
<property name="receiveBufferSize">128</property>
<!-- mysql port -->
<property name="port">3306</property>
<!-- mysql schema -->
<property name="schema">test</property>
<!-- mysql user -->
<property name="user">root</property>
<property name="password">root</property>
</factoryConfig>
<poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool">
<property name="maxActive">500</property>
<property name="maxIdle">500</property>
<property name="minIdle">10</property>
<property name="minEvictableIdleTimeMillis">600000</property>
<property name="timeBetweenEvictionRunsMillis">600000</property>
<property name="testOnBorrow">true</property>
<property name="testOnReturn">true</property>
<property name="testWhileIdle">true</property>
</poolConfig>
</dbServer>
<dbServer name="server1" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">192.168.234.231</property>
</factoryConfig>
</dbServer>
<dbServer name="server2" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">192.168.234.232</property>
</factoryConfig>
</dbServer>
<dbServer name="multiPool" virtual="true">
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
<!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
<property name="loadbalance">1</property>
<!-- Separated by commas,such as: server1,server2,server1 -->
<property name="poolNames">server1,server2</property>
</poolConfig>
</dbServer>
</amoeba:dbServers>
5.针对amoeba.xml 进行配置
配置示例:
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd">
<amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/">
<proxy>
<!-- service class must implements com.meidusa.amoeba.service.Service -->
<service name="Amoeba for Mysql" class="com.meidusa.amoeba.net.ServerableConnectionManager">
<!-- port -->
<property name="port">8066</property>
<!-- bind ipAddress -->
<!--
<property name="ipAddress">127.0.0.1</property>
-->
<property name="manager">${clientConnectioneManager}</property>
<property name="connectionFactory">
<bean class="com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory">
<property name="sendBufferSize">128</property>
<property name="receiveBufferSize">64</property>
</bean>
</property>
<property name="authenticator">
<bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">
<property name="user">root</property>
<property name="password">root</property>
<property name="filter">
<bean class="com.meidusa.amoeba.server.IPAccessController">
<property name="ipFile">${amoeba.home}/conf/access_list.conf</property>
</bean>
</property>
</bean>
</property>
</service>
<!-- server class must implements com.meidusa.amoeba.service.Service -->
<service name="Amoeba Monitor Server" class="com.meidusa.amoeba.monitor.MonitorServer">
<!-- port -->
<!-- default value: random number
<property name="port">9066</property>
-->
<!-- bind ipAddress -->
<property name="ipAddress">127.0.0.1</property>
<property name="daemon">true</property>
<property name="manager">${clientConnectioneManager}</property>
<property name="connectionFactory">
<bean class="com.meidusa.amoeba.monitor.net.MonitorClientConnectionFactory"></bean>
</property>
</service>
<runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext">
<!-- proxy server net IO Read thread size -->
<property name="readThreadPoolSize">20</property>
<!-- proxy server client process thread size -->
<property name="clientSideThreadPoolSize">30</property>
<!-- mysql server data packet process thread size -->
<property name="serverSideThreadPoolSize">30</property>
<!-- per connection cache prepared statement size -->
<property name="statementCacheSize">500</property>
<!-- query timeout( default: 60 second , TimeUnit:second) -->
<property name="queryTimeout">60</property>
</runtime>
</proxy>
<!--
Each ConnectionManager will start as thread
manager responsible for the Connection IO read , Death Detection
-->
<connectionManagerList>
<connectionManager name="clientConnectioneManager" class="com.meidusa.amoeba.net.MultiConnectionManagerWrapper">
<property name="subManagerClassName">com.meidusa.amoeba.net.ConnectionManager</property>
<!--
default value is avaliable Processors
<property name="processors">5</property>
-->
</connectionManager>
<connectionManager name="defaultManager" class="com.meidusa.amoeba.net.MultiConnectionManagerWrapper">
<property name="subManagerClassName">com.meidusa.amoeba.net.AuthingableConnectionManager</property>
<!--
default value is avaliable Processors
<property name="processors">5</property>
-->
</connectionManager>
</connectionManagerList>
<!-- default using file loader -->
<dbServerLoader class="com.meidusa.amoeba.context.DBServerConfigFileLoader">
<property name="configFile">${amoeba.home}/conf/dbServers.xml</property>
</dbServerLoader>
<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">
<property name="ruleLoader">
<bean class="com.meidusa.amoeba.route.TableRuleFileLoader">
<property name="ruleFile">${amoeba.home}/conf/rule.xml</property>
<property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xml</property>
</bean>
</property>
<property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property>
<property name="LRUMapSize">1500</property>
<property name="defaultPool">server1</property>
<property name="writePool">server1</property>
<property name="readPool">server2</property>
<property name="needParse">true</property>
</queryRouter>
</amoeba:configuration>
6.进入amoeba的bin目录
执行:vim amoeba
然后针对这行数据进行更改:把128k变成256k。这是amoeba最小的java栈大小。128k太小,会导致启动报错。
课后完成:和amoeba整合
实现步骤:
-
将京淘电商的数据库表,建立到mysql-master的数据库中的jtdb库中
-
京淘的数据库连接,更改为amoeba的代理连接。
配置京淘后台系统的jdbc.properties文件
配置示例:
- 启动京淘,测试是否能正确访问
架构缓存的问题
存在的问题
对于电商网站,在购物节时,大家都来到商场上浏览商品,购买商品。并且在大多数情况下,用户浏览的商品会集中在某些热门商品上,比如某新款手机、电脑,某折扣服饰等。在这种情况下,某一商品的url会产生极大的访问量。
缓存机制
缓存是介于应用程序和物理数据源之间,作用是为了降低应用程序对物理数据源访问的频次,从而提高应用的运行性能。缓存内的数据是对物理数据源中数据的复制,应用程序在运行时从缓存读数据,在特定的时刻或事件被触发时,缓存会同步物理数据源的数据。
缓存机制的介质一般是内存,所以读写速度很快。目前也有用SSD固态硬盘做缓存介质的情况。
引入缓存机制后新的问题和挑战
1.缓存的与物理数据源的数据一致性问题。如果物理数据源的数据更新,但是缓存里的数据没更新,这时用户如果从缓存里读数据就是错误数据。
2.缓存数据的生命周期问题。即缓存里的某些数据不可能一直存放在缓存里,比如一些几乎不会被访问的数据、一些历史周期较长的数据存在缓存里,占用宝贵的内存资源。所以哪些缓存数据该定期进行清理、哪些缓存数据该保留,这不仅仅要通过代码,还要结合实际业务情况。
3.缓存里数据的落地问题(数据持久化)。
4.缓存服务器的单点故障问题。比如一个电商网站,如果只用一台服务器做缓存,如果这台缓存服务器挂掉,就会去数据库访问数据,此时极有可能带来缓存雪崩现象。
雪崩现象指的是:当缓存服务器重启或者大量缓存集中在某一个时间段失效,在失效阶段里,本来应该涌入缓存的大量访问请求,都涌入了后端系统(比如DB),在短时间内给服务器带来特别高的负载,这种现象称之为雪崩现象。雪崩最严重的后果是使服务器失去响应能力,甚至宕机。
5.主—从缓存架构的主节点失效检测问题,即需要有一个检测机制,能够检测到主节点的状态,一旦主节点宕机能够把从节点变成主节点继续对外提供服务。
……