Introduction to Data Concurrency and Consistency in a Multiuser Environment

The serializable mode of transaction behavior tries to ensure that transactions run in such a way that they appear to be executed one at a time, or serially, rather than concurrently.

  • Dirty reads: A transaction reads data that has been written by another transaction that has not been committed yet
  • Nonrepeatable reads: A transaction re-reads data it has previously read and finds that another committed transaction has modified or deleted the data
  • Phantom reads: A transaction re-runs a query returning a set of rows that satisfies a search condition and finds that another committed transaction has inserted additional rows that satisfy the condition.

Oracle offers the read committed and serializable isolation levels, as well as a read-only mode. Read committed is the default.

How Oracle Manages Data Concurrency and Consistency

Multiversion Concurrency Control

Oracle automatically provides read consistency to a query so that all the data that the query sees comes from a single point in time (statement-level read consistency). Orable can also provide read consistency to all of the queries in a transaction (transaction-level read consistency).

Oracle uses the information maintained in its rollback segments to provide these consistent views. The rollback segments contain the old values of data that have been changed by uncommitted or recently committed transactions.

Statement-Level Read Consistency

Oracle always enforces statement-level read consistency. This gurantees that all the data returned by a single query comes from a single point in time - the time that the query begin.

As query execution proceesds, only data committed before the query began is visible to the query. The query does not see changes committed after statement execution begins.

Transaction-Level Read Consistency

Oracle also offers the option of enforcing transaction-level read consistency. When a transaction runs in serializable mode, all data accesses reflect the state of the database as of the time the transaction began.

This mean that the data seen by all queries within the same transaction is consistent with respect to a single point in time, except that queries mde by a serializable transaction do see changes mode by the transaction itself.

Transactional-level read consistency produces repeatable reads and does not expose a query to phantoms.

Comparison of Read Committed and Seriablizable Isolation

Row-Level Locking

Both read committed and serializable transactions use row-level locking, and both will wait if they try to change a row updated by an uncommitted concurrent transaction. The second transaction that tries to update a given row waits for the other transaction to commit or undo and release its lock. If that other transaction rolls back, the waiting transaction, regardless of its isolation mode, can proceed to change the previously locked row as if the other transaction had not existed.

Referential Integrity

Because Orable does not use read locks in either read-consistent or seriablizable transactions. data read by one transaction can be overwritten by another. Transaction that perform database consistency checks at the application level cannot assume that the data they read will remain unchanged during the execution of the transaction even though such changes are not visible to the transaction.

2016/3/21 posted in  数据库

实验一

  • 两次读写操作
for (int i = 0; i < 2; i++)
{
    try
    {
        Thread.sleep(5000);
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
    List<WechatUser> wechatUserList = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUserList.size());
    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");
    try
    {
        Thread.sleep(1000);
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }

}   

结果:

2016-03-20 22:13:30 [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource]-[DEBUG] Adding transactional method 'WechatRegistServiceImpl.insertUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_SERIALIZABLE; ''
2016-03-20 22:13:30 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Acquired Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
2016-03-20 22:13:30 [org.springframework.jdbc.datasource.DataSourceUtils]-[DEBUG] Changing isolation level of JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] to 8
2016-03-20 22:13:30 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[WARN] satart
2016-03-20 22:13:31 [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[DEBUG] Looking up handler method for path /insert.html
2016-03-20 22:13:31 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Acquired Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
2016-03-20 22:13:35 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Creating a new SqlSession
2016-03-20 22:13:35 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:35 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER
2016-03-20 22:13:35 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:35 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:0 用户数量:0
2016-03-20 22:13:35 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36] from current transaction
2016-03-20 22:13:35 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())
2016-03-20 22:13:35 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:35 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 0 条
2016-03-20 22:13:36 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Creating a new SqlSession
2016-03-20 22:13:36 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@32358420]
2016-03-20 22:13:36 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER
2016-03-20 22:13:41 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36] from current transaction
2016-03-20 22:13:41 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER
2016-03-20 22:13:41 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:41 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:1 用户数量:1
2016-03-20 22:13:41 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36] from current transaction
2016-03-20 22:13:41 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())
2016-03-20 22:13:41 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:41 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 1 条
2016-03-20 22:13:42 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:42 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:42 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4fb26a36]
2016-03-20 22:13:42 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Initiating transaction commit
2016-03-20 22:13:42 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver]
2016-03-20 22:13:42 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] <== Total: 2
2016-03-20 22:13:42 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@32358420]
2016-03-20 22:13:42 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:0 用户数量:2
2016-03-20 22:13:42 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@32358420] from current transaction
2016-03-20 22:13:42 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())
2016-03-20 22:13:42 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@32358420]
2016-03-20 22:13:42 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 0 条
2016-03-20 22:13:42 [org.springframework.jdbc.datasource.DataSourceUtils]-[DEBUG] Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] to 4
2016-03-20 22:13:42 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Releasing JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction
2016-03-20 22:13:42 [org.springframework.web.servlet.DispatcherServlet]-[DEBUG] Null ModelAndView returned to DispatcherServlet with name 'spring': assuming HandlerAdapter completed request handling
2016-03-20 22:13:42 [org.springframework.web.servlet.DispatcherServlet]-[DEBUG] Successfully completed request
2016-03-20 22:13:48 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@32358420] from current transaction

