有个题不会,求教SQL SERVER数据并发,使用事务+存储过程

.Net技术 码拜 9年前 (2015-05-10) 843次浏览 0个评论
 

使用事务+存储过程啥意思啊,怎么就解决了并发呢?看不懂啊。
怎么还跟存储过程扯上关系了?完全看不明白,题目就是数据并发如何解决。
答案就是事务+存储过程。
然后我就彻底迷茫了。

满分求教,有没有明白人给讲讲啊。

事务就是相当于给数据库加锁,并且使用日志以便回滚。因为事务的独占加锁特性,所以可以解决并发的冲突问题。但是要慎用,因为对性能造成影响。
存储过程是预先编译好的查询,所以理论上要比调用sql快一些。
高并发主要是通过读写分离、分库分表实现。
80分
并发都是跟锁有关的,不管是程序锁还是数据库锁
没听过事务+存储过程能解决并发问题,最多也就是解决一部分问题
事务只是解决数据准确性、一致性问题
存储过程只是因为是预解析过,相对于以文本的方式执行的SQL,少了解析的过程而已,最多只能说是提高了一定的数据库访问速度

高并发解决方案也是按实际业务需求来定的,一般通用的方案是负载均衡、动静分离、CDN什么的、数据库优化等等结合起来,增加单位时间内可以承受的最大访问量

引用 1 楼 devmiao 的回复:

事务就是相当于给数据库加锁,并且使用日志以便回滚。因为事务的独占加锁特性,所以可以解决并发的冲突问题。但是要慎用,因为对性能造成影响。
存储过程是预先编译好的查询,所以理论上要比调用sql快一些。
高并发主要是通过读写分离、分库分表实现。

事务是有隔离级别的,你说设置事务的隔离级别可能会解决你所说的问题,但是事务的默认隔离级别是很低的。
我的答案跟2楼类似,结果被人喷了,说我这都不懂,不应该啊,还给了我标准答案事务+存储过程让我回家查查书。
可能是理解的方向不一样吧。
我在瞅瞅有木有更高深的答案。2楼讲的很细。

120分
SQL Server 数据库服务器软件本来就是设计得支持多用户、多客户端会话的并发访问的,因此不论用户写不写存储过程来从客户端应用程序访问数据库,都是支持并发访问的。说存储过程是专门用来解决并发的关键特征之一,这确实真的应该算是扯淡的。

SQL Server默认地——假设你没有显式地在客户端应用中声明事务,也会自动为每一条sql查询语句(包括调用存储过程的语句)启用一个事务。因此事务也是天然地就自动“有了”的,也不是什么煞有介事地非要强调你的程序写了事务才能保证并发可执行的。

关系数据库事务的概念,是为了保证对数据库的更改产生ACID的效果。根据事务隔离级别的不同,对数据修改的“一致性”的保护就不同。例如SQL Server数据库默认地比Oracle数据库的隔离级别低一个级别,所以SQL Server比Oracle更能保证事务操作的一致性,而Oracle比SQL Server的事务处理的速递快那么一点点。但是事物隔离级别实际上可以由你的应用程序自由设置的。

关于“存储过程更快”的问题,基本上属于是一个“看不出来有什么差别”的想当然的问题。如果要你维护300个存储过程,你肯定叫苦不迭。所以由客户端发送sql语句这种方式更加常见,这更加符合开发过程中“随时修改重构sql查询语句”的特点。同时SQL Server可以自动对客户端发送的sql语句得编译结果进行缓存,下一次再执行相同类型(仅仅参数或者常量不同)的sql语句时会自动使用编译结果。 

总的来说,你在面试时遇到了一个比较低级的程序员,没有测试你的编程设计能力,而是因为你跟他的“言语不和”他就打法你了。有这样的人来当面试官公司,幸亏你没有去。

引用 楼主 wjfwd2010 的回复:

完全看不明白,题目就是数据并发如何解决。

我想,要么可能是对方出的题目有特定的描述,例如并发中的ACID如何解决,而你只看到了一个大概、没有看到特定细节描述。

但是这也联系不上存储过程啊?!一般来说,跟普通的数据库编程操作相关的,都扯不上存储过程的问题。使用存储过程是“体验”的区别,而不是必须用或者必须不用的区别。可能有的人以为用了存储过程就能让一个持续3秒钟的查询变成了30毫秒的查询,也许这样理解存储过程的人就会处处不忘把它抬出来说一下。而实际上,你可以测试一下,如果一个sql经常使用,SQL Server会自动缓存sql编译结果,因此用与不用存储过程基本上察觉不出什么区别。反而倒是在工程上“哪一种方式更加提高开发效率”是重点要顾及的。

