Oracle触发器实现迁移历史数据时的数据审计

项目背景


生产环境下,将目标生产表TM_DO_STATUS中2019年以前的数据迁移至历史表,保留2019年的数据。目标表中包含2014年至今的数据,数据量约10G,若采用先备走历史数据再delete的方式,delete的数据量较大,非常慢,对数据库性能影响较大,并且不能保证业务数据的一致性(如果在迁移过程修改了2019年以前的数据行)。另外,Oracle表在delete操作后还需要回收空间才能降低高水位线,回收空间的操作也是非常慢的。

优化方案


将目标原生产表2019年以后的数据写入新表,同时利用触发器审计写入新表期间的数据变更,写入新表完成后,将原生产表重命名为历史表,以防应用更改数据,然后将审计的变更数据(增删改操作)同步至新表,最后将新表重命名为生产表。这样,整个过程中表数据不可用的时间仅为同步变更数据的时间,同步时间很短。

实施细节


准备阶段

原生产表结构

SQL> desc tm_do_status

名称                   空值?      类型            
-------------------- -------- ------------- 
UUID                 NOT NULL VARCHAR2(32)  
DO_ID                         VARCHAR2(60)  
ACCEPT_TIME                   DATE          
NAME                          VARCHAR2(300) 
REMARK                        VARCHAR2(500) 
STATUS                        VARCHAR2(20)  
SCAN_FLAG                     VARCHAR2(4)   
ORDER_NO                      VARCHAR2(250) 
MEMO                          VARCHAR2(450) 
FACTORY_ID                    VARCHAR2(60)  
LOCATION                      VARCHAR2(100) 
ERRORCOUNT                    NUMBER        
STATUS_REASON                 VARCHAR2(10)  
CREATE_DATE                   DATE          
CREATOR                       VARCHAR2(30)  
LAST_MODIFY_TIME              DATE          
LAST_MODIFIER                 VARCHAR2(30)  
TRACKINFO_UUID                VARCHAR2(32)  
ABNORMAL_DESCRIPTION          VARCHAR2(900) 
ABNORMAL_TYPE_ID              VARCHAR2(10)  
TRANS_ID                      VARCHAR2(20)  

创建审计数据表

create table aud_tm_do_status(
old_UUID                  VARCHAR2(32)   ,
old_DO_ID                 VARCHAR2(60)   ,
old_ACCEPT_TIME           DATE           ,
old_NAME                  VARCHAR2(300)  ,
old_REMARK                VARCHAR2(500)  ,
old_STATUS                VARCHAR2(20)   ,
old_SCAN_FLAG             VARCHAR2(4)    ,
old_ORDER_NO              VARCHAR2(250)  ,
old_MEMO                  VARCHAR2(450)  ,
old_FACTORY_ID            VARCHAR2(60)   ,
old_LOCATION              VARCHAR2(100)  ,
old_ERRORCOUNT            NUMBER         ,
old_STATUS_REASON         VARCHAR2(10)   ,
old_CREATE_DATE           DATE           ,
old_CREATOR               VARCHAR2(30)   ,
old_LAST_MODIFY_TIME      DATE           ,
old_LAST_MODIFIER         VARCHAR2(30)   ,
old_TRACKINFO_UUID        VARCHAR2(32)   ,
old_ABNORMAL_DESCRIPTION  VARCHAR2(900)  ,
old_ABNORMAL_TYPE_ID      VARCHAR2(10)   ,
old_TRANS_ID              VARCHAR2(20)   ,
new_UUID                  VARCHAR2(32)   ,
new_DO_ID                 VARCHAR2(60)   ,
new_ACCEPT_TIME           DATE           ,
new_NAME                  VARCHAR2(300)  ,
new_REMARK                VARCHAR2(500)  ,
new_STATUS                VARCHAR2(20)   ,
new_SCAN_FLAG             VARCHAR2(4)    ,
new_ORDER_NO              VARCHAR2(250)  ,
new_MEMO                  VARCHAR2(450)  ,
new_FACTORY_ID            VARCHAR2(60)   ,
new_LOCATION              VARCHAR2(100)  ,
new_ERRORCOUNT            NUMBER         ,
new_STATUS_REASON         VARCHAR2(10)   ,
new_CREATE_DATE           DATE           ,
new_CREATOR               VARCHAR2(30)   ,
new_LAST_MODIFY_TIME      DATE           ,
new_LAST_MODIFIER         VARCHAR2(30)   ,
new_TRACKINFO_UUID        VARCHAR2(32)   ,
new_ABNORMAL_DESCRIPTION  VARCHAR2(900)  ,
new_ABNORMAL_TYPE_ID      VARCHAR2(10)   ,
new_TRANS_ID              VARCHAR2(20)   ,
systemdate                TIMESTAMP       
);

创建审计触发器