...

2016-03-20 22:13:49 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver]
2016-03-20 22:13:49 [org.springframework.jdbc.datasource.DataSourceUtils]-[DEBUG] Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] to 4
2016-03-20 22:13:49 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Releasing JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction

2016/3/20 posted in  数据库

数据库事务实验 (ISOLATION_DEFAULT)

ISOLATION_DEFAULT: Use the default isolation level of the underlying datastore.

The default isolation level in MySQL’s InnoDB is REPEATABLE READ.

实验一

  • @transaction标签在class上
  • mapper xml没有配置flushCache以及useCache
  • 初始表数据为空

先执行insert20次,每次休眠1秒

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

再执行select30次,每次休眠1秒

    List<WechatUser> wechatUser = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUser.size());

结果:

2016-03-18 12:49:39 [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource]-[DEBUG] Adding transactional method 'WechatRegistServiceImpl.insertUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''

2016-03-18 12:49:39 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Creating new transaction with name [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl.insertUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''

2016-03-18 12:49:40 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Acquired Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction

2016-03-18 12:49:40 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Switching JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] to manual commit

2016-03-18 12:49:40 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Creating a new SqlSession

2016-03-18 12:49:40 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353]

2016-03-18 12:49:40 [org.mybatis.spring.transaction.SpringManagedTransaction]-[DEBUG] JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] will be managed by Spring

2016-03-18 12:49:40 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())

2016-03-18 12:49:40 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Parameters: 1234567890(String), insert(String), null

2016-03-18 12:49:40 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] <== Updates: 1

2016-03-18 12:49:40 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353]

2016-03-18 12:49:40 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 0 条

2016-03-18 12:49:41 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353] from current transaction

2016-03-18 12:49:41 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())

2016-03-18 12:49:41 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Parameters: 1234567890(String), insert(String), null

2016-03-18 12:49:41 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] <== Updates: 1

2016-03-18 12:49:41 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession
[org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353]
2016-03-18 12:49:41 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 1 条

...

2016-03-18 12:49:43 [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[DEBUG] Looking up handler method for path /select.html

2016-03-18 12:49:43 [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource]-[DEBUG] Adding transactional method 'WechatRegistServiceImpl.selectUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''

2016-03-18 12:49:43 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Creating new transaction with name [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl.selectUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''

2016-03-18 12:49:43 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Acquired Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction

2016-03-18 12:49:43 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Switching JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] to manual commit

2016-03-18 12:49:43 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Creating a new SqlSession

2016-03-18 12:49:43 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d]

2016-03-18 12:49:43 [org.mybatis.spring.transaction.SpringManagedTransaction]-[DEBUG] JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] will be managed by Spring

2016-03-18 12:49:43 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER

2016-03-18 12:49:43 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Parameters:

2016-03-18 12:49:43 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] <== Total: 0

2016-03-18 12:49:43 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession
[org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d]

2016-03-18 12:49:43 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:0 用户数量:0

2016-03-18 12:49:44 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353] from current transaction

2016-03-18 12:49:44 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())

...

2016-03-18 12:49:44 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d] from current transaction

2016-03-18 12:49:44 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d]

2016-03-18 12:49:44 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:1 用户数量:0

...

2016-03-18 12:50:00 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353]