所以跟应聘者花时间在这里而没有更深入一点的交流,对于求贤者来说,实在是他自己的损失。

使用事务同时提交多个数据表示例
 private void Frm_Main_Load(object sender, EventArgs e)
        {
            //创建数据库连接对象
            SqlConnection sqlConn = new SqlConnection(“Data Source=WIN-GI7E47AND9R\LS;Database=db_TomeTwo;uid=sa;pwd =;”);
            List<String> strSqls = new List<string>();//创建集合对象
            String strDelete1 = “delete From tb_Author Where AuthorId = “”99″””;//定义删除第一个表的SQL语句
            strSqls.Add(strDelete1);//将SQL语句添加到集合中
            String strDelete2 = “delete From tb_AuthorsBook Where AuthorId = “”99″””;//定义删除第二个表的SQL语句
            strSqls.Add(strDelete2);//将SQL语句添加到集合中
            string strInsert1 = “insert into tb_Author values(“”99″”,””zhd””)”;//定义添加第一个表的SQL语句
            strSqls.Add(strInsert1);//将SQL语句添加到集合中
            string strInsert2 = “insert into tb_AuthorsBook values(“”66″”,””C#范例大全””,””99″”)”;//定义添加第二个表的SQL语句
            strSqls.Add(strInsert2);//将SQL语句添加到集合中
            if (ExecDataBySqls(strSqls, sqlConn))//如果执行成功
            {
                MessageBox.Show(“提交tb_Author数据表成功!”, “信息提示”);
                MessageBox.Show(“提交tb_AuthorsBook数据表成功!”,”信息提示”);
            }
            else
            {
                MessageBox.Show(“提交tb_Author数据表失败!”, “信息提示”);
                MessageBox.Show(“提交tb_AuthorsBook数据表失败!”, “信息提示”);
            }
            SqlDataAdapter sqlda1 = new SqlDataAdapter(“select * from tb_Author”, sqlConn);//创建数据桥接器对象
            SqlDataAdapter sqlda2 = new SqlDataAdapter(“select * from tb_AuthorsBook”, sqlConn);//创建数据桥接器对象
            DataSet myds = new DataSet();//创建数据集对象
            sqlda1.Fill(myds, “tb_Author”);//填充数据集
            sqlda2.Fill(myds, “tb_AuthorsBook”);//填充数据集
            dataGridView1.DataSource = myds.Tables[“tb_Author”];//对第一个DataGridView进行数据绑定
            dataGridView2.DataSource = myds.Tables[“tb_AuthorsBook”];//对第二个DataGridView进行数据绑定
        }//codego.net/

        /// <param name=”strSqls”>使用List泛型封装多条SQL语句</param>
        /// <param name=”sqlConn”>数据库连接</param>
        public bool ExecDataBySqls(List<string> strSqls, SqlConnection sqlConn)
        {
            bool booIsSucceed = false;//声明提交数据是否成功的标记
            SqlCommand sqlCmd = new SqlCommand();//创建SqlCommand对象
            sqlCmd.Connection = sqlConn;//设置SqlCommand对象的Connection属性
            if (sqlConn.State == ConnectionState.Closed)
            {
                sqlConn.Open();//打开数据库连接
            }
            SqlTransaction sqlTran = sqlConn.BeginTransaction();//开始一个事务
            try
            {
                sqlCmd.Transaction = sqlTran;//设置SqlCommand对象的Transaction属性
                foreach (string item in strSqls)
                {
                    sqlCmd.CommandType = CommandType.Text;//设置命令类型为SQL文本命令
                    sqlCmd.CommandText = item;//设置要对数据源执行的SQL语句
                    sqlCmd.ExecuteNonQuery();//执行SQL语句并返回受影响的行数
                }
                sqlTran.Commit();//提交事务,持久化数据
                booIsSucceed = true;//表示提交数据库成功
            }
            catch
            {
                sqlTran.Rollback();//回滚事务,恢复数据
                booIsSucceed = false;//表示提交数据库失败!
            }
            finally
            {
                sqlConn.Close();//关闭连接
                strSqls.Clear();//清除列表strSqls中的元素
            }
            return booIsSucceed;//方法返回值
        }
  二、在存储过程中使用事务示例
–判断proc_TransInProc存储过程是否存在,如果存在将它删除
if exists(select name from sysobjects 
where name=””proc_TransInProc””and type=””p””)
  drop proc proc_TransInProc  –删除存储过程
GO
create procedure proc_TransInProc
as
declare @truc int
select @truc=@@trancount
if @truc=0
begin tran p1
else
save tran pl
if (@truc=2)
begin
rollback tran pl
return 25
end
if(@truc=0)
commit tran pl
return 0  

引用 4 楼 sp1234 的回复:

SQL Server 数据库服务器软件本来就是设计得支持多用户、多客户端会话的并发访问的,因此不论用户写不写存储过程来从客户端应用程序访问数据库,都是支持并发访问的。说存储过程是专门用来解决并发的关键特征之一,这确实真的应该算是扯淡的。

SQL Server默认地——假设你没有显式地在客户端应用中声明事务,也会自动为每一条sql查询语句(包括调用存储过程的语句)启用一个事务。因此事务也是天然地就自动“有了”的,也不是什么煞有介事地非要强调你的程序写了事务才能保证并发可执行的。

关系数据库事务的概念,是为了保证对数据库的更改产生ACID的效果。根据事务隔离级别的不同,对数据修改的“一致性”的保护就不同。例如SQL Server数据库默认地比Oracle数据库的隔离级别低一个级别,所以SQL Server比Oracle更能保证事务操作的一致性,而Oracle比SQL Server的事务处理的速递快那么一点点。但是事物隔离级别实际上可以由你的应用程序自由设置的。

关于“存储过程更快”的问题,基本上属于是一个“看不出来有什么差别”的想当然的问题。如果要你维护300个存储过程,你肯定叫苦不迭。所以由客户端发送sql语句这种方式更加常见,这更加符合开发过程中“随时修改重构sql查询语句”的特点。同时SQL Server可以自动对客户端发送的sql语句得编译结果进行缓存,下一次再执行相同类型(仅仅参数或者常量不同)的sql语句时会自动使用编译结果。 

查询不加with(nolock)脏读否则会受锁影响而等待
update,delete,insert执行过程中必定会有锁

sql语句会被缓存,所以要尽可能的通过参数化执行SQL语句,一方面是为了防止SQL注入,另一方面就是为了让SQL能被缓存(貌似一个sql要被缓存,一个字都不能差,哪怕两种查询是同一种效果),这个有听过,但一直不是很确定,现在终于确定了

sql不是自带锁 还需要自己写代码?
意思基本上就是在存储过程内写事务。
比如Begin Tran
高并发的情况下,基本上都是这样。存储过程是事先编译的所以速度极快。
开启事务,去处理,就是并发了
引用 5 楼 sp1234 的回复:
Quote: 引用 楼主 wjfwd2010 的回复:

完全看不明白,题目就是数据并发如何解决。

我想,要么可能是对方出的题目有特定的描述,例如并发中的ACID如何解决,而你只看到了一个大概、没有看到特定细节描述。

但是这也联系不上存储过程啊?!一般来说,跟普通的数据库编程操作相关的,都扯不上存储过程的问题。使用存储过程是“体验”的区别,而不是必须用或者必须不用的区别。可能有的人以为用了存储过程就能让一个持续3秒钟的查询变成了30毫秒的查询,也许这样理解存储过程的人就会处处不忘把它抬出来说一下。而实际上,你可以测试一下,如果一个sql经常使用,SQL Server会自动缓存sql编译结果,因此用与不用存储过程基本上察觉不出什么区别。反而倒是在工程上“哪一种方式更加提高开发效率”是重点要顾及的。

所以跟应聘者花时间在这里而没有更深入一点的交流,对于求贤者来说,实在是他自己的损失。

。。。。题目撑死就12个字,我不可能记错,我当时亚口无言。。。

引用 9 楼 diaodiaop 的回复:

sql不是自带锁 还需要自己写代码?

有的时候你要考虑并发读取,所以会加脏读,这个时候你就要考虑插入update等逻辑的加锁机制了。

引用 5 楼 sp1234 的回复:
Quote: 引用 楼主 wjfwd2010 的回复:

完全看不明白,题目就是数据并发如何解决。

我想,要么可能是对方出的题目有特定的描述,例如并发中的ACID如何解决,而你只看到了一个大概、没有看到特定细节描述。

但是这也联系不上存储过程啊?!一般来说,跟普通的数据库编程操作相关的,都扯不上存储过程的问题。使用存储过程是“体验”的区别,而不是必须用或者必须不用的区别。可能有的人以为用了存储过程就能让一个持续3秒钟的查询变成了30毫秒的查询,也许这样理解存储过程的人就会处处不忘把它抬出来说一下。而实际上,你可以测试一下,如果一个sql经常使用,SQL Server会自动缓存sql编译结果,因此用与不用存储过程基本上察觉不出什么区别。反而倒是在工程上“哪一种方式更加提高开发效率”是重点要顾及的。

所以跟应聘者花时间在这里而没有更深入一点的交流,对于求贤者来说,实在是他自己的损失。

非常感谢非常感谢。

这个主要是对大型的程序 来说吧。小型的程序根本就不存在并发,也就更没有数据并发这么一回事。
设想:一个操作(比如销售某件东西) 你要更新几十个表 (比如日报表,周报,旬报,月报,季报,年报等),    //:为什么要更新这么多,当记录很多的时候,你使用Sum之类的去合计,是不现实的。

这个时候使用存储过程是唯一的选择。
如果你使用.net  sqlCmd.Transaction  这个来一条一条更新。那将是要命的。
也许你会向上面的所说。
可能有的人以为用了存储过程就能让一个持续3秒钟的查询变成了30毫秒的查询
可能不会有这么100倍这么大的差距。但有时候一倍的差距也足可以让你需要50台服务完成的事,不用去买100台服务器。

好好看下书吧~
hahaha不知道怎么回事
引用 15 楼 zanfeng 的回复:

这个主要是对大型的程序 来说吧。小型的程序根本就不存在并发,也就更没有数据并发这么一回事。
设想:一个操作(比如销售某件东西) 你要更新几十个表 (比如日报表,周报,旬报,月报,季报,年报等),    //:为什么要更新这么多,当记录很多的时候,你使用Sum之类的去合计,是不现实的。

这个时候使用存储过程是唯一的选择。
如果你使用.net  sqlCmd.Transaction  这个来一条一条更新。那将是要命的。
也许你会向上面的所说。
可能有的人以为用了存储过程就能让一个持续3秒钟的查询变成了30毫秒的查询
可能不会有这么100倍这么大的差距。但有时候一倍的差距也足可以让你需要50台服务完成的事,不用去买100台服务器。

怎么个要命法啊,你要讲解一下底层的工作原理和存储过程对比一下哪里要命了?
什么叫大型程序,小型程序就不存在并发了,这个有点说不过去了吧?您是做后端的么?
你别说
可能不会有这么100倍这么大的差距
这个还真有可能!
请您能详细的讲解一下么?我也是第一次听说存储过程是首选的问题。
很好奇呢。

看下解决方法~
引用 6 楼 kkk_hongyuen 的回复:

使用事务同时提交多个数据表示例
 private void Frm_Main_Load(object sender, EventArgs e)
        {
            //创建数据库连接对象
            SqlConnection sqlConn = new SqlConnection(“Data Source=WIN-GI7E47AND9R\LS;Database=db_TomeTwo;uid=sa;pwd =;”);
            List<String> strSqls = new List<string>();//创建集合对象
            String strDelete1 = “delete From tb_Author Where AuthorId = “”99″””;//定义删除第一个表的SQL语句
            strSqls.Add(strDelete1);//将SQL语句添加到集合中
            String strDelete2 = “delete From tb_AuthorsBook Where AuthorId = “”99″””;//定义删除第二个表的SQL语句
            strSqls.Add(strDelete2);//将SQL语句添加到集合中
            string strInsert1 = “insert into tb_Author values(“”99″”,””zhd””)”;//定义添加第一个表的SQL语句
            strSqls.Add(strInsert1);//将SQL语句添加到集合中
            string strInsert2 = “insert into tb_AuthorsBook values(“”66″”,””C#范例大全””,””99″”)”;//定义添加第二个表的SQL语句
            strSqls.Add(strInsert2);//将SQL语句添加到集合中
            if (ExecDataBySqls(strSqls, sqlConn))//如果执行成功
            {
                MessageBox.Show(“提交tb_Author数据表成功!”, “信息提示”);
                MessageBox.Show(“提交tb_AuthorsBook数据表成功!”,”信息提示”);
            }
            else
            {
                MessageBox.Show(“提交tb_Author数据表失败!”, “信息提示”);
                MessageBox.Show(“提交tb_AuthorsBook数据表失败!”, “信息提示”);
            }
            SqlDataAdapter sqlda1 = new SqlDataAdapter(“select * from tb_Author”, sqlConn);//创建数据桥接器对象
            SqlDataAdapter sqlda2 = new SqlDataAdapter(“select * from tb_AuthorsBook”, sqlConn);//创建数据桥接器对象
            DataSet myds = new DataSet();//创建数据集对象
            sqlda1.Fill(myds, “tb_Author”);//填充数据集
            sqlda2.Fill(myds, “tb_AuthorsBook”);//填充数据集
            dataGridView1.DataSource = myds.Tables[“tb_Author”];//对第一个DataGridView进行数据绑定
            dataGridView2.DataSource = myds.Tables[“tb_AuthorsBook”];//对第二个DataGridView进行数据绑定
        }//codego.net/

        /// <param name=”strSqls”>使用List泛型封装多条SQL语句</param>
        /// <param name=”sqlConn”>数据库连接</param>
        public bool ExecDataBySqls(List<string> strSqls, SqlConnection sqlConn)
        {
            bool booIsSucceed = false;//声明提交数据是否成功的标记
            SqlCommand sqlCmd = new SqlCommand();//创建SqlCommand对象
            sqlCmd.Connection = sqlConn;//设置SqlCommand对象的Connection属性
            if (sqlConn.State == ConnectionState.Closed)
            {
                sqlConn.Open();//打开数据库连接
            }
            SqlTransaction sqlTran = sqlConn.BeginTransaction();//开始一个事务
            try
            {
                sqlCmd.Transaction = sqlTran;//设置SqlCommand对象的Transaction属性
                foreach (string item in strSqls)
                {
                    sqlCmd.CommandType = CommandType.Text;//设置命令类型为SQL文本命令
                    sqlCmd.CommandText = item;//设置要对数据源执行的SQL语句
                    sqlCmd.ExecuteNonQuery();//执行SQL语句并返回受影响的行数
                }
                sqlTran.Commit();//提交事务,持久化数据
                booIsSucceed = true;//表示提交数据库成功
            }
            catch
            {
                sqlTran.Rollback();//回滚事务,恢复数据
                booIsSucceed = false;//表示提交数据库失败!
            }
            finally
            {
                sqlConn.Close();//关闭连接
                strSqls.Clear();//清除列表strSqls中的元素
            }
            return booIsSucceed;//方法返回值
        }
  二、在存储过程中使用事务示例
–判断proc_TransInProc存储过程是否存在,如果存在将它删除
if exists(select name from sysobjects 
where name=””proc_TransInProc””and type=””p””)
  drop proc proc_TransInProc  –删除存储过程
GO
create procedure proc_TransInProc
as
declare @truc int
select @truc=@@trancount
if @truc=0
begin tran p1
else
save tran pl
if (@truc=2)
begin
rollback tran pl
return 25
end
if(@truc=0)
commit tran pl
return 0  

你这种封装的东西根本就不靠谱,逻辑简单还行。

为什么会频繁出现网段被封锁?
数据库自己开发,多好,灵活、高效,不过跨平台时不方便
好吧,实际上我也是来看答案的,这个答案给的也太挫了,事务加存储过程。。。啥书上的东西啊
下载个软件还要积分草了, 软件可以用不
事务要慎用,会阻塞其它用户。
这二者是没有关联的,答案是错的。
并发跟事务或者并发跟存储过程是扯不上因果关系的
打撒火炬大厦可以呢妈的胶带机的按时
关注下
看的自己 好无知啊
小曹越来越不负责了,这种帖子有啥好推荐的。。。
学习了
只有提交事务硬盘IO才动作,所以使用事务处理数据并发时能提高性能,如果不使用事务硬盘IO就要动作一次,当多并发的时候,就会造成性能瓶颈。
为了这个事,我们公司请了专家来解决这个事情,现在我们公司所有的软件都已经采用事务的方式批量上传数据,的确效果明显,以前上传几千数据要几分钟(因为有很多软件一起上传),现在只要30多秒。
学习了
大神讲的好多啊 棒

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明有个题不会,求教SQL SERVER数据并发,使用事务+存储过程
喜欢 (0)
[1034331897@qq.com]
分享 (0)

文章评论已关闭!