create or replace trigger tri_aud_tm_do_status
after insert or update or delete on tm_do_status 
for each row
begin
INSERT INTO aud_tm_do_status
  VALUES (
:OLD.UUID                 ,
:OLD.DO_ID                ,
:OLD.ACCEPT_TIME          ,
:OLD.NAME                 ,
:OLD.REMARK               ,
:OLD.STATUS               ,
:OLD.SCAN_FLAG            ,
:OLD.ORDER_NO             ,
:OLD.MEMO                 ,
:OLD.FACTORY_ID           ,
:OLD.LOCATION             ,
:OLD.ERRORCOUNT           ,
:OLD.STATUS_REASON        ,
:OLD.CREATE_DATE          ,
:OLD.CREATOR              ,
:OLD.LAST_MODIFY_TIME     ,
:OLD.LAST_MODIFIER        ,
:OLD.TRACKINFO_UUID       ,
:OLD.ABNORMAL_DESCRIPTION ,
:OLD.ABNORMAL_TYPE_ID     ,
:OLD.TRANS_ID             ,
:NEW.UUID                 ,
:NEW.DO_ID                ,
:NEW.ACCEPT_TIME          ,
:NEW.NAME                 ,
:NEW.REMARK               ,
:NEW.STATUS               ,
:NEW.SCAN_FLAG            ,
:NEW.ORDER_NO             ,
:NEW.MEMO                 ,
:NEW.FACTORY_ID           ,
:NEW.LOCATION             ,
:NEW.ERRORCOUNT           ,
:NEW.STATUS_REASON        ,
:NEW.CREATE_DATE          ,
:NEW.CREATOR              ,
:NEW.LAST_MODIFY_TIME     ,
:NEW.LAST_MODIFIER        ,
:NEW.TRACKINFO_UUID       ,
:NEW.ABNORMAL_DESCRIPTION ,
:NEW.ABNORMAL_TYPE_ID     ,
:NEW.TRANS_ID             ,
sysdate                 
  );
end;
/

--开始写入新生产表时才开启触发器

alter trigger tri_aud_tm_do_status disable;

truncate table aud_tm_do_status;

TM_DO_STATUS建表语句