2016-03-18 12:50:00 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353]

2016-03-18 12:50:00 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21e3b353]

2016-03-18 12:50:00 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Initiating transaction commit

2016-03-18 12:50:00 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver]

2016-03-18 12:50:00 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Releasing JDBC Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction

2016-03-18 12:50:00 [org.springframework.jdbc.datasource.DataSourceUtils]-[DEBUG] Returning JDBC Connection to DataSource

2016-03-18 12:50:11 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d] from current transaction

2016-03-18 12:50:11 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d]

2016-03-18 12:50:11 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:28 用户数量:0

2016-03-18 12:50:12 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d] from current transaction

2016-03-18 12:50:12 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a50701d]

2016-03-18 12:50:12 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:29 用户数量:0

再发一次select请求

...

2016-03-18 12:59:34 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER
2016-03-18 12:59:34 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Parameters:
2016-03-18 12:59:34 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] == Total: 20
2016-03-18 12:59:34 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75ab43b7]
2016-03-18 12:59:34 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:0 用户数量:20
2016-03-18 12:59:35 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75ab43b7] from current transaction
2016-03-18 12:59:35 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75ab43b7]
2016-03-18 12:59:35 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:1 用户数量:20
2016-03-18 12:59:36 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75ab43b7] from current transaction
2016-03-18 12:59:36 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75ab43b7]
2016-03-18 12:59:36 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:2 用户数量:20

结论

  • 读在一个事务中读的都是缓存,一个事务进行中没有新的请求过去
  • 事务的状态是:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
  • 先是Creating a new SqlSession,然后Releasing transactional SqlSession,再有请求则是Fetched SqlSession from current transaction
  • 读事务中第一次读,拿到的数据是写事务进行之前数据库的状态;之后拿到的数据都是在缓存中拿到的,无论写事务状态是什么

实验二

  • @transaction标签在class上
  • mapper xml没有配置flushCache以及useCache

先执行insert20次,每次休眠1秒(同一个insert方法)

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

再执行insert20次,每次休眠1秒(同一个insert方法)

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

结果:

2016-03-18 13:16:56 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())
2016-03-18 13:16:56 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Parameters: 1234567890(String), insert(String), null
2016-03-18 13:16:56 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] == Updates: 1
2016-03-18 13:16:56 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3cb57f97]
2016-03-18 13:16:56 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 1 条
2016-03-18 13:16:57 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@302693e9] from current transaction
2016-03-18 13:16:57 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())
2016-03-18 13:16:57 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Parameters: 1234567890(String), insert(String), null
2016-03-18 13:16:57 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] == Updates: 1
2016-03-18 13:16:57 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@302693e9]
2016-03-18 13:16:57 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 8 条

结论

  • 两次写事务并行进行

实验三

  • @transaction标签在方法上
  • mapper xml没有配置flushCache以及useCache
  • 初始表数据为空

先执行insert20次,每次休眠1秒

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

再执行select30次,每次休眠1秒

    List<WechatUser> wechatUser = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUser.size());

结果:

同实验一


实验四

  • @transaction标签在方法上
  • mapper xml没有配置flushCache以及useCache

先执行insert20次,每次休眠1秒

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

再执行insert20次,每次休眠1秒

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

结果:

同实验二


实验五

  • @transaction标签在方法上
  • mapper xml设置flushCache="true" useCache="false"
    <select id="select" resultMap="BaseResultMap" parameterType="java.lang.String" flushCache="true" useCache="false">
  • 初始表数据为空

先执行insert20次,每次休眠1秒

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

再执行select30次,每次休眠1秒

    List<WechatUser> wechatUser = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUser.size());

结果:

2016-03-18 13:49:37 [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource]-[DEBUG] Adding transactional method 'WechatRegistServiceImpl.insertUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-03-18 13:49:37 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Creating new transaction with name [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl.insertUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-03-18 13:49:38 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Acquired Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction

...

2016-03-18 13:49:38 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Preparing: insert into WECHAT_USER (open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date) values (?, ?, 1, ?, "system", now(), "system", now())

2016-03-18 13:49:38 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] ==> Parameters: 1234567890(String), insert(String), null

2016-03-18 13:49:38 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.insert]-[DEBUG] <== Updates: 1

2016-03-18 13:49:38 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2e6ab308]

