import java.math.BigDecimal; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.Map.Entry; import java.util.Random; import java.util.TreeMap;
import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.impl.NutDao; import org.nutz.trans.Atom; import org.nutz.trans.Trans;
import cn.gbase.jiangsu.data.transfer.bean.ShopUser;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class TestUpdate {
NutDao daoOut = new NutDao(); MysqlDataSource dsOut = null; TreeMap<Long,String> msgMap = new TreeMap<Long,String>();
private void setOutDataSource() { dsOut = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
// 更新连接信息 dsOut.setServerName("localhost"); dsOut.setDatabaseName("shop"); dsOut.setUser("root"); dsOut.setPassword("123456");
// 设置数据源 daoOut.setDataSource(dsOut); }
private void doWork() throws SQLException, InterruptedException { setOutDataSource();
// 初始为1 daoOut.update(ShopUser.class, Chain.make("accountBalance", 0), Cnd.where("id", "=", 6));
msgMap.clear(); // 100个线程加1 for (int i = 0; i < 10; i++) { new Thread() { public void run() { updateMoney(); } }.start(); } Thread.sleep(10000); // ShopUser u = daoOut.fetch(ShopUser.class, 6); // System.out.println(u.getAccountBalance()); StringBuilder sb =new StringBuilder(); for(Entry<Long,String> entry:msgMap.entrySet()){ sb.append(entry.getKey()); sb.append(":").append(entry.getValue()).append("\n"); } System.out.println(sb.toString());
}
private void updateMoney() {
// 随机延迟 try { Thread.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } // 有事务的话,多个sql执行使用的是同一个connection // 无事务的话,不论select还是update,每次都是新开的一个connection Trans.exec(Connection.TRANSACTION_READ_UNCOMMITTED, new Atom() { public void run() { // 更新 daoOut.update(ShopUser.class, Chain.make("accountBalance", BigDecimal.ONE), Cnd.where("id", "=", 6));
// 随机延迟 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } // 读取 ShopUser u = daoOut.fetch(ShopUser.class, 6); BigDecimal money = u.getAccountBalance(); echo(""+money,System.nanoTime()); } });
}
private synchronized void echo(String msg, long time){ msgMap.put(time, msg); }
public static void main(String[] args) { TestUpdate xx = new TestUpdate(); try { try { xx.doWork(); } catch (InterruptedException e) { } } catch (SQLException e) { e.printStackTrace(); } }
}
想了两个小时才想通~~~
因为一开始执行,控制台就把10个update语句打出来了。 啊?10个更新都执行了? 想了N久也想不通~~ update,铁定上锁,怎么会同时执行呢? ………… 后来,把update后的等待时间改为10秒,终于暴露出来了: 很多Lock wait timeout exceeded,一看行号,就是执行update那行~~
瞬间想通,虽然控制台把update语句打出来了,但不代表它执行完了~~ 这些update语句都在那等锁呢。
第一个update语句,上锁(事务里貌似是上的共享锁,即其它事务能查,不能更新), 然后等10秒,查询,commit事务,解锁 然后轮到第二个update语句~
在mysql里用: show status like 'innodb_row_lock%'; 可以看到Innodb_row_lock_current_waits 随着select语句执行,这个waits数就会减一,而之前的10秒不会变, 充分说明了,事务结束才会解锁。
--------------------------- update和select之间,被其它事务update的可能性是0,因为update上锁了,事务结束才会解锁。其它事务只能查询
也就保证了,select到的值,是之前update的值。
|