Hive学习一

vi命令:

ctrl + :切换到命令模式,输入 %s/a/b/g ,会将 a 替换成 b。

vi 编辑后文件,如果错误地使用 ctrl +zz 关闭编辑,下一次打开时会老是报错。这是因为这样并没有退出编辑文件,可以通过杀死进程来解决

注意直接杀死进程是不行的,因为会有一个临时文件

ps aux | grep vi (或者 ps -ef | grep vi) ,kill -9 6087 ,但是不行。

正确方法,删除 .xx.txt.swp 临时文件

ll 显示文件后,然后 rm .xx.txt.swp

删除全部内容:

在命令模式下 ,先使用 :0或:1 跳到文件第一行,然后输入:.,$d(表示删除当前光标后面的所有内容) 一回车就全没了

另::$ 跳到文件最后一行

hive 的基本使用介绍

1、hive 的参数设置方式

a、直接在 hive 窗口使用 set 去设置:只对当前连接的 session 有效

让提示符显示当前库,这样结果就会显示当前所在库名了。

设置 hve命令 打印当前的库

set hive.cli.print.current.db=true;

显示查询结果时显示字段名,不设置使用 select * from table ,只会有数据,不会有对应字段提示。

set hive.cli.print.header=true

[图片上传失败...(image-d503ab-1546140371812)]

注:上面两个参数设置都只是临时的,如果关闭 hive 将会失效

解决办法:

在 linux 的当前用户目录下(即~,cd 命令下),编辑一个 .hiverc 文件(隐藏文件,hive启动时会去加载这个文件),写入以下参数

set hive.cli.print.current.db=true;

set hive.cli.print.header=true;

b、配置文件设置:全局有效

用户自定义配置文件:$HIVE_CONF_DIR/hive-site.xml

默认设置文件:$HIVE_CONF_DIR/hive-default.xml

注:使用 vi ~/.bashrh 命令查看设置的各个全局变量,知道 hive、hadoop 的安装目录是在哪里

   使用 vi /etc/hosts 查看 ip 地址的别名

[图片上传失败...(image-a4b758-1546140371812)]

c、启动hive 窗口或者远程连接时指定:只当前session 有效

使用以下命令启动,设置显示当前库名,可以跟多条参数

hive -hiveconf hive.cli.print.current.db=true hive.cli.print.header=true

3、启动 hive 的另一种方式:把 hive 启动为一个服务(端口10000),这样就可以使用 hive客户端(beeline)在任意地方访问了。

启动服务端,注意这是阻塞式的,输入以下命令

/opt/hive2.1/bin/hiveserver2

注意:在后台运行的启动方式,1表示标准输出信息>重定向输出,2表示错误信息,最后一个&就是表示不阻塞后台运行,这里指向 /dec/null 是一个黑洞,放什么都会消失。加nohup 是为了不会因为当前用户退出而导致服务退出,而是会一直运行除非使用 kill -9 杀进程,jobs -l 命令可以查看进程号, 或者使用ps aux|grep file.name

nohup /opt/hive2.1/bin/hiveserver2 1>/dev/null 2>&1 &

可以使用命令查看 10000 端口,有看到10000端口这样就可以启动客户端

netstat -nltp

启动客户端,这是一个交互式程序

/opt/hive2.1/beeline

写命令连接到服务端,URI 符合jdbc规范,用户是启动 hdfs 的用户名

!connect jdbc:hive2://192.168.109.147:10000:用户名:密码

服务端连接成功后,就可以写 hive 命令了

select * from table

客户端关闭

!quit

总结:

使用这种方式连接会和 mysql 的显示一样很好看,服务端会显示客户端的执行结果

注意程序是在开启服务端的机器运行的,下面分区表中是从本地加载文件到 hdfs,如果文件是在客户端的,那么 load data local inpath 语句将会报错;'文件不存在'。此时就需要在文件所在机器直接用 hive 命令开启而不是用客户端连接了,这样就可以了。

4、hive 的脚本运行方式:不会进入hive 命令行

注意这个是 linux 命令,而不是在 hive 里面敲的,etl.sh 脚本如下

hive -e 'show databases;

select * from cookie4'

所以利用上面这个原理,就可以把很多命令都写成 hive -e 的形式,组成一个脚本 et1.sh,然后使用 sh et1.sh 命令执行 shell 脚本,可以把所有要执行的hive 语句都放到一个 hive -e "*****"; 里面, 使用hive -v -e “***” 会输出日志信息

