在描述使用存储过程解决事务并发之前,简单描述下上下问。 我做了一个简单积分的对外接口,因为需要做几个表的查询和创建更改,在调用特别频繁的时候,会发现小范围阻塞的情况,作为同步的接口,这种时间消耗也难让人接受…
事务是做什么的? 他可以保证你的一系列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会导致性能问题并增加死锁的机率, 所以尽量少用.
该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新.
定义事务的方法
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.
如果存储过程中很多逻辑判断,mysql很多资源都在做计算,这样存储过程真的高效么
看到「 帮忙点击广告」这句,我关闭了广告屏蔽扩展,然后发现不是 Google 家的广告。(本来我想举报来着 )
哈哈,那么你最后点击了么?
django 中的 select_for_update 对应serializerble吗