并发测试命令:安装apache
ab -c 1000 -n 100 http://www.baidu.com/接口地址
请求1000次、每次有100个人同时请求 http://www.baidu.com/api
常规思路(优化&不推荐)
// 业务逻辑:
// 用户执行下单操作
// mysql 查询商品库存
// 判断物品库存是否充足
// 优化1:库存字段设置 unsigned (无符号)即不能是负数
假设目前商品库存100件、同时有1w个人下单。
虽然商品库存为0了,但是实际生成的订单远远超过规定库存
// 优化2:使用事务 (性能 对比缓存来说性能较低 实现起来较为繁琐,需要自己考虑锁超时,加事务)
Db::startTrans();
... lock(true)...
Db::commit();
Db::rollback();
// 优化3:文件排它锁 fopen、flock(EX | NB)、fclose
// 优化4:redis队列 原子性
// 优化5:redis 分布式锁 setnx 同一个键值指针生效一次 判断设置内容是否失效 时间戳
// 优化6:redis 悲观锁 watch、multi、incr、exec
// 优化7:think-queue队列 redis队列一个一个执行
隔离级别
ACID(原子性、一致性、隔离性、持久性)
并发一致性:(激活码)
丢失更新:A、B同时修改数据,A先,B后,A提交后B提交,B操作覆盖了A的操作,导致A丢失更新。
读脏数据:A修改数据,B读取数据;随后A撤销操作,则B读到脏数据。
不可重复读:B读取数据,A修改数据,B再次读取数据,发现数据和第一次读时不一致。
幻读:A读取了某个范围的数据,B在此范围内插入一条数据;A再次读取,结果不一样。
脏读 (回滚)
男女朋友分手了,舍不得复合了 备胎咋又有男朋友了呢
不可重复读(变化)
男女朋友闹矛盾了,男花了银行卡钱,女没告诉男的用了他银行卡钱,男的我钱被银行黑了
幻读(多了)
男女朋友度蜜月,男花了银行卡钱,父母给男的银行卡打钱了。钱怎么变多了。
并发环境下,为解决并发一致性问题保证事务的隔离性,可采取封锁机制。
行锁:事务A操作数据时,只封锁被操作的行,事务B可以操作其他行的数据,并发程度高;
表锁:事务A操作数据时,封锁整个表,事务B要等A完成才能操作,并发度较低。
读写方面数据库锁也分为读锁(共享锁)和写锁(排他锁)。
读锁:若事务A加了此锁,A可以对数据进行读取操作,但不能更新;其它事务也可以读,但不能修改;
写锁:若事务A加了此锁,A可以对数据进行读和写操作,其它事务不能读写,否则会阻塞。
悲观锁,MySQL中InnoDB也提供了乐观锁的实现——MVCC(多版本并发控制)。用通俗的方式解释悲观锁和乐观锁大概是这样:
悲观锁:认为每次操作都会修改数据,每次都在操作前上锁;
乐观锁:认为每次操作都不会修改数据,不上锁,但是会记录一个版本号或者时间戳,用来对比。
未提交读:事务修改数据,即使未提交,其它事务依旧可见。
已提交读:事务修改数据提交之前,其他事务不可见。
可重复读:事务多次读取数据的结果都一样。
可序列化:解决了幻读问题。
存储引擎
一般在读操作比较多的情况下,MyISAM的效率更高,因为相比于InnoDB,它维护的东西要少,比如版本号,索引数据等。
但是InnoDB支持事务,而且在并发环境下优势显著。至于如何选择存储引擎,应根据具体情况而定。