[图片上传失败...(image-ef0edd-1546140371812)]

5、hive 命令的另一种执行方式:不会进入hive 命令行

适用于命令很长,用脚本也不不好写(一条语句几百行),就可以将命令写到一个 *.hql 文件(有; ,上面脚本的没有 ;),然后执行

hive -f test.hql

文件内容如下

select * from cookie5;

6、运行一个参数文件,从配置文件中启动 hive,并加载配置文件中的参数:会进入hive 命令行

inithive.conf 的内容如下,里面也可以放一下 hql 执行语句

use myhive;

set hive.cli.print.current.db=true;

一、hive的语句

查看当前所在库

select current_database();

模糊查找表名:包含lan 的表

show tables 'lan';

查看表信息

desc formatted table_name; 排列友好

desc extended table _name;

show create table table_name;

1、建库

建好库后,就会在 hdfs 中生成目录:/opt/hive2.1_data/warehouse/db_order.db

create database db_order;

建带属性的库:

create database db_name comment '库的属性:可选' dbproperties('creater'='lanyi','date'='2018-11-02')

删除库:

drop database db_lan; 空库

drop database db_lan cascade; 不是空库,库里面有表

2、建表

标准,包含 数组、结构体、字典的建表语句

create table cdt(

id int comment '识别码',

name string comment '姓名',

work_location array<string>,

piaofang map<string,bigint>,

address struct<location:string,zipcode:int,phone:string,value:int>)

row format delimited fields terminated by '\t'

collection items terminated by ','

map keys terminated by ':'

lines terminated by '\n' (默认就是以换行区分一条记录,可以省略)

stored as textfile; (默认文件格式)

使用 show create table cdt; 命令可以得到

表示是以\n 区分每行记录

ROW FORMAT SERDE

'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'

表示底层文件存储格式是 textFile

STORED AS INPUTFORMAT

'org.apache.hadoop.mapred.TextInputFormat'

OUTPUTFORMAT

'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'

注意:Hive 对文件中字段的分隔符默认情况下只支持单字节分隔符,如果数据文件中的分隔符是多 字符的,如下所示:

01||huangbo

02||xuzheng

03||wangbaoqiang

但是可以使用RegexSerDe 正则表达式来解析实现双字节分隔符

create table t_bi_reg(id string,name string)

row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe'

with serdeproperties('input.regex'='(.)\|\|(.)','output.format.string'='%1s %2s')

stored as textfile;

load data local inpath '/home/hadoop/data.txt' into table t_bi_reg;

表建好后,会在库目录下再建一个表目录, /opt/hive2.1_data/warehouse/db_order.db /t_order

注意,如果不写 row 后面的语句,hive 会认为表数据文件中的字段分隔符为 ^A,这是一个

create table t_order(id string,create_time string,amount float,uid string) row format delimited fields terminated by ',';

表建好后,可以尝试把数据放到 hdfs 下,数据要放到表目录中。如

建立一个表

create table t_movie(movie string,actor string) row format delimited fields terminated by ',';

数据文件为 movie.dat,内容如下(一行一条数据,以逗号分隔)

zhuilong,liudehua

tianyi,

sansheng,wangfei

把数据放到 hdfs 文件目录,注意是表目录下

hadoop fs -put ./movie.dat /opt/hive2.1_data/warehouse/db_order.db/t_movie

通过 子查询建表,会带有查询出来的数据

create table t_data

as

select *****;

注意:这种查询建表的方式生成的表字段名,会是查询结果的字段名,如果使用了 nvl(非空字段时取值,空字段时取值)这种不确定字段名的情况,就要使用 as 取别名,不然会又异常。

上面的注意情况,如果是查询数据插入到表中,则不用关注别名问题,因为不管取不取名字,插入表都是已经有字段名字了。

增加字段:

alter table t_date add columns (score int comment '得分',address string comment '地址')

增加分区:是增加分区,而不是增加分区字段

alter table t_date add partition(city = 'shenzhen');

alter table t_date add partition(city='ganzhou') partition(city='nankang');

3、删除表

hive 会从元数据库中清除关于这个表的信息,还会从 hdfs 中删除这个表的表目录

drop table t_order;。

删除表中数据,清空数据

truncate table tablename

删除库:

drop database db_lan; 空库