2016-03-18 13:49:38 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 0 条

...

2016-03-18 13:49:39 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 1 条

...

2016-03-18 13:49:40 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 2 条

2016-03-18 13:49:40 [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[DEBUG] Looking up handler method for path /select.html

2016-03-18 13:49:40 [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource]-[DEBUG] Adding transactional method 'WechatRegistServiceImpl.selectUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''

...

2016-03-18 13:49:40 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER

...

2016-03-18 13:49:40 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:0 用户数量:0

...

2016-03-18 13:49:41 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER

...

2016-03-18 13:49:41 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:1 用户数量:0

...

2016-03-18 13:50:08 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER

2016-03-18 13:50:08 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:28 用户数量:0

2016-03-18 13:50:09 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.select]-[DEBUG] ==> Preparing: select user_id, open_id, user_name, use_yn, authority_value, insert_id, insert_date, modify_id, modify_date from WECHAT_USER

2016-03-18 13:50:09 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:29 用户数量:0

结论

  • 读事务中第一次读,拿到的数据是写事务进行之前数据库的状态

实验六

  • @transaction标签在方法上

先执行update20次,每次休眠1秒(update同一条数据)

    int result = wechatUserMapper.update(wechatUser);
    logger.info("----------------修改数据:" + i + "  结果" + result);

再执行update20次,每次休眠1秒(update同一条数据)

    int result = wechatUserMapper.update(wechatUser);
    logger.info("----------------修改数据:" + i + "  结果" + result);

结果:

2016-03-18 14:50:30 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.update]-[DEBUG] ==> Preparing: update WECHAT_USER SET open_id = ?, user_name = ?, modify_date = now() where open_id = "abc"

2016-03-18 14:50:30 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------修改数据:0 结果1

2016-03-18 14:50:31 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@77e2cc94] from current transaction

2016-03-18 14:50:31 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.update]-[DEBUG] ==> Preparing: update WECHAT_USER SET open_id = ?, user_name = ?, modify_date = now() where open_id = "abc"

2016-03-18 14:50:31 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------修改数据:1 结果0

2016-03-18 14:50:32 [org.springframework.web.servlet.DispatcherServlet]-[DEBUG] DispatcherServlet with name 'spring' processing GET request for [/springtest/update.html]

2016-03-18 14:50:32 [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[DEBUG] Looking up handler method for path /update.html

...

2016-03-18 14:50:32 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.update]-[DEBUG] ==> Preparing: update WECHAT_USER SET open_id = ?, user_name = ?, modify_date = now() where open_id = "abc"

2016-03-18 14:50:32 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------修改数据:2 结果0

...

2016-03-18 14:50:49 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------修改数据:19 结果0

2016-03-18 14:50:50 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@77e2cc94]

2016-03-18 14:50:50 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@77e2cc94]

2016-03-18 14:50:50 [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@77e2cc94]

2016-03-18 14:50:50 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Initiating transaction commit

2016-03-18 14:50:50 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sip?useUnicode=yes&characterEncoding=UTF8, UserName=root@localhost, MySQL-AB JDBC Driver]

...

2016-03-18 14:50:50 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------修改数据:0 结果0

...

2016-03-18 14:50:51 [cn.hao24.mobauto.mapper.wechatusers.WechatUserMapper.update]-[DEBUG] ==> Preparing: update WECHAT_USER SET open_id = ?, user_name = ?, modify_date = now() where open_id = "abc"

2016-03-18 14:50:51 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------修改数据:1 结果0

结论

  • 当两个写事务都是操作同一个数据的时候,第二个写事务会等第一个写事务提交之后才会进行

实验七

  • @transaction标签在方法上

先执行update20次,每次休眠1秒(update不同一条数据)

    int result = wechatUserMapper.update(wechatUser);
    logger.info("----------------修改数据:" + i + "  结果" + result);

再执行update20次,每次休眠1秒(update不同一条数据)

    int result = wechatUserMapper.update(wechatUser);
    logger.info("----------------修改数据:" + i + "  结果" + result);

结果:

同实验六


实验八

  • @transaction标签在方法上

先执行update20次,每次休眠1秒

    int result = wechatUserMapper.update(wechatUser);
    logger.info("----------------修改数据:" + i + "  结果" + result);

再执行insert20次,每次休眠1秒

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

结果:

