postgres-xl分布式数据库测试2_分布式事务的强一致性

postgres-xl分布式数据库测试1已经测试了pgxl的分布式存储(REPLICATION、ROUNDROBIN、HASH(key) 、MODULO(key))、跨分片join查询、各种聚合运算、分布式事务回滚/提交等操作,接下来我们会测试分布式事务的强一致性。

这个测试,利用了上一篇文章的结论:一个事务修改记录时,会在该事务修改的记录上加上 write lock,直至事务结束(提交或者回滚)其他事务才能修改这部分记录。

测试环境

4台以下配置的虚拟机

  • CPU:4核
  • 内存:8GB
  • 硬盘:200GB
  • 系统:CentOS7.6 minimal

主机规划

server0 192.168.80.100 gtm
server1 192.168.80.101 gtm-proxy1,coordinator1,datanode1
server2 192.168.80.102 gtm-proxy2,coordinator2,datanode2
server3 192.168.80.103 gtm-proxy3,coordinator3,datanode3

测试规划:

  • a). sessionA、sessionB 连接 coordinator1,sessionC连接 coordinator2,sessionD连接 coordinator3;
  • b). 创建一个 t_session_lock ,并增加一条记录1,作为事务间的锁;
  • c). sessionA 启动事务,在 t_session_lock表 上加 access exclusive 锁;
  • d). sessionA 修改 其它表的数据;
  • e). sessionB、sessionC,sessionD 执行脚本
# 查询 t_session_lock表 的数据(目的是为了hold住后面的操作);
# select now();
# 做一个 步骤d修改数据的查询
  • f). sessionA 执行脚本
# rollback/commit;
# select now();
# 做一个 步骤d修改数据的查询
  • g). 对比4个session的输出的最后查询结果是否一致。

初始化 t_session_lock 脚本

postgres=#  CREATE TABLE t_session_lock (id int primary key,val int) DISTRIBUTE BY HASH(id);
CREATE TABLE
postgres=#  insert into t_session_lock (id,val ) values(1,1);
INSERT 0 1
postgres=# select * from t_session_lock;
 id | val 
----+-----
  1 |   1
(1 row)

测试脚本

  • sessionA
# 提前做一次查询,作为数据基础,如果回滚,需要用于结果对比
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;

begin;
lock table t_session_lock in access exclusive mode;
update t_student set class_id=class_id+1;
update t_grades set record=record+5 where grade_id between 20000 and 50000;
# 事务结束前查询,如果提交,需要对比这个结果
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;

# 两次测试,一次是回滚事务,一次是提交事务
rollback/commit; 

select now();
# 最终数据
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
  • sessionB、sessionC、sessionD
# 提前做一次查询,作为数据基础,如果回滚,需要用于结果对比
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;

select * from t_session_lock where id=1;

select now();
# 最终数据
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;

测试过程——事务回滚

更新前的数据对比
每个session的继续执行时间
最终结果对比

sessionA:

[postgres@xCloud ~]$ psql -h server1 -p 20004
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# begin;
BEGIN
postgres=# lock table t_session_lock in access exclusive mode;
LOCK TABLE
postgres=# update t_student set class_id=class_id+1;
UPDATE 50000
postgres=# update t_grades set record=record+5 where grade_id between 20000 and 50000;
UPDATE 30001
postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 66455.79 | 159.57 | 0.14 | 65.7978118811881188
       92 | 54803.99 | 157.35 | 0.02 | 62.9930919540229885
      163 | 68855.25 | 159.16 | 0.02 | 63.1699541284403670
      194 | 57095.21 | 152.89 | 0.18 | 62.7419890109890110
      235 | 77933.95 | 151.70 | 0.02 | 64.9449583333333333
      296 | 69351.39 | 159.00 | 0.01 | 62.4787297297297297
      337 | 67968.84 | 157.38 | 0.16 | 62.3567339449541284
      368 | 73681.44 | 158.76 | 0.02 | 66.3796756756756757
      439 | 62588.58 | 158.35 | 0.10 | 61.9688910891089109
      490 | 65240.53 | 155.75 | 0.12 | 64.5945841584158416
(10 rows)

postgres=# rollback; 
ROLLBACK
postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:46:54.582986+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# 

sessionB:

