企业高并发的成熟解决方案(二) ----数据库高并发解决方案

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<User> 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的配置信息如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
	http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
	default-lazy-init="true">

	<!-- 使用Annotation自动注册Bean,解决事物失效问题:在主容器中不扫描@Controller注解,在SpringMvc中只扫描@Controller注解。  -->
	<context:component-scan base-package="com"><!-- base-package 如果多个,用“,”分隔 -->
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
    <context:annotation-config/>
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	   <property name="location">
	     <value>classpath:/jdbc.properties</value>
	   </property>
	</bean>
	
	<!-- 配置数据源开始 -->
    <bean id="dataSources" class="com.caland.sun.client.datasources.DefaultDataSourceService">
		<property name="dataSourceDescriptors">
			<set>
				<bean class="com.caland.sun.client.datasources.DataSourceDescriptor">
					<property name="identity" value="partition1"/>
					<property name="targetDataSource" ref="dataSource1"/>
					<property name="targetDetectorDataSource" ref="dataSource1"/>
					<property name="standbyDataSource" ref="dataSource4"/>
					<property name="standbyDetectorDataSource" ref="dataSource4"/>
				</bean>
				<bean class="com.caland.sun.client.datasources.DataSourceDescriptor">
					<property name="identity" value="partition2"/>
					<property name="targetDataSource" ref="dataSource2"/>
					<property name="targetDetectorDataSource" ref="dataSource2"/>
					<property name="standbyDataSource" ref="dataSource5"/>
					<property name="standbyDetectorDataSource" ref="dataSource5"/>
				</bean>
				<bean class="com.caland.sun.client.datasources.DataSourceDescriptor">
					<property name="identity" value="partition3"/>
					<property name="targetDataSource" ref="dataSource3"/>
					<property name="targetDetectorDataSource" ref="dataSource3"/>
					<property name="standbyDataSource" ref="dataSource6"/>
					<property name="standbyDetectorDataSource" ref="dataSource6"/>
				</bean>
			</set>
		</property>
		<property name="haDataSourceCreator">
			<bean class="com.caland.sun.client.datasources.ha.FailoverHotSwapDataSourceCreator">
				<property name="detectingSql" value="update caland set timeflag=CURRENT_TIMESTAMP()"/>
			</bean>
		</property>
	</bean>
	
	<!-- 数据源1 -->
	<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc1.url}" />
		<property name="user" value="${jdbc1.username}" />
		<property name="password" value="${jdbc1.password}" />
		<property name="autoCommitOnClose" value="true"/>
<!--		<property name="checkoutTimeout" value="${cpool.checkoutTimeout}"/>-->
		<property name="initialPoolSize" value="${cpool.minPoolSize}"/>
		<property name="minPoolSize" value="${cpool.minPoolSize}"/>
		<property name="maxPoolSize" value="${cpool.maxPoolSize}"/>
		<property name="maxIdleTime" value="${cpool.maxIdleTime}"/>
		<property name="acquireIncrement" value="${cpool.acquireIncrement}"/>
		<property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}"/>
	</bean>
	
	<!-- 数据源2 -->
		<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc2.url}" />
		<property name="user" value="${jdbc2.username}" />
		<property name="password" value="${jdbc2.password}" />
		<property name="autoCommitOnClose" value="true"/>
<!--		<property name="checkoutTimeout" value="${cpool.checkoutTimeout}"/>-->
		<property name="initialPoolSize" value="${cpool.minPoolSize}"/>
		<property name="minPoolSize" value="${cpool.minPoolSize}"/>
		<property name="maxPoolSize" value="${cpool.maxPoolSize}"/>
		<property name="maxIdleTime" value="${cpool.maxIdleTime}"/>
		<property name="acquireIncrement" value="${cpool.acquireIncrement}"/>
		<property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}"/>
	</bean>
	
	<!-- 数据源3 -->
	<bean id="dataSource3" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc3.url}" />
		<property name="user" value="${jdbc3.username}" />
		<property name="password" value="${jdbc3.password}" />
		<property name="autoCommitOnClose" value="true"/>
