免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 10243 | 回复: 15
打印 上一主题 下一主题

一致性读的秘密(innobase note -- read) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-08-25 11:48 |只看该作者 |倒序浏览
本帖最后由 justlooks 于 2010-08-26 14:18 编辑

首先是来自手册的一致性读的定义:
A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time
一致读就是innodb使用MVCC机制把数据库某一点的快照提供给一个查询
The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions.
该查询能够看到在这个点之前提交的事务所做的所有改变,但是看不到之后开始的事务,或者是在这个时间点还未提交的事务所做的改变

innobase使用read view来实现这个一致读,read view和trx是紧密联系的,通过view_list域把自己连到trx_sys上,而每个read view都有个创建它的trx,用creator_trx_id字段标记
来看下read view的数据结构

  1. struct read_view_struct{
  2.          ulint   type;
  3.          dulint  undo_no;
  4.          dulint  low_limit_no;         #这个不是很清楚
  5.          dulint  low_limit_id;
  6.          dulint  up_limit_id;
  7.          ulint   n_trx_ids;
  8.          dulint* trx_ids;
  9.          dulint  creator_trx_id;
  10.          UT_LIST_NODE_T(read_view_t) view_list;
  11. }
复制代码
那么从read view的创建代码(read_view_open_now调用)并结合定义来分析以上字段的意义

  1. #首先会在trx_sys系统结构中统计所有当前没有提交的事务
  2. view = read_view_create_low(UT_LIST_GET_LEN(trx_sys->trx_list), heap)
  3. #那么对于该view创建点之后的新事务,该read view是无法看到其数据变化的,read view用low_limit_id来标记这个时间点,也就是说凡是trx id超过这个值的就属于将来的事务
  4. view->low_limit_no = trx_sys->max_trx_id;
  5. view->low_limit_id = view->low_limit_no;
  6. #那么自然所有这个创建时间中活跃的事务也是无法被该view看到的,除了read view的创建者(注意这个例外,会导致奇怪的现象)
  7. n = 0;
  8. trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
  9. while (trx) {
  10.         if (ut_dulint_cmp(trx->id, cr_trx_id) != 0
  11.                            && (trx->conc_state == TRX_ACTIVE
  12.                            || trx->conc_state == TRX_PREPARED)) {
  13. .......
  14.       trx = UT_LIST_GET_NEXT(trx_list, trx);
  15. }
  16. #而如何描述过去呢?read view用up_limit_id描述过去,也就是当trx id小于这个值都是过去commit的事务
  17. if (n > 0) {
  18.         view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);  #因为这个数组是来自trx list的,而trx list是开头是最新的事务,所以这个就是最老的未commit的事务
  19.        } else {
  20.           view->up_limit_id = view->low_limit_id;
  21.      }
  22. #VIEW list也是从新到旧排列的
  23. UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
复制代码
下面是一致读的判定代码,对照上面的说明应该很清楚了,在read_view_sees_trx_id调用中

  1.    if (ut_dulint_cmp(trx_id, view->up_limit_id) < 0) {

  2.                 return(TRUE);
  3.         }

  4.         if (ut_dulint_cmp(trx_id, view->low_limit_id) >= 0) {

  5.                 return(FALSE);
  6.         }

  7.         n_ids = view->n_trx_ids;

  8.         for (i = 0; i < n_ids; i++) {

  9.                 cmp = ut_dulint_cmp(
  10.                         trx_id,
  11.                         read_view_get_nth_trx_id(view, n_ids - i - 1));
  12.                 if (cmp <= 0) {
  13.                         return(cmp < 0);
  14.                 }
  15.         }

  16.         return(TRUE);
复制代码
那么再来看下上面提到的奇怪的现象,先来看演示

mysql> show create table t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `id` int(11) NOT NULL,
  `name` char(10) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

S1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

S2
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set id=id+200 where name='bbb';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

S1
mysql> select * from t1;
+----+------+
| id | name |
+----+------+
|  1 | aaa  |
| 22 | bbb  |
+----+------+
2 rows in set (0.00 sec)

S2
mysql> commit;
Query OK, 0 rows affected (0.04 sec)


S1
mysql> update t1 set id=id+20 where name='bbb';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from t1;
+-----+------+
| id  | name |
+-----+------+
|   1 | aaa  |
|  22 | bbb  |
| 242 | bbb  |
+-----+------+
3 rows in set (0.00 sec)

评分

参与人数 1可用积分 +6 收起 理由
枫影谁用了 + 6 原创文章

查看全部评分