CREATE TABLE "TMSNEW"."TM_DO_STATUS" 
   (    "UUID" VARCHAR2(32 BYTE) NOT NULL ENABLE, 
    "DO_ID" VARCHAR2(60 BYTE), 
    "ACCEPT_TIME" DATE, 
    "NAME" VARCHAR2(300 BYTE), 
    "REMARK" VARCHAR2(500 BYTE), 
    "STATUS" VARCHAR2(20 BYTE), 
    "SCAN_FLAG" VARCHAR2(4 BYTE), 
    "ORDER_NO" VARCHAR2(250 BYTE), 
    "MEMO" VARCHAR2(450 BYTE), 
    "FACTORY_ID" VARCHAR2(60 BYTE), 
    "LOCATION" VARCHAR2(100 BYTE), 
    "ERRORCOUNT" NUMBER DEFAULT 0, 
    "STATUS_REASON" VARCHAR2(10 BYTE), 
    "CREATE_DATE" DATE, 
    "CREATOR" VARCHAR2(30 BYTE), 
    "LAST_MODIFY_TIME" DATE, 
    "LAST_MODIFIER" VARCHAR2(30 BYTE), 
    "TRACKINFO_UUID" VARCHAR2(32 BYTE), 
    "ABNORMAL_DESCRIPTION" VARCHAR2(900 BYTE), 
    "ABNORMAL_TYPE_ID" VARCHAR2(10 BYTE), 
    "TRANS_ID" VARCHAR2(20 BYTE), 
     CONSTRAINT "PK_TM_DO_STATUS" PRIMARY KEY ("UUID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "INDX"  ENABLE
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TMSNEW"  ENABLE ROW MOVEMENT ;

  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_CRDATE" ON "TMSNEW"."TM_DO_STATUS" ("CREATE_DATE") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TMSNEW" ;
 

  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_FACID" ON "TMSNEW"."TM_DO_STATUS" ("FACTORY_ID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TMSNEW" ;
 

  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_ORDERNO" ON "TMSNEW"."TM_DO_STATUS" ("ORDER_NO") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TMSNEW" ;
 

  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_SCANFLAG" ON "TMSNEW"."TM_DO_STATUS" ("SCAN_FLAG") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TMSNEW" ;
 

  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_STATUS" ON "TMSNEW"."TM_DO_STATUS" ("STATUS") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TMSNEW" ;
 

  CREATE INDEX "TMSNEW"."IDX_TM_DO_ST_STATUS" ON "TMSNEW"."TM_DO_STATUS" ("DO_ID", "STATUS") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TMSNEW" ;

按照TM_DO_STATUS表的结构创建新生产表TM_DO_STATUS_TMP


CREATE TABLE "TMSNEW"."TM_DO_STATUS_TMP"                                                                                
   (    "UUID" VARCHAR2(32 BYTE) NOT NULL ENABLE,                                                                     
    "DO_ID" VARCHAR2(60 BYTE),                                                                                        
    "ACCEPT_TIME" DATE,                                                                                               
    "NAME" VARCHAR2(300 BYTE),                                                                                        
    "REMARK" VARCHAR2(500 BYTE),                                                                                      
    "STATUS" VARCHAR2(20 BYTE),                                                                                       
    "SCAN_FLAG" VARCHAR2(4 BYTE),                                                                                     
    "ORDER_NO" VARCHAR2(250 BYTE),                                                                                    
    "MEMO" VARCHAR2(450 BYTE),                                                                                        
    "FACTORY_ID" VARCHAR2(60 BYTE),                                                                                   
    "LOCATION" VARCHAR2(100 BYTE),                                                                                    
    "ERRORCOUNT" NUMBER DEFAULT 0,                                                                                    
    "STATUS_REASON" VARCHAR2(10 BYTE),                                                                                
    "CREATE_DATE" DATE,                                                                                               
    "CREATOR" VARCHAR2(30 BYTE),                                                                                      
    "LAST_MODIFY_TIME" DATE,                                                                                          
    "LAST_MODIFIER" VARCHAR2(30 BYTE),                                                                                
    "TRACKINFO_UUID" VARCHAR2(32 BYTE),                                                                               
    "ABNORMAL_DESCRIPTION" VARCHAR2(900 BYTE),                                                                        
    "ABNORMAL_TYPE_ID" VARCHAR2(10 BYTE),                                                                             
    "TRANS_ID" VARCHAR2(20 BYTE),                                                                                     
     CONSTRAINT "PK_TM_DO_STATUS_TMP" PRIMARY KEY ("UUID")                                                                
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS                                                 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "INDX"  ENABLE                                                                                         
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING                                               
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "TMSNEW"  ENABLE ROW MOVEMENT ;                                                                        
                                                                                                     
  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_TMP_CRDATE" ON "TMSNEW"."TM_DO_STATUS_TMP" ("CREATE_DATE")                        
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS                                                             
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "TMSNEW" ;                                                                                             
                                                                                                                    
                                                                                                                    
  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_TMP_FACID" ON "TMSNEW"."TM_DO_STATUS_TMP" ("FACTORY_ID")                          
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS                                                             
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "TMSNEW" ;                                                                                             
                                                                                                                    
                                                                                                                    
  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_TMP_ORDERNO" ON "TMSNEW"."TM_DO_STATUS_TMP" ("ORDER_NO")                          
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS                                                             
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "TMSNEW" ;                                                                                             
                                                                                                                    
                                                                                                                    
  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_TMP_SCANFLAG" ON "TMSNEW"."TM_DO_STATUS_TMP" ("SCAN_FLAG")                        
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS                                                             
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "TMSNEW" ;                                                                                             
                                                                                                                    
                                                                                                                    
  CREATE INDEX "TMSNEW"."IDX_TM_DO_STATUS_TMP_STATUS" ON "TMSNEW"."TM_DO_STATUS_TMP" ("STATUS")                             
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS                                                             
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "TMSNEW" ;                                                                                             
                                                                                                                    
                                                                                                                    
  CREATE INDEX "TMSNEW"."IDX_TM_DO_ST_STATUS_TMP" ON "TMSNEW"."TM_DO_STATUS_TMP" ("DO_ID", "STATUS")                        
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS                                                             
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645                                             
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)                                                  
  TABLESPACE "TMSNEW" ;                                                                                             
迁移阶段

开启触发器,并立刻开始写入新表操作。

alter trigger aud_tm_do_status enable;
insert into tm_do_status_tmp select * from tm_do_status where CREATE_DATE>to_date('2019-01-01','yyyy-mm-dd');

将原生产表重命名,然后开始同步变更数据

alter table tm_do_status rename to tm_do_status_his;

--删除old_uuid非空的,这些行被删除或更新过

delete from tm_do_status_tmp where uuid in 
(select old_uuid from aud_tm_do_status where create_date>to_date('2019-01-01','yyyy-mm-dd'));

--通过new_uuid反查原表,同步更新过的或新写入的行(new_uuid非空)

insert into tm_do_status_tmp select * from tm_do_status where uuid in
 (select new_uuid from aud_tm_do_status where create_date>to_date('2019-01-01','yyyy-mm-dd'));

--将tmp表重命名为新的生产表

alter table tm_do_status_tmp rename to tm_do_status;

总结:


经测试,整个迁移过程,应用对生产表不可访问时间不到一秒,下面总结一下迁移过程需要执行的SQL脚本(假设触发器和审计表结构以及新表结构均已准备完成)

--清空审计表
truncate table aud_tm_do_status;
--开启触发器
alter trigger tri_aud_tm_do_status enable;
--立刻开始传输数据
insert into tm_do_status_tmp select * from tm_do_status where CREATE_DATE>to_date('2019-01-01','yyyy-mm-dd');
commit;
--重命名生产表
alter table tm_do_status rename to tm_do_status_his;
--同步变更数据
delete from tm_do_status_tmp where uuid in (select old_uuid from aud_tm_do_status where create_date>to_date('2019-01-01','yyyy-mm-dd'));
insert into tm_do_status_tmp select * from tm_do_status where uuid in (select new_uuid from aud_tm_do_status where create_date>to_date('2019-01-01','yyyy-mm-dd'));
commit;
--重命名新生产表
alter table tm_do_status_tmp rename to tm_do_status;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容