<!--		<property name="checkoutTimeout" value="${cpool.checkoutTimeout}"/>-->
		<property name="initialPoolSize" value="${cpool.minPoolSize}"/>
		<property name="minPoolSize" value="${cpool.minPoolSize}"/>
		<property name="maxPoolSize" value="${cpool.maxPoolSize}"/>
		<property name="maxIdleTime" value="${cpool.maxIdleTime}"/>
		<property name="acquireIncrement" value="${cpool.acquireIncrement}"/>
		<property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}"/>
	</bean>
	<!-- 数据源4 -->
	<bean id="dataSource4" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc4.url}" />
		<property name="user" value="${jdbc4.username}" />
		<property name="password" value="${jdbc4.password}" />
		<property name="autoCommitOnClose" value="true"/>
<!--		<property name="checkoutTimeout" value="${cpool.checkoutTimeout}"/>-->
		<property name="initialPoolSize" value="${cpool.minPoolSize}"/>
		<property name="minPoolSize" value="${cpool.minPoolSize}"/>
		<property name="maxPoolSize" value="${cpool.maxPoolSize}"/>
		<property name="maxIdleTime" value="${cpool.maxIdleTime}"/>
		<property name="acquireIncrement" value="${cpool.acquireIncrement}"/>
		<property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}"/>
	</bean>
	<!-- 数据源5 -->
	<bean id="dataSource5" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc5.url}" />
		<property name="user" value="${jdbc5.username}" />
		<property name="password" value="${jdbc5.password}" />
		<property name="autoCommitOnClose" value="true"/>
<!--		<property name="checkoutTimeout" value="${cpool.checkoutTimeout}"/>-->
		<property name="initialPoolSize" value="${cpool.minPoolSize}"/>
		<property name="minPoolSize" value="${cpool.minPoolSize}"/>
		<property name="maxPoolSize" value="${cpool.maxPoolSize}"/>
		<property name="maxIdleTime" value="${cpool.maxIdleTime}"/>
		<property name="acquireIncrement" value="${cpool.acquireIncrement}"/>
		<property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}"/>
	</bean>
	<!-- 数据源6 -->
	<bean id="dataSource6" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc6.url}" />
		<property name="user" value="${jdbc6.username}" />
		<property name="password" value="${jdbc6.password}" />
		<property name="autoCommitOnClose" value="true"/>
<!--		<property name="checkoutTimeout" value="${cpool.checkoutTimeout}"/>-->
		<property name="initialPoolSize" value="${cpool.minPoolSize}"/>
		<property name="minPoolSize" value="${cpool.minPoolSize}"/>
		<property name="maxPoolSize" value="${cpool.maxPoolSize}"/>
		<property name="maxIdleTime" value="${cpool.maxIdleTime}"/>
		<property name="acquireIncrement" value="${cpool.acquireIncrement}"/>
		<property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}"/>
	</bean>
<!-- 配置数据源结束 -->	
    
<!-- 配置路由规则开始 -->
    <bean id="hashFunction" class="com.caland.core.dao.router.HashFunction"/>
	<bean id="internalRouter"
		class="com.caland.sun.client.router.config.InteralRouterXmlFactoryBean">
		<!-- functionsMap是在使用自定义路由规则函数的时候使用 -->
		<property name="functionsMap">
			<map>
				<entry key="hash" value-ref="hashFunction"></entry>
			</map>
	    </property>
		<property name="configLocations">
			<list>
				<value>classpath:/dbRule/sharding-rules-on-namespace.xml</value>
			</list>
		</property>
	</bean>
