【性能测试】基本入门(包含mysql锁)
性能测试要防止走入缓存中,因为一旦走入缓存中,就是进入了理想的情况。
数据库原理可以读这篇文章
http://blog.jobbole.com/100349/
mysql innodb 的select … for update语句
如果另一个事务修改了某一行,但是没有提交,然后使用select … for update进行查询,这时会阻塞掉,直到获取锁超时
(1205, 'Lock wait timeout exceeded; try restarting transaction')
查看mysql innodb锁超时时间,查看当前锁的情况
show status like 'innodb_row_lock%';
+-------------------------------+----------+
| Variable_name | Value |
+-------------------------------+----------+
| Innodb_row_lock_current_waits | 1 |
| Innodb_row_lock_time | 15406545 |
| Innodb_row_lock_time_avg | 49 |
| Innodb_row_lock_time_max | 51229 |
| Innodb_row_lock_waits | 314101 |
+-------------------------------+----------+
- 要验证能够承载的用户数量
- 要确定没有并发问题
通过Http作为性能测试的入口
Dubbo作为性能测试的入口
通过api直连的方式调用dubbo
客户端设置300个线程的时候
Caused by: com.alibaba.dubbo.remoting.RemotingException: Server side(192.168.10.108,31806) threadpool is exhausted ,detail msg:Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-192.168.10.108:31806, Pool Size: 200 (active: 199, core: 200, max: 200, largest: 200), Task: 746213 (completed: 746013), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://192.168.10.108:31806!
重要的指标
响应时间
mysql
mybatis有没有缓存
show processlist命令(如果您有SUPER权限,您可以看到所有线程。否则,您只能看到您自己的线程)
dubbo有没有缓存?
抽奖活动
流程:
1.查看用户抽奖次数
2. 用户抽奖次数减少一次
3. 抽取奖品,写入奖品记录表
4. 减少奖品库存
5. 告知用户抽奖结果
剩余抽奖次数surplus_amount
我们要验证并发情况下,每次抽奖 surplus_amount 都会减少
数据库引擎类型innodb
show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+--------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+--------+------------+
| TokuDB | YES | Tokutek TokuDB Storage Engine with Fractal Tree(tm) Technology | YES | YES | YES |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| SPHINX | YES | Sphinx storage engine 2.3.2-dev | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| FEDERATED | NO | Federated MySQL storage engine | <null> | <null> | <null> |
+--------------------+---------+----------------------------------------------------------------+--------------+--------+------------+
查看事务隔离级别:
SELECT @@tx_isolation
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
修改数据库的核心语句
<update id="updateAmount">
update lottery_chance set surplus_amount=surplus_amount-1 where user_id=#{userId,jdbcType=BIGINT} and activity_id=#{activityId,jdbcType=BIGINT} and surplus_amount>0
</update>
测试方法
创建100个线程,每个线程里面调用100次,一共10000次调用, 查看surplus_amount字段
原理是什么?
假设A,B两个事务,在A update的过程中,会加上排他锁,B 要update要获取排他锁,从而保证了不会出现并发问题。
update lottery_chance set surplus_amount=surplus_amount-1 语句会使用排他锁
锁
悲观锁
原理是:
如果一个事务需要一条数据
它就把数据锁住
如果另一个事务也需要这条数据
它就必须要等第一个事务释放这条数据
这个锁叫排他锁。
但是对一个仅仅读取数据的事务使用排他锁非常昂贵,因为这会迫使其它只需要读取相同数据的事务等待。因此就有了另一种锁,共享锁。
共享锁是这样的:
如果一个事务只需要读取数据A
它会给数据A加上『共享锁』并读取
如果第二个事务也需要仅仅读取数据A
它会给数据A加上『共享锁』并读取
如果第三个事务需要修改数据A
它会给数据A加上『排他锁』,但是必须等待另外两个事务释放它们的共享锁。
同样的,如果一块数据被加上排他锁,一个只需要读取该数据的事务必须等待排他锁释放才能给该数据加上共享锁。
mysql加的锁在commit后才会释放
比如开两个窗口,起两个事务,一个update, 另一个update会被block
某种情况下出现的死锁
Exception in thread "Thread-2" java.lang.RuntimeException: org.springframework.dao.DeadlockLoserDataAccessException:
### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: update lottery_chance set surplus_amount=surplus_amount-1 where user_id=? and activity_id=? and surplus_amount>0
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
; SQL []; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
org.springframework.dao.DeadlockLoserDataAccessException:
### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: update lottery_chance set surplus_amount=surplus_amount-1 where user_id=? and activity_id=? and surplus_amount>0
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
; SQL []; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:263)
并发情况插入两条数据
先查询,不存在的时候插入数据,这个在并发情况下遇到的问题如下:
下面的代码在高并发情况下,数据库里面可能有两条一样的数据
LotteryChanceEntity entity = lotteryChanceDao.selectByUserId(userId);
if (entity == null) {
entity = new LotteryChanceEntity();
entity.setUserId(userId);
lotteryChanceDao.insert(entity);
}
伪代码
if(oderid != null){
//该记录已存在
update();
}else{
//写入记录
insert();
}
解决方法:
数据库层面添加唯一索引约束
代码层面 :单机可以使用synchronized锁, 分布式可以使用redis setnx锁
插入两条数据可能导致下面的问题
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
InnoDB行锁:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!