先执行updateupdate事务结束后,执行insert


实验九

  • @transaction标签在class

先执行insert20次,每次休眠1秒(不同insert方法)

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

再执行insert20次,每次休眠1秒(不同insert方法)

    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

结果:

并行


实验十

  • @transaction标签在方法上
  • mapper xml设置flushCache="true" useCache="false"
    <select id="select" resultMap="BaseResultMap" parameterType="java.lang.String" flushCache="true" useCache="false">

先执行select + insert20次,每次休眠1秒(同一个select + insert方法)

    List<WechatUser> wechatUserList = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUserList.size());
    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

再执行select + insert20次,每次休眠1秒(同一个select + insert方法)

    List<WechatUser> wechatUserList = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUserList.size());
    wechatUserMapper.insert(wechatUser);
    logger.info("----------------写入数据第   " + i + " 条");

结果:

2016-03-18 16:17:43 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:5 用户数量:5

...

2016-03-18 16:17:43 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 5 条

...

2016-03-18 16:17:44 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:0 用户数量:0

...

2016-03-18 16:17:44 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 0 条

...

2016-03-18 16:17:44 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:6 用户数量:6

...

2016-03-18 16:17:44 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 6 条

...

2016-03-18 16:17:45 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:1 用户数量:1

...

2016-03-18 16:17:58 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:19 用户数量:19

...

2016-03-18 16:17:58 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 19 条

...

2016-03-18 16:17:58 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------读出数据:14 用户数量:14

...

2016-03-18 16:17:58 [cn.hao24.mobauto.db.service.wechatuser.impl.WechatRegistServiceImpl]-[INFO] ----------------写入数据第 14 条

结论

  • 两个读写事务或并行运行;但是他们对数据库的影响不会作用到彼此,即大家读的都是快照

实验十一

  • @transaction标签在方法上
  • mapper xml设置flushCache="true" useCache="false"
    <select id="select" resultMap="BaseResultMap" parameterType="java.lang.String" flushCache="true" useCache="false">

先执行select + update20次,每次休眠1秒(同一个select + update方法)

    List<WechatUser> wechatUserList = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUserList.size());
    int result = wechatUserMapper.update(wechatUser);
    logger.info("----------------修改数据:" + i + "  结果" + result);

再执行select + update20次,每次休眠1秒(同一个select + update方法)

    List<WechatUser> wechatUserList = wechatUserMapper.select();
    logger.info("----------------读出数据:" + i + "    用户数量:" + wechatUserList.size());
    int result = wechatUserMapper.update(wechatUser);
    logger.info("----------------修改数据:" + i + "  结果" + result);

结果:

两次大事务串行

2016/3/18 posted in  数据库

MyBatis缓存机制

关于Mybatis

MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和JavaPOJOPlain Old Java Objects,普通的Java对象)映射成数据库中的记录。

一、为什么需要缓存

提高对数据库查询的效率,提高应用的性能。

二、MyBatis缓存机制整体设计

MyBatis缓存分为一级和二级,使用顺序:

二级缓存 —> 一级缓存 —> 数据库

三、MyBatis 一级缓存

1、什么是一级缓存

对于会话(Session)级别的数据缓存,称之为一级缓存。