[postgres@xCloud ~]$ psql -h server1 -p 20004
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# select * from t_session_lock where id=1;
select now();
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 id | val 
----+-----
  1 |   8
(1 row)

postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:46:54.586793+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# 

sessionC:

[postgres@xCloud ~]$ psql -h server2 -p 20005
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# select * from t_session_lock where id=1;
select now();
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 id | val 
----+-----
  1 |   8
(1 row)

postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:46:54.772295+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# 

sessionD:

[postgres@xCloud ~]$ psql -h server3 -p 20004
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# select * from t_session_lock where id=1;
select now();
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 id | val 
----+-----
  1 |   8
(1 row)

postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:46:54.942099+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# 

测试过程——事务提交

更新前的数据对比
每个session的继续执行时间
最终结果对比

sessionA:

[postgres@xCloud ~]$ psql -h server1 -p 20004
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# begin;
BEGIN
postgres=# lock table t_session_lock in access exclusive mode;
LOCK TABLE
postgres=# update t_student set class_id=class_id+1;
UPDATE 50000
postgres=# update t_grades set record=record+5 where grade_id between 20000 and 50000;
UPDATE 30001
postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 66455.79 | 159.57 | 0.14 | 65.7978118811881188
       92 | 54803.99 | 157.35 | 0.02 | 62.9930919540229885
      163 | 68855.25 | 159.16 | 0.02 | 63.1699541284403670
      194 | 57095.21 | 152.89 | 0.18 | 62.7419890109890110
      235 | 77933.95 | 151.70 | 0.02 | 64.9449583333333333
      296 | 69351.39 | 159.00 | 0.01 | 62.4787297297297297
      337 | 67968.84 | 157.38 | 0.16 | 62.3567339449541284
      368 | 73681.44 | 158.76 | 0.02 | 66.3796756756756757
      439 | 62588.58 | 158.35 | 0.10 | 61.9688910891089109
      490 | 65240.53 | 155.75 | 0.12 | 64.5945841584158416
(10 rows)

postgres=# commit; 
select now();
COMMIT
postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:58:13.875002+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 66455.79 | 159.57 | 0.14 | 65.7978118811881188
       92 | 54803.99 | 157.35 | 0.02 | 62.9930919540229885
      163 | 68855.25 | 159.16 | 0.02 | 63.1699541284403670
      194 | 57095.21 | 152.89 | 0.18 | 62.7419890109890110
      235 | 77933.95 | 151.70 | 0.02 | 64.9449583333333333
      296 | 69351.39 | 159.00 | 0.01 | 62.4787297297297297
      337 | 67968.84 | 157.38 | 0.16 | 62.3567339449541284
      368 | 73681.44 | 158.76 | 0.02 | 66.3796756756756757
      439 | 62588.58 | 158.35 | 0.10 | 61.9688910891089109
      490 | 65240.53 | 155.75 | 0.12 | 64.5945841584158416
(10 rows)

postgres=# 

sessionB:

[postgres@xCloud ~]$ psql -h server1 -p 20004
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# select * from t_session_lock where id=1;
select now();
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 id | val 
----+-----
  1 |   8
(1 row)

postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:58:13.879005+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 66455.79 | 159.57 | 0.14 | 65.7978118811881188
       92 | 54803.99 | 157.35 | 0.02 | 62.9930919540229885
      163 | 68855.25 | 159.16 | 0.02 | 63.1699541284403670
      194 | 57095.21 | 152.89 | 0.18 | 62.7419890109890110
      235 | 77933.95 | 151.70 | 0.02 | 64.9449583333333333
      296 | 69351.39 | 159.00 | 0.01 | 62.4787297297297297
      337 | 67968.84 | 157.38 | 0.16 | 62.3567339449541284
      368 | 73681.44 | 158.76 | 0.02 | 66.3796756756756757
      439 | 62588.58 | 158.35 | 0.10 | 61.9688910891089109
      490 | 65240.53 | 155.75 | 0.12 | 64.5945841584158416
(10 rows)

postgres=# 

sessionC:

[postgres@xCloud ~]$ psql -h server2 -p 20005
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# select * from t_session_lock where id=1;
select now();
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 id | val 
----+-----
  1 |   8
(1 row)

postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:58:14.063537+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 66455.79 | 159.57 | 0.14 | 65.7978118811881188
       92 | 54803.99 | 157.35 | 0.02 | 62.9930919540229885
      163 | 68855.25 | 159.16 | 0.02 | 63.1699541284403670
      194 | 57095.21 | 152.89 | 0.18 | 62.7419890109890110
      235 | 77933.95 | 151.70 | 0.02 | 64.9449583333333333
      296 | 69351.39 | 159.00 | 0.01 | 62.4787297297297297
      337 | 67968.84 | 157.38 | 0.16 | 62.3567339449541284
      368 | 73681.44 | 158.76 | 0.02 | 66.3796756756756757
      439 | 62588.58 | 158.35 | 0.10 | 61.9688910891089109
      490 | 65240.53 | 155.75 | 0.12 | 64.5945841584158416
(10 rows)

postgres=# 

sessionD:

[postgres@xCloud ~]$ psql -h server3 -p 20004
psql (PGXL 10r1, based on PG 10.5 (Postgres-XL 10r1))
Type "help" for help.

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 58952.04 | 154.56 | 0.50 | 64.7824615384615385
       92 | 68456.71 | 153.52 | 0.16 | 63.9782336448598131
      163 | 67983.32 | 149.90 | 0.20 | 62.9475185185185185
      194 | 62714.90 | 151.45 | 0.00 | 64.6545360824742268
      235 | 73089.31 | 149.80 | 0.06 | 64.6808053097345133
      296 | 63664.81 | 151.19 | 0.21 | 65.6338247422680412
      337 | 59510.79 | 149.64 | 0.21 | 62.6429368421052632
      368 | 54241.03 | 149.83 | 0.18 | 63.0709651162790698
      439 | 58996.90 | 152.02 | 0.08 | 60.2009183673469388
      490 | 73640.25 | 154.75 | 0.17 | 62.9403846153846154
(10 rows)

postgres=# select * from t_session_lock where id=1;
select now();
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 id | val 
----+-----
  1 |   8
(1 row)

postgres=# select now();
              now              
-------------------------------
 2018-12-06 13:58:14.232027+08
(1 row)

postgres=# select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id;
 class_id |   sum    |  max   | min  |         avg         
----------+----------+--------+------+---------------------
       31 | 66455.79 | 159.57 | 0.14 | 65.7978118811881188
       92 | 54803.99 | 157.35 | 0.02 | 62.9930919540229885
      163 | 68855.25 | 159.16 | 0.02 | 63.1699541284403670
      194 | 57095.21 | 152.89 | 0.18 | 62.7419890109890110
      235 | 77933.95 | 151.70 | 0.02 | 64.9449583333333333
      296 | 69351.39 | 159.00 | 0.01 | 62.4787297297297297
      337 | 67968.84 | 157.38 | 0.16 | 62.3567339449541284
      368 | 73681.44 | 158.76 | 0.02 | 66.3796756756756757
      439 | 62588.58 | 158.35 | 0.10 | 61.9688910891089109
      490 | 65240.53 | 155.75 | 0.12 | 64.5945841584158416
(10 rows)

postgres=# 

可以看到,无论是相同的coordinator还是不同的coordinator间,更新后立马查询的数据是一致。符合了强一致性原则。

同时,我们也看到,用这种锁的形式,同一个coordinator间(例如sessionA和sessionB都是连接 coordinator1 )一个session释放到另一个session活得锁时间差在0.004秒左右。不同的coordinator 间(例如sessionA和sessionB都是连接 coordinator1,sessionC连接 coordinator2,sessionD连接 coordinator3 )一个session释放到另一个session活得锁时间差在0.16秒左右。为了减少这种时间差造成影响,后面会使用编程形式,减少时间差。

使用java代码pgxl事务一致性

主要测试步骤:

sessionA(连接coordinator1) 更新数据
sessionA(连接coordinator1) 提交事务
sessionD(连接coordinator3) 查询这批数据相关信息得到【结果D】
sessionA(连接coordinator1) 查询这批数据相关信息得到【结果A】
对比【结果D】和【结果A】是否一致。

  • 代码如下:
package cn.hemny.test.test_pg;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * 使用java代码pgxl全局事务强一致性。
 * @author      hemny
 * @Package     cn.hemny.test.test_pg   
 * @Title       EventBusThread.java
 * @Email       hemny@qq.com
 * @Description 使用java代码pgxl全局事务强一致性。
 *                  主要测试步骤:
 *                  sessionA(连接coordinator1) 更新数据;
 *                  sessionA(连接coordinator1) 提交事务;
 *                  sessionD(连接coordinator3) 查询这批数据相关信息得到【结果D】;
 *                  sessionA(连接coordinator1) 查询这批数据相关信息得到【结果A】;
 *                  对比【结果D】和【结果A】是否一致。
 * 
 *              说明:本次测试使用pgxl 10r1版本
 *              部署结构
 *                      server0 192.168.80.100 gtm
 *                      server1 192.168.80.101 gtm-proxy1,coordinator1,datanode1
 *                      server2 192.168.80.102 gtm-proxy2,coordinator2,datanode2
 *                      server3 192.168.80.103 gtm-proxy3,coordinator3,datanode3
 */
public class TestPgxlFullyACID {
    private static final String JDBC_DRIVER = "org.postgresql.Driver";
    private static final String JDBC_URL1 = "jdbc:postgresql://192.168.80.101:20004/postgres";
    private static final String JDBC_URL2 = "jdbc:postgresql://192.168.80.102:20005/postgres";
    private static final String JDBC_URL3 = "jdbc:postgresql://192.168.80.103:20004/postgres";
    private static final String JDBC_USERNAME = "postgres";
    private static final String JDBC_PASSWORD = "postgres";