论坛徽章:
59
2015七夕节徽章
日期:2015-08-24 11:17:25ChinaUnix专家徽章
日期:2015-07-20 09:19:30每周论坛发贴之星
日期:2015-07-20 09:19:42ChinaUnix元老
日期:2015-07-20 11:04:38荣誉版主
日期:2015-07-20 11:05:19巳蛇
日期:2015-07-20 11:05:26CU十二周年纪念徽章
日期:2015-07-20 11:05:27IT运维版块每日发帖之星
日期:2015-07-20 11:05:34操作系统版块每日发帖之星
日期:2015-07-20 11:05:36程序设计版块每日发帖之星
日期:2015-07-20 11:05:40数据库技术版块每日发帖之星
日期:2015-07-20 11:05:432015年辞旧岁徽章
日期:2015-07-20 11:05:44
2 [报告]
发表于 2010-08-25 14:32 |只看该作者
学习。谢谢分享。

论坛徽章:
0
3 [报告]
发表于 2010-08-25 18:03 |只看该作者
回复 1# justlooks

使用了什么隔离级别?

论坛徽章:
0
4 [报告]
发表于 2010-08-25 19:23 |只看该作者
回复 3# yejr


    默认的,也就是可重复读

论坛徽章:
0
5 [报告]
发表于 2010-08-26 11:28 |只看该作者
你试下就知道了,关于这个手册上有提到,在一致读章节

论坛徽章:
9
每日论坛发贴之星
日期:2016-01-04 06:20:00数据库技术版块每日发帖之星
日期:2016-01-04 06:20:00每日论坛发贴之星
日期:2016-01-04 06:20:00数据库技术版块每日发帖之星
日期:2016-01-04 06:20:00IT运维版块每日发帖之星
日期:2016-01-04 06:20:00IT运维版块每日发帖之星
日期:2016-01-04 06:20:00综合交流区版块每日发帖之星
日期:2016-01-04 06:20:00综合交流区版块每日发帖之星
日期:2016-01-04 06:20:00数据库技术版块每周发帖之星
日期:2016-03-07 16:30:25
6 [报告]
发表于 2010-08-26 12:03 |只看该作者
同觉得不可能

论坛徽章:
8
综合交流区版块每周发帖之星
日期:2015-12-02 15:03:53数据库技术版块每日发帖之星
日期:2015-10-02 06:20:00IT运维版块每日发帖之星
日期:2015-10-02 06:20:00IT运维版块每日发帖之星
日期:2015-09-14 06:20:00金牛座
日期:2014-10-10 11:23:34CU十二周年纪念徽章
日期:2013-10-24 15:41:34酉鸡
日期:2013-10-19 10:17:1315-16赛季CBA联赛之北京
日期:2017-03-06 15:12:44
7 [报告]
发表于 2010-08-26 12:13 |只看该作者
本帖最后由 ruochen 于 2010-08-26 12:15 编辑

我在5.5.4-m3上测试没发现,RR隔离级别


LZ的帖子中有点奇怪,一个简单的update在你的机器上跑了11.22 sec???
-------------------------------------------------
Query OK, 1 row affected (11.22 sec)
-------------------------------------------------


LZ把版本(mysql & innodb)和时间戳都加上,再测试一遍看看

论坛徽章:
0
8 [报告]
发表于 2010-08-26 13:42 |只看该作者
本帖最后由 justlooks 于 2010-08-26 14:23 编辑
我在5.5.4-m3上测试没发现,RR隔离级别


LZ的帖子中有点奇怪,一个简单的update在你的机器上跑了11.22 s ...
ruochen 发表于 2010-08-26 12:13



    那个是因为session 1 没有commit导致了这个update被block。

另外抱歉我的演示过程写错了,为什么你们按我的演示过程无法出我的结果呢,因为我session 2 忘记首先select了,那么如果第1次不select就不会从undo log中建立先前的版本,我改正了下你们再看看

这个结果是一致读本身的机制导致的,结果并不会因为MYSQL的版本不同而不同

论坛徽章:
8
综合交流区版块每周发帖之星
日期:2015-12-02 15:03:53数据库技术版块每日发帖之星
日期:2015-10-02 06:20:00IT运维版块每日发帖之星
日期:2015-10-02 06:20:00IT运维版块每日发帖之星
日期:2015-09-14 06:20:00金牛座
日期:2014-10-10 11:23:34CU十二周年纪念徽章
日期:2013-10-24 15:41:34酉鸡
日期:2013-10-19 10:17:1315-16赛季CBA联赛之北京
日期:2017-03-06 15:12:44
9 [报告]
发表于 2010-08-26 15:20 |只看该作者
我在5.5.4-m3上演示了,存在LZ所说的事实

S1
mysql> update t1 set id=id+20 where name='bbb';
这里S1没有commit

RR隔离级别是存在幻读

论坛徽章:
0
10 [报告]
发表于 2010-08-26 16:07 |只看该作者
回复 9# ruochen


    我觉得这个不应该是phantom read,我理解的phantom read是重复执行同一条查询会得到前后不一致的情况,而演示中2个select中间插了个update,那么就应该不是phantom read
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP