怎么用存储过程来提高事务的并发

在描述使用存储过程解决事务并发之前,简单描述下上下问。  我做了一个简单积分的对外接口,因为需要做几个表的查询和创建更改,在调用特别频繁的时候,会发现小范围阻塞的情况,作为同步的接口,这种时间消耗也难让人接受…  

事务是做什么的?  他可以保证你的一系列dml语句要么都做,要么都别做.   那么存储过程又是干嘛的? 

存储过程可以说是一个服务,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),代码块就相当于函数,既然是函数了,那么你就可以调用它。


存储过程的好处:


1.由于数据库执行动作时,是先编译后执行的。然而存储过程是一个编译过的代码块,所以执行效率要比T-SQL语句高, 毕竟不用每次都实时编译了.

2.存储过程可以介绍大量的网络io的消耗,因为存储过程是可编程的,所以他可以替代大堆的T-SQL语句,当然也就降低网络的通信量,提高通信速率。

很多的电商平台,当然除了那种淘宝,京东这种典型的大流量平台之外,好多公司的解决方案都是使用存储过程来解决事务上的并发。 因为涉及到钱和积分这类的东西,一定要使用事务的。 一般的逻辑是这样,来了一个用户做消费的请求,那么我会在状态表里查看他账号是否有问题,然后扣除积分,商品的数目减一,最后加入他的订单信息,最后返回给用户。 这个简单的消费过程,他访问mysql的次数达到4次。 

为了保证订单的数据安全性,我们会加事务锁。  那么,同时又来了几个用户来消费,那他们只能等着事务的完毕了。   如果我们用存储过程是解决事务,那性能要好很多,我只需要调用已经创建好的存储过程,就解决了所有的事情。  一次网络io,全部解决….


下面是mysql事务锁的级别,这个是必须要懂得.

#xiaorui.cc 
1. READ UNCOMMITTED:最低级别的隔离,通常又称为dirty read,它允许一个事务读取还没commit的数据,这样可能会提高性能,但是dirty read可能不是我们想要的  
2. READ COMMITTED:在一个事务中只允许已经commit的记录可见,如果session中select还在查询中,另一session此时insert一条记录,则新添加的数据不可见  
3. REPEATABLE READ:在一个事务开始后,其他session对数据库的修改在本事务中不可见,直到本事务commit或rollback。在一个事务中重复select的结果一样,除非本事务中update数据库。
4. SERIALIZABLE:最高级别的隔离,只允许事务串行执行。为了达到此目的,数据库会锁住每行已经读取的记录,其他session不能修改数据直到前一事务结束,事务commit或取消时才释放锁。

另外:
Mysql的默认隔离级别是:REPEATABLE READ  ,   READ UNCOMMITTED级别会导致数据完整性的严重问题,需要自己控制如何保持数据完整性.
SERIALIZABLE会导致性能问题并增加死锁的机率,  所以尽量少用. 

该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新.

http://xiaorui.cc/?p=3545

定义事务的方法
MySQL默认的行为是在每条SQL语句执行后执行一个COMMIT语句,从而有效的将每条语句独立为一个事务.  

那么事务怎么打开? 存储过程的事务使用方法跟日常用法没区别.  
1,  设置MySQL的autocommit属性为0,默认为1 ,  注意你的python客户端autocommit配置.
2,使用START TRANSACTION语句显式的打开一个事务
上面已经说了,当使用START TRANSACTION开始一个事物的时候,则SET autocommit=0不会起作用,因为START TRANSACTION会隐式的提交session中所有当前的更改,结束已有的事务,并打开一个新的事务。

我就不把自己业务上的关于事务的存储过程给贴出来了,涉及到好几个表,几个状态的判断,及返回值。  这里贴一个简单的存储过程事务, 有兴趣深入的朋友可以自己看看mysql的存储过程语法,他是兼容变量赋值,if  else while  for 的用法… 

#xiaorui.cc

CREATE PROCEDURE xiaorui  
    (from_account int, to_account int, tfer_amount numeric(10,2))  
BEGIN  
    SET autocommit=0;  
  
    UPDATE nima SET balance=balance-tfer_amount WHERE account_id=from_account;  
  
    UPDATE nima SET balance=balance+tfer_amount WHERE account_id=to_account;  
  
    COMMIT;  
END;  

在mysql client调用的方法很简单.

CALL xiaorui('11','222'); 


如果你是使用python的peewee库,那么只需这么调用就可以了。

cursor = db.cursor()
cursor.callproc(...)

如果是python的mysqldb ,那么….

cur.execute('call func_name(%s,%s)',(111,222))

END.


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

4 Responses

  1. 450962 2016年11月17日 / 下午3:35

    如果存储过程中很多逻辑判断,mysql很多资源都在做计算,这样存储过程真的高效么

  2. 依云 2016年9月20日 / 下午4:23

    看到「 帮忙点击广告」这句,我关闭了广告屏蔽扩展,然后发现不是 Google 家的广告。(本来我想举报来着

  3. 卓修武 2016年8月8日 / 下午10:49

    django 中的 select_for_update 对应serializerble吗

发表评论

邮箱地址不会被公开。 必填项已用*标注