    public static Connection getConnection(String url, String username, String password)
            throws ClassNotFoundException, SQLException {
        Connection connection = null;
        try {
            Class.forName(JDBC_DRIVER);
            connection = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    public static void executeUpdate(Connection conn, String sql) throws SQLException {
        try (Statement stmt = conn.createStatement()) {
            stmt.executeUpdate(sql);
        }
    }

    public static List<List<Object>> executeQuery(Connection conn, String sql) throws SQLException {
        try (Statement stmt = conn.createStatement()) {
            stmt.setFetchSize(50);
            try (ResultSet rs = stmt.executeQuery(sql)) {
                return convertList(rs);
            }
        }
    }

    private static List<List<Object>> convertList(ResultSet rs) throws SQLException {
        List<List<Object>> list = new ArrayList<>();
        ResultSetMetaData md = rs.getMetaData();// 获取键名
        int columnCount = md.getColumnCount();// 获取行的数量
        List<Object> listColName = new ArrayList<>();
        for (int i = 1; i <= columnCount; i++) {
            listColName.add(md.getColumnName(i));
        }
        list.add(listColName);

        while (rs.next()) {
            List<Object> listRow = new ArrayList<>();
            for (int i = 1; i <= columnCount; i++) {
                listRow.add(rs.getObject(i));
            }
            list.add(listRow);
        }
        return list;
    }

    private static void printList(List<List<Object>> list) throws SQLException {
        for (List<Object> row : list) {
            for (Object cell : row) {
                System.out.print(cell);
                System.out.print("\t");
            }
            System.out.println();
        }
        System.out.println();
    }

    public static void testFullyACID(boolean commit) {
        try (Connection sessionA = getConnection(JDBC_URL1, JDBC_USERNAME, JDBC_PASSWORD);
                Connection sessionB = getConnection(JDBC_URL1, JDBC_USERNAME, JDBC_PASSWORD);
                Connection sessionC = getConnection(JDBC_URL2, JDBC_USERNAME, JDBC_PASSWORD);
                Connection sessionD = getConnection(JDBC_URL3, JDBC_USERNAME, JDBC_PASSWORD)) {

            sessionA.setAutoCommit(false);
            sessionB.setAutoCommit(false);
            sessionC.setAutoCommit(false);
            sessionD.setAutoCommit(false);

            String sql = "select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id";

            System.out.println(" 统计信息SQL:");
            System.out.println(sql);

            System.out.println("\n 更新前的统计信息");
            List<List<Object>> listA = executeQuery(sessionA, sql);
            printList(listA);

            String updatesql1 = "update t_student set class_id=class_id+1";
            String updatesql2 = "update t_grades set record=record+5 where grade_id between 20000 and 50000";
            System.out.println("\n sessionA 执行更新SQL:");
            System.out.println(updatesql1);
            System.out.println(updatesql2);
            executeUpdate(sessionA, updatesql1);
            executeUpdate(sessionA, updatesql2);

            System.out.println("\n 更新后(事务结束前)的统计信息:");
            listA = executeQuery(sessionA, sql);
            printList(listA);

            long commitTime = System.currentTimeMillis();
            System.out.println(" currentTimeMillis:" + commitTime);
            if (commit) {
                System.out.println(" sessionA commit ");
                sessionA.commit();
            } else {
                System.out.println(" sessionA rollback ");
                sessionA.rollback();
            }

            long currentTime = System.currentTimeMillis();
            System.out.println(" 通过 sessionD 获取的统计信息:currentTimeMillis:" + currentTime + ",time difference(ms):"
                    + (currentTime - commitTime));
            List<List<Object>> listD = executeQuery(sessionD, sql);

            currentTime = System.currentTimeMillis();
            System.out.println(" 通过 sessionC 获取的统计信息:currentTimeMillis:" + currentTime + ",time difference(ms):"
                    + (currentTime - commitTime));
            List<List<Object>> listC = executeQuery(sessionC, sql);

            currentTime = System.currentTimeMillis();
            System.out.println(" 通过 sessionB 获取的统计信息:currentTimeMillis:" + currentTime + ",time difference(ms):"
                    + (currentTime - commitTime));
            List<List<Object>> listB = executeQuery(sessionB, sql);

            currentTime = System.currentTimeMillis();
            System.out.println(" 通过 sessionA 获取的统计信息:currentTimeMillis:" + currentTime + ",time difference(ms):"
                    + (currentTime - commitTime));
            listA = executeQuery(sessionA, sql);

            System.out.println("\n sessionA 获取的统计信息:");
            printList(listA);

            System.out.println("\n sessionB 获取的统计信息:");
            printList(listB);

            System.out.println("\n sessionC 获取的统计信息:");
            printList(listC);

            System.out.println("\n sessionD 获取的统计信息:");
            printList(listD);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println("\n\n\n测试 rollback 的事务一致性");
        testFullyACID(false);

        System.out.println("\n\n\n测试 commit 的事务一致性");
        testFullyACID(true);
    }
}

  • 执行结果:
测试 rollback 的事务一致性
 统计信息SQL:
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id

 更新前的统计信息
class_id    sum max min avg 
31  52791.57    164.41  0.00    62.8471071428571429 
92  61404.44    154.07  0.10    64.6362526315789474 
163 62794.69    158.28  0.18    61.5634215686274510 
194 57102.65    162.49  0.03    63.4473888888888889 
235 55942.69    149.90  0.12    64.3019425287356322 
296 67018.50    160.36  0.09    65.0665048543689320 
337 58588.59    164.04  0.03    66.5779431818181818 
368 67174.81    162.97  0.06    62.1988981481481481 
439 60358.30    156.47  0.07    63.5350526315789474 
490 64538.23    164.50  0.07    65.1901313131313131 


 sessionA 执行更新SQL:
update t_student set class_id=class_id+1
update t_grades set record=record+5 where grade_id between 20000 and 50000

 更新后(事务结束前)的统计信息:
class_id    sum max min avg 
31  62520.97    168.37  0.31    63.7318756371049949 
92  73695.64    165.07  0.11    64.0831652173913043 
163 64017.59    149.84  0.03    64.6642323232323232 
194 66819.13    149.91  0.10    66.8191300000000000 
235 68856.35    149.91  0.07    66.8508252427184466 
296 64840.53    165.95  0.14    63.5691470588235294 
337 60443.11    168.30  0.05    64.9925913978494624 
368 69326.67    164.02  0.02    64.1913611111111111 
439 60284.25    167.12  0.29    62.7960937500000000 
490 57130.31    169.76  0.13    65.6670229885057471 

 currentTimeMillis:1544089230808
 sessionA rollback 
 通过 sessionD 获取的统计信息:currentTimeMillis:1544089230812,time difference(ms):4
 通过 sessionC 获取的统计信息:currentTimeMillis:1544089232288,time difference(ms):1480
 通过 sessionB 获取的统计信息:currentTimeMillis:1544089233218,time difference(ms):2410
 通过 sessionA 获取的统计信息:currentTimeMillis:1544089234922,time difference(ms):4114

 sessionA 获取的统计信息:
class_id    sum max min avg 
31  52791.57    164.41  0.00    62.8471071428571429 
92  61404.44    154.07  0.10    64.6362526315789474 
163 62794.69    158.28  0.18    61.5634215686274510 
194 57102.65    162.49  0.03    63.4473888888888889 
235 55942.69    149.90  0.12    64.3019425287356322 
296 67018.50    160.36  0.09    65.0665048543689320 
337 58588.59    164.04  0.03    66.5779431818181818 
368 67174.81    162.97  0.06    62.1988981481481481 
439 60358.30    156.47  0.07    63.5350526315789474 
490 64538.23    164.50  0.07    65.1901313131313131 


 sessionB 获取的统计信息:
class_id    sum max min avg 
31  52791.57    164.41  0.00    62.8471071428571429 
92  61404.44    154.07  0.10    64.6362526315789474 
163 62794.69    158.28  0.18    61.5634215686274510 
194 57102.65    162.49  0.03    63.4473888888888889 
235 55942.69    149.90  0.12    64.3019425287356322 
296 67018.50    160.36  0.09    65.0665048543689320 
337 58588.59    164.04  0.03    66.5779431818181818 
368 67174.81    162.97  0.06    62.1988981481481481 
439 60358.30    156.47  0.07    63.5350526315789474 
490 64538.23    164.50  0.07    65.1901313131313131 


 sessionC 获取的统计信息:
class_id    sum max min avg 
31  52791.57    164.41  0.00    62.8471071428571429 
92  61404.44    154.07  0.10    64.6362526315789474 
163 62794.69    158.28  0.18    61.5634215686274510 
194 57102.65    162.49  0.03    63.4473888888888889 
235 55942.69    149.90  0.12    64.3019425287356322 
296 67018.50    160.36  0.09    65.0665048543689320 
337 58588.59    164.04  0.03    66.5779431818181818 
368 67174.81    162.97  0.06    62.1988981481481481 
439 60358.30    156.47  0.07    63.5350526315789474 
490 64538.23    164.50  0.07    65.1901313131313131 


 sessionD 获取的统计信息:
class_id    sum max min avg 
31  52791.57    164.41  0.00    62.8471071428571429 
92  61404.44    154.07  0.10    64.6362526315789474 
163 62794.69    158.28  0.18    61.5634215686274510 
194 57102.65    162.49  0.03    63.4473888888888889 
235 55942.69    149.90  0.12    64.3019425287356322 
296 67018.50    160.36  0.09    65.0665048543689320 
337 58588.59    164.04  0.03    66.5779431818181818 
368 67174.81    162.97  0.06    62.1988981481481481 
439 60358.30    156.47  0.07    63.5350526315789474 
490 64538.23    164.50  0.07    65.1901313131313131 




测试 commit 的事务一致性
 统计信息SQL:
select class_id,sum(record),max(record),min(record),avg(record) from t_student s left join t_student_course sc on sc.student_id=s.student_id left join t_grades g on g.grade_id=sc.id where class_id in (31,92,163,194,235,296,337,368,439,490) group by s.class_id order by s.class_id

 更新前的统计信息
class_id    sum max min avg 
31  52791.57    164.41  0.00    62.8471071428571429 
92  61404.44    154.07  0.10    64.6362526315789474 
163 62794.69    158.28  0.18    61.5634215686274510 
194 57102.65    162.49  0.03    63.4473888888888889 
235 55942.69    149.90  0.12    64.3019425287356322 
296 67018.50    160.36  0.09    65.0665048543689320 
337 58588.59    164.04  0.03    66.5779431818181818 
368 67174.81    162.97  0.06    62.1988981481481481 
439 60358.30    156.47  0.07    63.5350526315789474 
490 64538.23    164.50  0.07    65.1901313131313131 


 sessionA 执行更新SQL:
update t_student set class_id=class_id+1
update t_grades set record=record+5 where grade_id between 20000 and 50000

 更新后(事务结束前)的统计信息:
class_id    sum max min avg 
31  62520.97    168.37  0.31    63.7318756371049949 
92  73695.64    165.07  0.11    64.0831652173913043 
163 64017.59    149.84  0.03    64.6642323232323232 
194 66819.13    149.91  0.10    66.8191300000000000 
235 68856.35    149.91  0.07    66.8508252427184466 
296 64840.53    165.95  0.14    63.5691470588235294 
337 60443.11    168.30  0.05    64.9925913978494624 
368 69326.67    164.02  0.02    64.1913611111111111 
439 60284.25    167.12  0.29    62.7960937500000000 
490 57130.31    169.76  0.13    65.6670229885057471 

 currentTimeMillis:1544089241513
 sessionA commit 
 通过 sessionD 获取的统计信息:currentTimeMillis:1544089241518,time difference(ms):5
 通过 sessionC 获取的统计信息:currentTimeMillis:1544089244709,time difference(ms):3196
 通过 sessionB 获取的统计信息:currentTimeMillis:1544089247823,time difference(ms):6310
 通过 sessionA 获取的统计信息:currentTimeMillis:1544089250748,time difference(ms):9235

 sessionA 获取的统计信息:
class_id    sum max min avg 
31  62520.97    168.37  0.31    63.7318756371049949 
92  73695.64    165.07  0.11    64.0831652173913043 
163 64017.59    149.84  0.03    64.6642323232323232 
194 66819.13    149.91  0.10    66.8191300000000000 
235 68856.35    149.91  0.07    66.8508252427184466 
296 64840.53    165.95  0.14    63.5691470588235294 
337 60443.11    168.30  0.05    64.9925913978494624 
368 69326.67    164.02  0.02    64.1913611111111111 
439 60284.25    167.12  0.29    62.7960937500000000 
490 57130.31    169.76  0.13    65.6670229885057471 


 sessionB 获取的统计信息:
class_id    sum max min avg 
31  62520.97    168.37  0.31    63.7318756371049949 
92  73695.64    165.07  0.11    64.0831652173913043 
163 64017.59    149.84  0.03    64.6642323232323232 
194 66819.13    149.91  0.10    66.8191300000000000 
235 68856.35    149.91  0.07    66.8508252427184466 
296 64840.53    165.95  0.14    63.5691470588235294 
337 60443.11    168.30  0.05    64.9925913978494624 
368 69326.67    164.02  0.02    64.1913611111111111 
439 60284.25    167.12  0.29    62.7960937500000000 
490 57130.31    169.76  0.13    65.6670229885057471 


 sessionC 获取的统计信息:
class_id    sum max min avg 
31  62520.97    168.37  0.31    63.7318756371049949 
92  73695.64    165.07  0.11    64.0831652173913043 
163 64017.59    149.84  0.03    64.6642323232323232 
194 66819.13    149.91  0.10    66.8191300000000000 
235 68856.35    149.91  0.07    66.8508252427184466 
296 64840.53    165.95  0.14    63.5691470588235294 
337 60443.11    168.30  0.05    64.9925913978494624 
368 69326.67    164.02  0.02    64.1913611111111111 
439 60284.25    167.12  0.29    62.7960937500000000 
490 57130.31    169.76  0.13    65.6670229885057471 


 sessionD 获取的统计信息:
class_id    sum max min avg 
31  62520.97    168.37  0.31    63.7318756371049949 
92  73695.64    165.07  0.11    64.0831652173913043 
163 64017.59    149.84  0.03    64.6642323232323232 
194 66819.13    149.91  0.10    66.8191300000000000 
235 68856.35    149.91  0.07    66.8508252427184466 
296 64840.53    165.95  0.14    63.5691470588235294 
337 60443.11    168.30  0.05    64.9925913978494624 
368 69326.67    164.02  0.02    64.1913611111111111 
439 60284.25    167.12  0.29    62.7960937500000000 
490 57130.31    169.76  0.13    65.6670229885057471 

这是事务更新t_student 的全部500条记录, t_grades中的部分记录(3W)。 看这次输出结果,无论是 rollback测试 还是 commit测试 中,sessionD 在 sessionA 操作rollback或者commit操作后3~5毫秒内查询数据的结果和最后sessionA的查询结果一致。

总结

此次更新通过多种方案,验证了【Fully ACID】特性在跨coordinator时也是有效的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容