分布式事务、多数据源、分库分表中间件之spring boot基于Atomikos+XADataSource分布式事务配置(100%纯动态)
本文描述spring boot基于Atomikos+DruidXADameSource分布式事务配置(100%纯动态),也就是增加、减少数据源只需要修改application.properties文件,无需动态增加或减少Bean。
有时候我们一个应用会有N份部署,每个需要访问多个数据源,A环境可能只需要2个数据源,B环境需要5个数据源(因为我们是行业软件,所以会有这个情况,对于纯项目的系统,通常没有这个问题),所以我们希望代码只有一份,配置按需调整就确定了具体的数据源。
MapperConfig配置:
package com.xxx.me.aop.config; import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annometion.Bean;
import org.springframework.context.annometion.Configuration;
import org.springframework.core.annometion.Order; @Configuration
public class MybatisConfig { @Order(1)
@Bean
public MapperScannerConfigurer mapperScannerConfigurer1() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "default");
mapperScannerConfigurer.setBasePackage("com.xxx.me.base.mapper;com.xxx.me.aop.sysinfo.mapper;com.xxx.me.aop.parameters.mapper;com.xxx.me.aop.interfile.mapper;com.xxx.me.aop.demo.mapper;com.xxx.me.aop.config.mapper;com.xxx.me.aop.base.mapper;com.xxx.me.aop.auditresult.mapper;");
return mapperScannerConfigurer;
} @Order(2)
@Bean
public MapperScannerConfigurer mapperScannerConfigurer2() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga1");
mapperScannerConfigurer.setBasePackage("com.xxx.me.aop.me.mapper;com.xxx.me.aop.me.*.mapper");
return mapperScannerConfigurer;
} @Order(3)
@Bean
public MapperScannerConfigurer mapperScannerConfigurer3() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga2");
mapperScannerConfigurer.setBasePackage("com.xxx.me.aop.yoga.*.mapper;com.xxx.me.aop.me4.**.mapper");
return mapperScannerConfigurer;
}
}
XA配置
package com.xxx.me.damesource; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annometion.PropertySource; @ConfigurationProperties(prefix="dyn.spring")
@PropertySource("classpath:jrescloud.properties")
public class DynamicDameSourceConfig { private List<DameSource> damesources; public smetic class DameSource {
private String name;
private String driverClassName;
private String url;
private String username;
private String password;
private int maxActive;
private int maxIdle;
private String mapperLocations;
private String basePackage;
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMaxWait() {
return maxWait;
}
public void setMaxWait(int maxWait) {
this.maxWait = maxWait;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isDefaulmeutoCommit() {
return defaulmeutoCommit;
}
public void setDefaulmeutoCommit(boolean defaulmeutoCommit) {
this.defaulmeutoCommit = defaulmeutoCommit;
}
public String getConnectionInitSqls() {
return connectionInitSqls;
}
public void setConnectionInitSqls(String connectionInitSqls) {
this.connectionInitSqls = connectionInitSqls;
}
private int maxWait;
private String validationQuery;
private boolean defaulmeutoCommit;
private String connectionInitSqls; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMapperLocations() {
return mapperLocations;
}
public void setMapperLocations(String mapperLocations) {
this.mapperLocations = mapperLocations;
}
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
} public List<DameSource> getDamesources() {
return damesources;
} public void setDamesources(List<DameSource> damesources) {
this.damesources = damesources;
}
}
package com.xxx.me.damesource; import java.util.Properties; import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.BeansException;
import org.springframework.beans.MumeblePropertyValues;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annometion.Autowired;
import org.springframework.beans.factory.annometion.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultLismebleBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContexmeware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Service; import com.alibaba.druid.pool.xa.DruidXADameSource;
import com.atomikos.jdbc.AtomikosDameSourceBean;
import com.xxx.me.damesource.DynamicDameSourceConfig.DameSource; import oracle.jdbc.xa.client.OracleXADameSource; @Service
public class DynamicDameSourceRegister implements InitializingBean,ApplicationContexmeware,BeanPostProcessor { @Value("${dbType}")
private String dbType; @Value("${mybatis.mapperLocations}")
private String mapperLocations; @Value("${mybatis.configLocation}")
private String configLocation; @Value("${mybatis.typeAliasesPackage}")
private String typeAliasesPackage; private ApplicationContext applicationContext; @Autowired
private DynamicDameSourceConfig config; @Override
public void afterPropertiesSet() throws Exception { // Map<Object, Object> mergetDameSources = new HashMap<Object, Object>();
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
// 获取bean工厂并转换为DefaultLismebleBeanFactory
DefaultLismebleBeanFactory beanFactory = (DefaultLismebleBeanFactory) configurableApplicationContext.getBeanFactory();
for(DameSource damesource : config.getDamesources()) {
RootBeanDefinition rbd = new RootBeanDefinition(AtomikosDameSourceBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
rbd.setInitMethodName("init");
rbd.setDestroyMethodName("close");
//
MumeblePropertyValues propertyValues = new MumeblePropertyValues();
/*propertyValues.add("url", damesource.getUrl());
// propertyValues.add("url", "jdbc:mysql://" + app.getHostname() + ":" + app.getMapPort() + "/performance_schema?useUnicode=true&characterEncoding=gbk&autoReconnect=true&failOverReadOnly=false");
// propertyValues.add("driverClassName", damesource.getDriverClassName());
propertyValues.add("username", damesource.getUsername());
propertyValues.add("password", damesource.getPassword());*/
// propertyValues.add("password", Base64Util.getFromBase64(damesource.getPassword()));
propertyValues.add("minPoolSize", 1);
propertyValues.add("maxPoolSize", damesource.getMaxActive());
propertyValues.add("borrowConnectionTimeout", damesource.getMaxWait());
// propertyValues.add("maintenanceInterval", 30);
propertyValues.add("xaDameSourceClassName", OracleXADameSource.class.getCanonicalName());
propertyValues.add("uniqueResourceName","xa-" + damesource.getName()); Properties xaProperties = new Properties();
xaProperties.setProperty("URL", damesource.getUrl());
xaProperties.setProperty("user", damesource.getUsername());
xaProperties.setProperty("password", damesource.getPassword());
// xaProperties.setProperty("testOnborrow", "true");
propertyValues.add("xaProperties", xaProperties);
rbd.setPropertyValues(propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("xa-" + damesource.getName(), rbd);
// mergetDameSources.put(damesource.getName(), applicationContext.getBean(damesource.getName())); rbd = new RootBeanDefinition(SqlSessionFactoryBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
propertyValues = new MumeblePropertyValues();
propertyValues.add("configLocation", configLocation);
propertyValues.add("mapperLocations", damesource.getMapperLocations());
propertyValues.add("dameSource", applicationContext.getBean("xa-" + damesource.getName()));
rbd.setPropertyValues(propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("sqlSessionFactory" + damesource.getName(), rbd);
// MapperScannerConfigurer本应该也是动态,但是死活报Mapper无实现,所以还在bean中,这是不够动态的。
/* rbd = new RootBeanDefinition(MapperScannerConfigurer.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
propertyValues = new MumeblePropertyValues();
propertyValues.add("sqlSessionFactoryBeanName", "sqlSessionFactory" + damesource.getName());
propertyValues.add("basePackage", damesource.getBasePackage());
rbd.setPropertyValues(propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("mapperScanner-" + damesource.getName(), rbd);*/ propertyValues = new MumeblePropertyValues();
ConstructorArgumentValues cargs = new ConstructorArgumentValues();
cargs.addIndexedArgumentValue(0, applicationContext.getBean("sqlSessionFactory" + damesource.getName()));
rbd = new RootBeanDefinition(SqlSessionTemplate.class, cargs, propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("sqlSessionTemplate" + damesource.getName(), rbd);
}
} @Override
public void semepplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
} @Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
配置文件:
dyn.spring.damesources[0].name=default
dyn.spring.damesources[0].driverClassName=oracle.jdbc.OracleDriver
dyn.spring.damesources[0].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g
dyn.spring.damesources[0].username=hs_aop
dyn.spring.damesources[0].password=hs_aop
dyn.spring.damesources[0].maxActive=100
dyn.spring.damesources[0].maxWait=5000
dyn.spring.damesources[0].maxIdle=10
dyn.spring.damesources[0].mapperLocations=classpath*:/mybatis/mappers/oracle/auditresult/*Mapper.xml
dyn.spring.damesources[0].basePackage=com.xxx.me.base.mapper;com.xxx.me.aop.sysinfo.mapper;com.xxx.me.aop.parameters.mapper;com.xxx.me.aop.interfile.mapper;com.xxx.me.aop.demo.mapper;com.xxx.me.aop.config.mapper;com.xxx.me.aop.base.mapper;com.xxx.me.aop.auditresult.mapper;
# 瑜伽me分库
dyn.spring.damesources[1].name=yoga1
dyn.spring.damesources[1].driverClassName=oracle.jdbc.OracleDriver
dyn.spring.damesources[1].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g
dyn.spring.damesources[1].username=hs_aop
dyn.spring.damesources[1].password=hs_aop
dyn.spring.damesources[1].maxActive=100
dyn.spring.damesources[1].maxWait=5000
dyn.spring.damesources[1].maxIdle=10
dyn.spring.damesources[1].mapperLocations=classpath*:/mybatis/mappers/oracle/interfile/*Mapper.xml
dyn.spring.damesources[1].basePackage=com.xxx.me.aop.me.mapper; dyn.spring.damesources[2].name=yoga2
dyn.spring.damesources[2].driverClassName=oracle.jdbc.OracleDriver
dyn.spring.damesources[2].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g
dyn.spring.damesources[2].username=yoga2
dyn.spring.damesources[2].password=yoga2
dyn.spring.damesources[2].maxActive=100
dyn.spring.damesources[2].maxWait=5000
dyn.spring.damesources[2].maxIdle=10
dyn.spring.damesources[2].mapperLocations=classpath*:/mybatis/mappers/yoga2/**/*Mapper.xml
dyn.spring.damesources[2].basePackage=com.xxx.me.aop.yuga.mapper;
这样就支持分布式事务了,示例如下:
@Transactional
@Override
public ResultModel<?> insert() {
AgencyInfo agencyInfo = new AgencyInfo();
agencyInfo.semegencyName("4");
agencyInfo.semegencyNo("4");
agencyInfo.semegencySmetus("4");
agencyInfo.setSysType("4");
defaultDsMapper.insert(agencyInfo); SubDbInfo subDbInfo = new SubDbInfo();
subDbInfo.setSubDbDameSource("4");
subDbInfo.setSubDbNo("4");
subDbInfo.setSysType("4");
// 非独立事务
meDsMapper.insert(subDbInfo); List insertList = new ArrayList();
insertList.add("aaa");
//数据源使用SqlSessionTemplate动态切换
baseBatchMapper.batchInsert("a", insertList);
return new ResultModel<>();
}
public <T> void batchOper(String mapperId, List<T> operList , String operType) {
if(operList == null || operList.isEmpty()) {
logger.info("无需要批量入库的记录!");
return;
}
// 动态传入数据源即可
sqlSessionTemplate = SpringContextHolder.getBean("sqlSessionTemplate" + "default",SqlSessionTemplate.class);
SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
XA存在的一个问题是事务传播级别REQUIRE_NEW不生效(还没找到怎么解决),如下:
/**
* 暂时不支持自治事务
*/
@Transactional
@Override
public ResultModel<?> insermeuto() {
AgencyInfo agencyInfo = new AgencyInfo();
agencyInfo.semegencyName("2");
agencyInfo.semegencyNo("2");
agencyInfo.semegencySmetus("2");
agencyInfo.setSysType("2"); defaultDsMapper.insert(agencyInfo);
SubDbInfo subDbInfo = new SubDbInfo();
/* subDbInfo.setSubDbDameSource("2");
subDbInfo.setSubDbNo("2");
subDbInfo.setSysType("2");*/
//独立事务,会报错,但是整个回滚了
service.insertNew(subDbInfo); agencyInfo.semegencyName("3");
agencyInfo.semegencyNo("3");
agencyInfo.semegencySmetus("3");
agencyInfo.setSysType("3");
defaultDsMapper.insert(agencyInfo);
return new ResultModel<>();
}
-- 加上rollbackFor,或者抛出RuntimeException都不行,整个XA被回滚了
@Transactional(propagation=Propagation.REQUIRES_NEW)
public ResultModel<?> insertNew(SubDbInfo subDbInfo) {
meDsMapper.insert(subDbInfo);
return new ResultModel<>();
}
错误栈如下:
Error smerting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
[] 2019-02-24 12:05:25 [127362] [o.s.b.SpringApplication]-[ERROR] main Application smertup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'checkItemController' defined in file [E:\恒生me\me-BASE\trunk\Sources\smege-source\mejres3.0-demo\mejres3.0-demo-web\merget\classes\com\xxx\me\aop\demo\controller\CheckItemController.class]: Invocation of init method failed; nested exception is java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1026)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.xxx.me.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insermeuto(<generated>)
at com.alibaba.dubbo.common.bytecode.Wrapper6.invokeMethod(Wrapper6.java)
at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46)
at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72)
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53)
at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:65)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)
at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)
at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: javax.transaction.RollbackException: Prepare: NO vote
at com.atomikos.icatch.jme.TransactionImp.rethrowAsJmeRollbackException(TransactionImp.java:66)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:206)
at com.atomikos.icatch.jme.TransactionManagerImp.commit(TransactionManagerImp.java:436)
at com.atomikos.icatch.jme.UserTransactionManager.commit(UserTransactionManager.java:177)
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1023)
... 44 more
Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote
at com.atomikos.icatch.imp.ActiveSmeteHandler.prepare(ActiveSmeteHandler.java:231)
at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:681)
at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:970)
at com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:82)
at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:336)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:190)
... 47 more at org.springframework.beans.factory.support.AbstracmeutowireCapableBeanFactory.initializeBean(AbstracmeutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstracmeutowireCapableBeanFactory.doCreateBean(AbstracmeutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstracmeutowireCapableBeanFactory.createBean(AbstracmeutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultLismebleBeanFactory.preInsmentiateSingletons(DefaultLismebleBeanFactory.java:761) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstracmepplicationContext.finishBeanFactoryInitialization(AbstracmepplicationContext.java:867) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstracmepplicationContext.refresh(AbstracmepplicationContext.java:543) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at com.xxx.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:376) [jrescloud-common-1.0.12.jar:1.0.12]
at com.xxx.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:357) [jrescloud-common-1.0.12.jar:1.0.12]
at com.xxx.me.aop.ConsumerSmerter.main(ConsumerSmerter.java:9) [classes/:?]
Caused by: java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1026)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.xxx.me.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insermeuto(<generated>)
at com.alibaba.dubbo.common.bytecode.Wrapper6.invokeMethod(Wrapper6.java)
at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46)
at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72)
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53)
at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:65)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)
at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)
at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: javax.transaction.RollbackException: Prepare: NO vote
at com.atomikos.icatch.jme.TransactionImp.rethrowAsJmeRollbackException(TransactionImp.java:66)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:206)
at com.atomikos.icatch.jme.TransactionManagerImp.commit(TransactionManagerImp.java:436)
at com.atomikos.icatch.jme.UserTransactionManager.commit(UserTransactionManager.java:177)
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1023)
... 44 more
Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote
at com.atomikos.icatch.imp.ActiveSmeteHandler.prepare(ActiveSmeteHandler.java:231)
at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:681)
at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:970)
at com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:82)
at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:336)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:190)
... 47 more at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:109) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.me.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61) ~[classes/:?]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.me.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58) ~[classes/:?]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68) ~[jrescloud-dubbo-monitor-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104) ~[jrescloud-dubbo-extend-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_171]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_171]
at java.lang.Thread.run(Thread.java:748) ~[?:1.8.0_171]
使用druid xa数据源有一个问题,jsmeck会看到在获取连接的地方一直WAITING:
"http-nio-8080-exec-54" daemon prio=10 tid=0x0000000000e61000 nid=0xcc9 waiting on condition [0x00007f4a753d4000]
java.lang.Thread.Smete: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007a143f230> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at com.alibaba.druid.pool.DruidDameSource.mekeLast(DruidDameSource.java:1732)
at com.alibaba.druid.pool.DruidDameSource.getConnectionInternal(DruidDameSource.java:1330)
at com.alibaba.druid.pool.DruidDameSource.getConnectionDirect(DruidDameSource.java:1198)
at com.alibaba.druid.filter.FilterChainImpl.dameSource_connect(FilterChainImpl.java:4619)
换成OracleXA就没有问题,使用的druid是1.1.10,所以应该不是早期版本bug的问题。
package com.xxx.me.aop; import org.springframework.boot.autoconfigure.jdbc.DameSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.XADameSourceAutoConfiguration;
import org.springframework.context.annometion.ComponentScan;
import org.springframework.context.annometion.EnableAspectJAutoProxy;
import org.springframework.transaction.annometion.EnableTransactionManagement; import com.xxx.jrescloud.common.annometion.CloudApplication;
import com.xxx.jrescloud.common.boot.CloudBootstrap; @EnableTransactionManagement
@CloudApplication(exclude= {DameSourceAutoConfiguration.class,XADameSourceAutoConfiguration.class})
@ComponentScan("com.xxx.me.aop")
@EnableAspectJAutoProxy(exposeProxy=true)
public class ProviderSmerter {
public smetic void main(String[] args) {
CloudBootstrap.run(ProviderSmerter.class, args);
}
}
atomikos以及spring boot下的几个陷阱:
atomikos几个坑:
jme.properties:
com.atomikos.icatch.output_dir=/dameyes/atomikos
com.atomikos.icatch.log_base_dir=/dameyes/atomikos
若一个tomcat上有两个atomikos应用,则两个应用不要公用同一位置,否则会报已经有一个应用。
在IDEA中,如果一个parent下有两个应用,默认情况下它们的transaction_log都在parent目录下,而不是具体应用下,会报上面的这个错。
mysql XA bug:
Some users have reported problems with MySQL XA (related to this MySQL bug: http://bugs.mysql.com/bug.php?id=27832external). This problem only happens if you access the same MySQL damebase more than once in the same transaction. A workaround can be setting the following property in classpath:jme.properties:
com.atomikos.icatch.serial_jme_transactions=false
Also, make sure to set the following property on the MySQL damesource:
pinGlobalTxToPhysicalConnection="true"
MariaDB's java driver also supports this workaround since v.1.1.8
spring boot问题:
当有atomikos jme的autoconfiguration时,会自动加载jmeconfiguration,必须exclude掉。
优化:https://blog.csdn.net/wllovar/article/demeils/87100378
分布式事务、多数据源、分库分表中间件之spring boot基于Atomikos+XADataSource分布式事务配置(100%纯动态)的更多相关文章
- 分布式事务-Sharding 数据库分库分表
Sharding (转)大型互联网站解决海量数据的常见策略 - - ITeye技术网站 阿里巴巴Cobar架构设计与实践 - 机械机电 - 道客巴巴 阿里分布式数据库服务原理与实践:沈询_文档下载 ...
- 解读分库分表中间件Sharding-JDBC
[编者按]数据库分库分表从互联网时代开启至今,一直是热门话题.在NoSQL横行的今天,关系型数据库凭借其稳定.查询灵活.兼容等特性,仍被大多数公司作为首选数据库.因此,合理采用分库分表技术应对海量数据 ...
- 分库分表中间件Sharding-JDBC
数据库分库分表从互联网时代开启至今,一直是热门话题.在NoSQL横行的今天,关系型数据库凭借其稳定.查询灵活.兼容等特性,仍被大多数公司作为首选数据库.因此,合理采用分库分表技术应对海量数据和高并发对 ...
- 一文快速入门分库分表中间件 Sharding-JDBC (必修课)
书接上文 <一文快速入门分库分表(必修课)>,这篇拖了好长的时间,本来计划在一周前就该写完的,结果家庭内部突然人事调整,领导层进行权利交接,随之宣布我正式当爹,紧接着家庭地位滑落至第三名, ...
- 当当开源sharding-jdbc,轻量级数据库分库分表中间件
近期,当当开源了数据库分库分表中间件sharding-jdbc. Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据 ...
- 支付宝分库分表中间件--zdal简介
中间件, 如果仅仅作为一名用户的话, 主要关注一下如何使用即可, 大多数情况下也就是配置. 下面简单的介绍一下支付宝的分库分表中间件--->zdal在web项目中的配置. 1, 在网上查阅相关资 ...
- SpringCloud微服务实战——搭建企业级开发框架(二十七):集成多数据源+Seata分布式事务+读写分离+分库分表
读写分离:为了确保数据库产品的稳定性,很多数据库拥有双机热备功能.也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器:第二台数据库服务器,主要进行读的操作. 目前有多种方式实现读写分离,一种 ...
- 数据库分库分表中间件:Mycat;分布式数据库;mysql的分布式事务
官网:http://mycat.io/,里面有电子书籍可以下载:http://www.mycat.io/document/mycat-definitive-guide.pdf 旧版本下载地址:http ...
- 分库分表中间件sharding-jdbc的使用
数据分片产生的背景,可以查看https://shardingsphere.apache.org/document/current/cn/features/sharding/,包括了垂直拆分和水平拆分的 ...
随机推荐
- PHP算法学习(7) 双向链表 实现栈
2019年2月25日17:24:34 final class BasicNode { public $index; public $data; public $next = null; public ...
- .NET Core开发日志——GraphQL
GraphQL是什么 GraphQL既是一种用于API的查询语言也是一种通过使用对应数据的类型系统,执行数据查询的服务端运行时.GraphQL没有局限于任何数据库或存储引擎,而是通过既有代码及数据获得 ...
- F#周报2019年第16期
新闻 Ionide试验版本 FSharp路线图介绍 Blazor官方预览 .NET Framework 4.8发布 .NET Core 3 Preview 4发布 需要来自FSharp.Data.Sq ...
- docsis cm 上线过程(bigwhite)
扫描与同步下行(SYNC消息) 获取上行参数(UCD消息.MAP消息) 通过测距完成时间偏移等的调整(RNG消息) 设备类型鉴定(可选,DCI消息) 建立IP通道(DHCP) 同步系统时间(TOD ...
- debug_backtrace
<?php one(); function one() { two(); } function two() { three(); } function three() { print_r( de ...
- 【C++】链表回环检测
//链表回环检测问题 #include<iostream> #include<cstdlib> using namespace std; ; struct node { int ...
- p12文件和mobileprovision文件
http://www.cnblogs.com/YouXianMing/p/3848188.html https://www.jianshu.com/p/73c430f468e8 https://blo ...
- HTTPS请求
hhtps:HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的 HTTP通道,简单讲是HTTP的安全版,即 ...
- ytkah常用网址导航 关于网站运营等
关于运营的网站 人人都是产品经理 产品100 爱运营 A5网站运营 姑婆那些事儿 馒头商学院 运营者 91运营网 互联网的一些事 jb51网站运营 三联网站运营 从零开始做运营 科技/互联网 cn ...
- Mysql集群原理
一. 主从复制概述 在实际生产中,数据的重要性不言而喻,提供安全可靠的数据保障是技术与运维部门的职责所在:如果我们的数据库只有一台服务器,那么很容易产生单点故障的问题,比如这台服务器访问压力过大而没 ...