2、一级缓存的生命周期

  • MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象, SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的 PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
  • 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;
  • 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;
  • SqlSession中执行了任何一个update操作(update()delete()insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;

3、一级缓存工作流程

  • 对于某个查询,根据statementId, params, rowBounds来构建一个key,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;
  • 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
  • 如果命中,则直接将缓存结果返回;
  • 如果没命中:

    • 去数据库中查询数据,得到查询结果;
    • 将key和查询到的结果分别作为key, value对存储到Cache中;
    • 将查询结果返回;
  • 结束。

4、一级缓存性能分析

  • MyBatis对会话(Session)级别的一级缓存设计的比较简单,就简单地使用了 HashMap来维护,并没有对HashMap的容量和大小进行限制。

  • 一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念

四、MyBatis二级缓存

1、二级缓存工作模式

当开一个会话时,一个SqlSession对象会使用一个Executor对象来完成会话操作, MyBatis的二级缓存机制的关键就是对这个Executor对象做文章。如果用户配了cacheEnabled=true,那么MyBatis在为SqlSession对象创建Executor对象时,会对Executor对象加上一个装饰者:CachingExecutor,这时 SqlSession使用CachingExecutor对象来完成操作请求。CachingExecutor对于查询请求,会先判断该查询请求在Application级别的二级缓存中是否有缓存结果,如果有查询结果,则直接返回缓存结果;如果缓存中没有,再交给真正的Executor对象来完成查询操作,之后CachingExecutor会将真正Executor返回的查询结果放置到缓存中,然后在返回给用户。

2、二级缓存划分

MyBatis并不是简单地对整个Application就只有一个Cache缓存对象,它将缓存划分的更细,即是Mapper级别的,即每一个Mapper都可以拥有一个Cache对象,具体如下:

  • 为每一个Mapper分配一个Cache缓存对象(使用 <cache> 节点配置);
  • 多个Mapper共用一个Cache缓存对象(使用 <cache-ref> 节点配置);

要想使某条Select查询支持二级缓存,你需要保证:

  1. MyBatis支持二级缓存的总开关:全局配置变量参数cacheEnabled = true
  2. select语句所在的Mapper,配置了 <cache><cached-ref>节点,并且有效
  3. select语句的参数useCache = true

3、二级缓存生命周期

二级缓存是Application应用级别的缓存,它的是生命周期很长,跟Application的声明周期一样,也就是说它的作用范围是整个Application应用。

4、二级缓存禁用与刷新

useCache = "false"可以禁用二级缓存,默认select语句useCache = "true"

flushCache = "false"即不会刷新缓存,默认flushCache = "true"

5、Mybatis Cache参数

lushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。

readOnly(只读)属性可以被设置为truefalse。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

如下例子:

<cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>

这个更高级的配置创建了一个FIFO缓存,并每隔60秒刷新,存数结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有, 默认的是`LRU:

LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

6、二级缓存的局限性

Mybatis的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空,而无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,解决此类问题需要在业务层根据需求对数据有针对性缓存。

2016/3/15 posted in  数据库

事务原理与实践

事务简介

  • 事务就是锁和并发的结合体。
  • 所有的针对数据库的一个操作,都可以看做成一个事务。
  • 事务单元之间只有这四种关系:读写、写读、读读、写写
  • 真实业务场景中,如何以最快的速度的方式完成事务和事务之间的关系?又能保证上面四种逻辑的顺序?
  • 做法:

    • 序列化读写ACID中的I破坏了一致性
      • 优势:不需要冲突控制
      • 劣势:慢
    • 针对同一单元的访问进行控制(排他锁):如果两个事务发生冲突,就串行;如果两个事务不会发生冲突,就并行。

      譬如两个事务单元Bob给Smith一百块;李磊给韩梅梅一百块,这两个事务单元完全没有冲突,应该可以并行
      
    • 读写锁分开读读的场景并行;读写写读写写串行

    • MVCC:多版本并发控制Muti-Version Concurrency Control,本质就是Copy on write,每次写都是写一个新的数据,并不是在原位更新,能够做到写不阻塞读,带来写读读写场景的优化。

      • 劣势:系统实现的复杂度增加

现在只剩下写写才会产生冲突

事务谁先谁后?

一个读请求应该读哪一个写之后的数据?

逻辑时间戳:内存中维护一个数据的自增ID号,本质就是用来说明事务和事务单元之间谁先谁后

故障恢复

  • 业务属性不匹配:ACID中的A 回滚,需要记住之前数据没有更改时的状态
  • 系统崩溃

死锁与死锁检测

死锁产生的原因:

  • 两个线程参与
  • 不同方向上加锁
  • 作用在相同资源

解决方案:

  • 尽可能不死锁:降低事务隔离级别
  • 碰撞检测:把所有事务单元维持的所有锁都记下来,终止死锁的一边
  • 等锁超时

事务的ACID(数据库怎么保证的)

  • 原子性

要么全部成功,要么全部失败

只是记录了一个undo日志回滚到之前的版本

Bob给Smith一百块

ver1: Bob有100元 Smith有0元
ver2: Bob有0元 Smith有0元
    (undo:Bob有100元 Smith有0元) <= 数据库回滚段
ver3: Bob有100元 Smith有100元
    (undo:Bob有0元 Smith有0元) <= 数据库回滚段
  • 一致性
-- Lock Bob and Smith --
ver1: Bob有100元 Smith有0元
ver2: Bob有0元 Smith有0元
    (undo:Bob有100元 Smith有0元) <= 数据库回滚段
ver3: Bob有0元 Smith有100元
    (undo:Bob有0元 Smith有0元) <= 数据库回滚段
-- Unlock Bob and Smith --
  • 隔离性

以性能为理由,对一致性的破坏(各种隔离级别)

隔离性扩展:快照

  • 持久性

更改便持久的保存在数据库中,只要提交了就不丢

快照读的核心做法

在回滚段中读

  • 针对读多写少的情况

大部分数据库已经将快照读映射到读未提交、读已提交

事务的调优原则

  • 在不影响业务应用的前提下,减小锁的覆盖范围
  • 增加锁上可并行的线程数:读写锁分离、允许并行读取数据
  • 选择正确锁的类型

Consistent Nonlocking Reads in MySQL

A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time.

The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions.

The exception to this rule is that the query sees the changes made by ealier statements within the same transaction. This exception causes the following anomaly: if you update some rows in a time, a SELECT sees the latest version of the updated rows, but it might also see older versions of any rows. If other sessions simultaneously update the same table, the anomaly means that you might see the table in a state that never existed in the database.

If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established byu the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries.

With READ COMMITTED isolation level, each consistent read within a transaction sets and reads its own fresh snapshot.

Consistent read is the default mode in which InnoDB processes SELECT statements in READ COMMITTED and REPEATABLE READ isolation levels. A consistent read does not set any locks on the tables it accesses, and therefore other sessions are free to modify those tables at the same time a consistent read is being performed on the table.

Suppose that you are running in the default REPEATABLE READ isolation level. When you issue a consistent read (that is, an ordinary SELECT statement), InnoDB gives your transaction a timepoint according to which your query sees the database. If another transaction deletes a row and commits after your timepoint was assigned, you do not see the row as having been deleted. Inserts and updates are treated similarly.

你读数据的时候,数据不会加锁,其他人都能改你读的数据;但是你一直读到的都是最原始的数据,其他人怎么改都不会影响你读到的东西。

In the following example, session A sees the row inserted by B onluy when B has committed the insert and A has committed as well, so that the timepoint is advanced past the commit of B.

Session ASession B
SET autocommit=0; SET autocommit=0;
SELECT * FROM t; # empty set
INSERT INTO t VALUES (1, 2);
SELECT * FROM t; # empty set
COMMIT;
SELECT * FROM t; # empty set
COMMIT;
SELECT * FROM t;

READ UNCOMMITTED: UserA will see the change made by UserB. This isolation level is called dirty reads, which means that read data is not consistent with other parts of the table or the query, and may not yet have been committed. This isolation level ensures the quickest performance, as data is read directly from the table’s blocks with no further processing, verifications or any other validation. The process is quick and the data is as dirty as it can get.

READ COMMITTED: UserA will not see the change made by UserB. This is because in the READ COMMITTED isolation level, the rows returned by a query are the rows that were committed when the query was started. The change made by UserB was not present when the query started, and therefore will not be included in the query result.

REPEATABLE READ: UserA will not see the change made by UserB. This is because in the REPEATABLE READ isolation level, the rows returned by a query are the rows that were committed when the transaction was started. The change made by UserB was not present when the transaction was started, and therefore will not be included in the query result.

This means that “All consistent reads within the same transaction read the snapshot established by the first read” (from MySQL documentation.).

SERIALIZABLE: This isolation level specifies that all transactions occur in a completely isolated fashion, meaning as if all transactions in the system were executed serially, one after the other. The DBMS can execute two or more transactions at the same time only if the illusion of serial execution can be maintained.

In practice, SERIALIZABLE is similar to REPEATABLE READ, but uses a different implementation for each database engine. In Oracle, the REPEATABLE READ level is not supported and SERIALIZABLE provides the highest isolation level. This level is similar to REPEATABLE READ, but InnoDB implicitly converts all plain SELECT statements to “SELECT … LOCK IN SHARE MODE.

The default isolation level in MySQL’s InnoDB is REPEATABLE READ.

Since old values of row data are required for current queris, databases use a special segment to store old row values and snapshot. MySQL calls this segment a Rollback Segment (Undo Segment in Oracle).

2016/3/11 posted in  数据库