drop database db_lan cascade; 不是空库,库里面有表

删除分区,注意要有引号,因为分区字段定义是string

ALTER TABLE my_partition_test_table DROP IF EXISTS PARTITION (y=“2018 ”,m=“08”;

删除字段:不支持,但是可以使用 replace columns 替换字段来实现

删除分区:

alter table t_data drop partition (city='shenzhen');

3.1、改表

修改字段: 会将 name 字段变成 new_name sring类型,如果有注释仍然保留,不影响表数据

alter table t_data change name new_name string;

修改(替换)所有字段:会依次把字段替换掉,表数据保留并一 一对应,如果字段变少数据也变少,变多则为null。用来替换所有字段

alter table t_date replace columns (id int , name string , address string);

修改分区:只能修改分区存储的位置

此时原先的分区文件夹仍存在,但是在往分区添加数据时,只会添加到新的分区目录

alter table student_ptn partition (city='beijing') set location '/student_ptn_beijing';

添加分区时就指定分区的存储目录

alter table student_ptn add if not exists partition(city='beijing')

location '/student_ptn_beijing' partition(city='cc') location '/student_cc';

4、内部表 Managed_table 和外部表 External_table

建外部表的语法:不指定目录的就是 内部表

a、内部表的目录在 hive 的仓库目录中(/opt/hive2.1_data/warehouse ),外部表的目录由用户指定

b、drop 一个内部表时:hive 会清除相关元数据,并删除表数据目录(数据也没了)

c、drop 一个外表表时:hive 只会清除相关元数据(数据还在)

eg:

在 hdfs 下建立两级目录,所以带上了 -p,

hadoop fs -mkdir -p /pvlog/2017/

将造的日志数据 pv.log 放到 hdfs 新目录下(暴力导入数据),数据如下图

hadoop fs -put pv.log /pvlog/2017/

[图片上传失败...(image-9e70a0-1546140371812)]

建立外部表,将数据和表关联到一起(这样就不用将数据移动到表目录了)

create external table t_access( ip string,url string, access_time string ) row format delimited fields terminated by ',' location 'access/log';

此时查询数据,可得下图

select * from t_access

[图片上传失败...(image-899bbf-1546140371809)]

5、分区表

在表目录中为数据文件创建分区子目录,以便于在查询时,MR 程序可以针对分区子目录中的数据进行处理,缩减读取数据的范围(不然就要全部读取)。

比如:网站每天产生的浏览记录,浏览记录应该建一个表来存放,但是,有时候,我们可能只需要对某一天的浏览记录进行分析,这时,就可以将这个表建为分区表,每天的数据导入其中的一个分区,当然,每日的分区目录,应该有一个目录名(分区字段)。

partitioned by(day string)就是分区的依据

下面是建表语句,只会建表目录,分区的目录是在放数据的时候建立,先建表再放数据

create table t_pv_log(ip string,url string,comit_time string)partitioned by(day string)row format delimited fields terminated by ',';

创建两个日志数据文件,分别是 pv15.log,pv16.log 表示15、16两日的日志信息,然后导入数据。

将数据导入分区表,local表示从本地目录,partition 表示当前数据是哪个区的。

注意要保证运行此命令的机器与文件所在机器是同一台,不然会报文件不存在错误,此问题针对于服务器、客户端方式启动 hive 的情况。

load data local inpath '/home/python/Desktop/pv15.log' into table t_pv_log partition(day='0615');

导入数据后在表目录下会创建一个目录 day=0615,里面存放着 pv15.log 文件

也可以查询到数据了,结果如下图(多了一个字段)

select * from t_pv_log

[图片上传失败...(image-21279f-1546140371812)]

查询不同区的数据

select * from t_pv_log where day=‘0615’;

注意:这里各个区的数据文件名字是无关紧要的,不同区的名字也可以相同,只看用户是将什么数据导入了什么区去。

另外因为分区会多一个字段,所以要注意不能是表中已有字段名

查询分区

show partiticons tablename;

6、CTAS 建表语法

通过已存在的表来建表

创建一个和 table1 一样字段的 table2 表

create table table2 like table1

创建一个表类似与已有表,不仅字段一样而且还带有数据(好像不会有分区),查出来是什么字段名新表就是什么字段名

create table table2

as

select * from table1 where ip>‘192.168.109.2’;

7、将数据导入 hive 表

方法1:手动用 hdfs 命令,将文件放到表目录下

方法2:从linux本地导入 文件,在 hive 的交互式 shell 中用 hive 命令来导入本地数据(非hdfs)到表目录,本地文件还在,并且表目录下也会有一份数据

这是 hive 命令:注意表的文件类型是 textfile 才可以导入 txt等文本文件

load data local inpath ‘/root/order.data’into table t_order;

方法3:从hdfs 系统导入文件,用 hive 命令导入 hdfs 中的数据到表目录(命令没有 local)

注意从 hdfs 中导入文件,文件会被移动到表目录下

这是 hive 命令

load data inpath ‘/opt/order.data’ into table t_order;

方法4:直接插入数据,和mysql一样

[table] 可以省略,注意

insert into [table] tablename [partition(y=,m=,d=*)] values(字段1值,字段2值,...) ,(字段1值,字段2值,...) ,,,

insert into [table] tablename (字段1,字段2,...) values (字段1值,字段2值,...),,,

会重写数据

insert overwrite table table_name 【partition(y=2018’,m=08)】

select * from 。。。

会插入数据,注意插入语句要保证数据与字段是能匹配的,不然报错

insert into table table_name【partition(y=2018’,m=08) 】

select * from 。。。

视图:

和关系型数据库有差别

a、只有逻辑视图,没有物化视图

b、视图只能查询,不能load、insert、update、Delete数据

c、视图再创建的时候,只是保存了一份元数据,当查询视图的时候,才开始执行视图对应的那些子查询

不手动指定视图的字段信息

cretate view view_name as select * from cdt;

创建视图的时候加上表注释

create view view_name(name comment '姓名',age comment '年龄')

comment '这是视图注释,可省略'

as select * from cdt;

二、Hive 的关联查询

1、内连接 ,join 或者 inner join

没有连接条件,结果会是 t_a、t_b两个表的所有行两两相连接

这种就是叫 笛卡尔积

select a.* ,b.* from t_a a join t_b b ;

有连接条件的连接语句,求交集,是inner join

select a.* ,b.* from t_a a join t_b b on a.name=b.name;

[图片上传失败...(image-9ac445-1546140371811)]

2、左连接, left join 和 left outer join是一样的

以左边为基准,左边表数据都保留,右表如果没有符合连接条件的会显示 NULL

select a.* ,b.* from t_a a left join t_b b on a.name=b.name;

[图片上传失败...(image-faa969-1546140371811)]

右连接,类似

[图片上传失败...(image-bfbb26-1546140371811)]

3、全外连接,full outer join

左右表的数据都返回,哪个表没有就补充 NULL,不是join

select a.* ,b.* from t_a a full outer join t_b b on a.name=b.name;

[图片上传失败...(image-356c2f-1546140371811)]

4、左半连接,semi join

右表数据不返回,左表数据只返回连接上的,类似 inner join 结果的一半

注意 select 结果不能有 b 表的东西,因为查出来就没有结果

select a.* from t_a a semi join t_b b on a.name=b.name;

[图片上传失败...(image-1b6c58-1546140371811)]

5、lateral view 连接:(这个一般意义上不叫连接)

可以把表扩展一些新的字段,但是后面需要连接数组(hive里面的数据类型array)、列表(tuple,使用 json_tuple 函数处理json字典数据时的结果),表扩展的对应行数据扩展后会对应行拼接起来

json 是表名,data 字段存放的是json 数据

select * from json lateral view json_tuple(data,'movie','rate','timeStamp','uid') a as a1,a2,a3,a4;

[图片上传失败...(image-fc29d0-1546140371811)]

[图片上传失败...(image-d597b7-1546140371811)]

[图片上传失败...(image-c4531a-1546140371811)]

三、分组聚合

一些函数

hive 中使用 show functions 可以查看函数命令

1、大写函数

将 url 字段内容大写

select upper(url)from t_pv_log;

[图片上传失败...(image-8c52d6-1546140371811)]

2、计数、count

3、查看分区

show partitions t_table;

求每条 url 的访问总次数

分组的表达式是对分组的数据进行逐组运算

count(1)表示一行就加一个

select url ,count(1) from t_pv_log group by url;

group by 分组后排序,使用 order by

这个是错的:会报 chn_name 字段不存在,这是因为order by 是对查询出来后的结果进行排序,必须是查询出来有的字段名

select length(chn_name),count(1) from tmp_base_info group by length(chn_name) order by length(chn_name);

下面这个才是对的:

select length(chn_name) as chn,count(1) from tmp_base_info group by length(chn_name) order by chn;

注意:分组查询的结果,只有分组条件字段可以直接写,其他字段只能是聚合函数的结果

四、子查询

把查询结果当做是一个表,用括号包起来并取一个别名,然后去查询。

五、hive 的数据类型

普通数据类型:

1、tinyint、smallint、int、bigint、float、double

2、日期类型 timestamp(时间戳)、date

eg:

有一些数据

1,zhangsan,1985-09-08

2,lisi,1979-07-18

那么可以创建一个表,带 birthday date 字段的去接收这些数据

3、string、char(字符型)

4、boolean(值为true、false)、binary(二进制)

复合数据类型:

1、array 数组类型

eg:有一个数据的一部分是 ‘吴京:杨刚:王伟’,如果将保存这个数据的字段定义为 string,那么要求获取其中一部分不太好求(要根据冒号切片),但是如果保存为数组就方便很多了。

定义为数组,里面是string

并且指定集合字段按照‘:’来切分

create table t_movie(movie_name string, actors array<string>,first_show date)

row format delimited fields terminated by ','

collection items terminated by ‘:’;

[图片上传失败...(image-95253e-1546140371811)]

只取数组中一部分数据,如这里用角标取出第一个演员名字

[图片上传失败...(image-fbe77-1546140371811)]

配合函数可以实现更多功能:例如array_contains 查询包含某一演员的电影

select movie_name,actors,first_show

from t_movie

where array_contains(actors,'吴刚');

求电影有几个演员

select movie_name,actors,first_show ,size(actors)

from t_movie;

2、map 类型

eg:有一些数据如下,第二个字段是表示其家庭成员(可以 key 不一致)

1,zhangsan,father:xiaoming#mother:xiaohua#brother:xiaosu,28

2,lisi,father:mayun#mother:huangyi#sister:guanwu,22

那么可以使用一个 map 类型来对上面数据中的家庭成员进行描述

建表语句,map 里面放的是两个 string

并且分别告诉字段怎么切,元素怎切,key values 怎么切

create table t_person(id int,name string,family_members map<string,string>,age int)

row format delimited fields terminated by ','

collection items terminated by '#'

map keys terminated by ':';

查询结果

select * from t_person

[图片上传失败...(image-7d2048-1546140371811)]

查询每个人的爸爸,使用字典的取值方式

select id,name,family_members【'father'】,age

from t_person;

[图片上传失败...(image-7af6fc-1546140371811)]

查出每个人有哪些亲属,只需要取 map 的 key

select id,name,map_keys(family_members)as relations

from t_family;

[图片上传失败...(image-edab34-1546140371811)]

查出每个人亲属的名字,只需要取 values

select id,name,map_values(family_members)as relations

from t_family;

[图片上传失败...(image-dc098f-1546140371811)]

查出每个人的亲人数量

select id,name,size(family_members)as relations

from t_family;

查出所有拥有兄弟的人

这里用了函数的嵌套,使用了数组判断元素的函数

select id,name,age from t_person

where array_contains(map_keys(family_members),‘brother’);

或者用子查询来实现

select id,name,age

from (select id,name,age,map_keys(family_members)as relations)tmp

where array_contains(relations,‘brother’);

[图片上传失败...(image-875979-1546140371811)]

3、struct 结构体类型:

是类与 对象的结构,就是 c 语言里面的对象,因此用点语法

eg:有以下数据

1,zhansan,18:male:深圳

2,lisi,28:female:北京

3,wangwu,38:male:广州

4,赵六,26:male:上海

5,钱琪,35:male:杭州

6,王八,48:female:南京

建表映射上面的数据,因为数据结合在一起所以用结构类型来表示

防止表存在,存在就删除,不存在不会影响

drop table [if exists] t_user

create table t_user(id int,name string,

info struct<age:int,sex:string,addr:string>)

row format delimited fields terminated by ','

collection items terminated by ':';(导入数据的时候结构体用:来分隔)

[图片上传失败...(image-a3d960-1546140371810)]

查询每个人的 id,地址,取数据用点语法

select id,name,info.addr from t_user;

[图片上传失败...(image-83696a-1546140371810)]

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

推荐阅读更多精彩内容