<!-- 配置路由规则结束 -->  
  
  	<!-- 事务配置 -->
    <bean id="transactionManager" class="com.caland.sun.client.transaction.MultipleDataSourcesTransactionManager">
        <property name="dataSourceService" ref="dataSources"/>
        <property name="transactionSynchronization" value="2"/>
    </bean>
    
    <!-- 使用annotation定义事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <!--  iBatis SQL map定义。                                                    -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    	<!-- 这里配置的dataSource0为默认的数据源,如果找不到数据库的话则到该数据源中查找 -->
   	 	<property name="dataSource" ref="dataSource1" />  
        <property name="configLocation">
            <value>classpath:/sqlmap-config.xml</value>
        </property>
    </bean>
    
    <!-- 工程里一定要使用此工程模板,不能再使用ibatis原生的api,不然有的情况会不经过的过滤 -->
 	<bean id="sqlMapClientTemplate" class="com.caland.sun.client.SunSqlMapClientTemplate">
		<property name="sqlMapClient" ref="sqlMapClient" />
		<property name="dataSourceService" ref="dataSources" />
		<property name="router" ref="internalRouter" />
		<property name="sqlAuditor">
			<bean class="com.caland.sun.client.audit.SimpleSqlAuditor" />
		</property>
		<property name="profileLongTimeRunningSql" value="true" />
		<property name="longTimeRunningSqlIntervalThreshold" value="3600000" />
	</bean>
</beans>

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

<rules>
	<rule>
		<namespace>Order</namespace>
		<!-- 
			   表达式如果不使用自定义路由规则函数,而是直接使用   taobaoId%2==0这种的话就不用在文件
			   中配置<property name="functionsMap">中了
		-->
		<shardingExpression>hash.applyOrder(userId) == 1</shardingExpression>
		<shards>partition1</shards>
	</rule>
	<rule>
		<namespace>Order</namespace>
		<shardingExpression>hash.applyOrder(userId) == 2</shardingExpression>
		<shards>partition2</shards>
	</rule>
	<rule>
		<namespace>Order</namespace>
		<shardingExpression>hash.applyOrder(userId) == 3</shardingExpression>
		<shards>partition3</shards>
	</rule>
	<rule>
		<namespace>User</namespace>
		<!-- 
			   表达式如果不使用自定义路由规则函数,而是直接使用   taobaoId%2==0这种的话就不用在文件
			   中配置<property name="functionsMap">中了
		-->
		<shardingExpression>hash.applyUser(username) == 1</shardingExpression>
		<shards>partition1</shards>
	</rule>
	<rule>
		<namespace>User</namespace>
		<shardingExpression>hash.applyUser(username) == 2</shardingExpression>
		<shards>partition2</shards>
	</rule>
	<rule>
		<namespace>User</namespace>
		<shardingExpression>hash.applyUser(username) == 3</shardingExpression>
		<shards>partition3</shards>
	</rule>
</rules>

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
高并发下插入数据前进行验证是否存在,可以采用数据库事务与唯一性约束(Unique Constraint)相结合的方案。 具体实现步骤如下: 1. 在数据库中创建唯一性约束,这样可以保证数据库中某个字段的值不重复。 2. 在 C# 中使用事务进行操作,事务可以保证多个操作要么全部成功,要么全部失败。 3. 在事务中进行数据查询操作,如果查询结果为空,则说明该数据不存在,可以进行插入操作;如果查询结果不为空,则说明该数据已经存在,不进行插入操作。 以下是一个示例代码: ```csharp using System.Data.SqlClient; using System.Transactions; public class DataAccess { private const string ConnectionString = "your_connection_string_here"; public bool InsertData(string data) { bool result = false; using (TransactionScope scope = new TransactionScope()) { try { using (SqlConnection connection = new SqlConnection(ConnectionString)) { connection.Open(); // 查询数据是否存在 string query = "SELECT COUNT(*) FROM YourTable WHERE Data = @Data"; SqlCommand command = new SqlCommand(query, connection); command.Parameters.AddWithValue("@Data", data); int count = (int)command.ExecuteScalar(); if (count == 0) { // 数据不存在,进行插入操作 string insert = "INSERT INTO YourTable(Data) VALUES(@Data)"; SqlCommand insertCommand = new SqlCommand(insert, connection); insertCommand.Parameters.AddWithValue("@Data", data); insertCommand.ExecuteNonQuery(); result = true; } else { // 数据已经存在,不进行插入操作 result = false; } } scope.Complete(); } catch (Exception ex) { // 处理异常 result = false; } } return result; } } ``` 以上代码中使用了事务(TransactionScope)来保证操作的原子性,使用了数据库查询语句(SELECT)来查询数据是否存在,使用了数据库插入语句(INSERT)来插入数据。同时,使用了参数化查询(Parameters.AddWithValue)来防止 SQL 注入攻击。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值