非关系数据库之redis入门到实战(2)Redis常用命令

启动redis: 服务+配置文件

1
redis-server /alidata/server/redis-3.2.3/redis.conf

后台启动:

打开redis.conf将daemonize设置成yes

1
daemonize yes

关闭服务:

1
redis-cli shutdown

进入客户端:

1
redis-cli

redis数据类型:

string  hash  list  set  zset

 

一、String类型的命令

设置值:

set name xiaoming

1
2
127.0.0.1:6379> set name xiaoming
OK

 

获取值:

get name

1
2
127.0.0.1:6379> get name
"xiaoming"

如果值存在则设置不成功并返回0,不存在设置成功返回1:(nx : not exists)

setnx name xiaohua

1
2
3
4
5
6
127.0.0.1:6379> set name xiaohua
OK
127.0.0.1:6379> setnx name xiaohua
(integer) 0
127.0.0.1:6379> setnx age 18
(integer) 1

指定值的有效期,过期返回空nil:

setex name 10 xiaoming

setex 健名 时间 值

1
2
3
4
5
6
127.0.0.1:6379> setex name 10 xiaoming
OK
127.0.0.1:6379> get name
"xiaoming"
127.0.0.1:6379> get name
(nil)

替换指定位置字符串:

setrang name 6 hello

1
2
3
4
5
6
127.0.0.1:6379> set name xiaoming
OK
127.0.0.1:6379> setrange name 2 hehe
(integer) 8
127.0.0.1:6379> get name
"xiheheng"

截取字符串:

getrange name 0 4

1
2
127.0.0.1:6379> getrange name 0 3
"xiao"

批量设置值:

mset key1 val1 key2 val2 key3 val3...

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> mset name xiaoming age 18 sex girl
OK
127.0.0.1:6379> get name
"xiaoming"
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> get sex
"girl"
127.0.0.1:6379> get weight
(nil)

批量设置(nx),如果有一个值没设置成功全都设置不成功并返回0:

msetnx key1 val key2 val2...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> set name xiaoming
OK
127.0.0.1:6379> msetnx name xiaoming address hehe
(integer) 0
127.0.0.1:6379> get address
(nil)
 
 
127.0.0.1:6379> msetnx address hehe phone 12345667
(integer) 1
127.0.0.1:6379> get address
"hehe"
127.0.0.1:6379> get phone
"12345667"

批量获取值:

mget key1 key2...

1
2
3
4
127.0.0.1:6379> mget name age address
1) "xiaohuang"
2) "18"
3) "hehe"

获取旧值设置新值:

getset name xiaohuang

1
2
127.0.0.1:6379> getset name xiaohuang
"xiaoming"

对一个值递增,返回自增后的值:

incr age

1
2
3
4
5
6
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> incr age
(integer) 19
127.0.0.1:6379> get age
"19"

对一个值加指点值,如果不存在,则设置该值,并以原值为0来算:

incrby num 7

1
2
3
4
127.0.0.1:6379> incrby num 7
(integer) 7
127.0.0.1:6379> incrby num -2
(integer) 5

对一个值进行自减:

decr num

1
2
127.0.0.1:6379> decr num
(integer) 4

对一个值减去指定数值:

decrby num 2

1
2
127.0.0.1:6379> decrby num 2
(integer) 2

追加一个字符串:

append name xiaohua

1
2
3
4
127.0.0.1:6379> append name xiaohua
(integer) 15
127.0.0.1:6379> get name
"xiaomingxiaohua"

查看字符串长度:

strlen name

1
2
127.0.0.1:6379> strlen name
(integer) 15

二、hashes类型

user:001 此hash的user可以当做一张表的表名称,001可以当做表中的id

设置字段:

hset user:001 name xiaoming

代表用户名为001的用户叫xiaoming

user:001 为hash值

1
2
127.0.0.1:6379> hset user:001 name xiaoming
(integer) 1

获取字段:

hget user:001 name

1
2
127.0.0.1:6379> hget user:001 name
"xiaoming"

不存在创建值,存在不创建:

hsetnx user:002 name xiaohuang

1
2
3
4
127.0.0.1:6379> hsetnx user:002 name xiaohuang
(integer) 1
127.0.0.1:6379> hsetnx user:002 name xiaoming
(integer) 0

批量设置值:

hmset user:003 name xiaoming age 18

1
2
3
4
5
6
127.0.0.1:6379> hmset user:003 name xiaoming age 18
OK
127.0.0.1:6379> hget user:003 name
"xiaoming"
127.0.0.1:6379> hget user:003 age
"18"

批量获取值:

hmget user:003 name age

1
2
3
127.0.0.1:6379> hmget user:003 name age
1) "xiaoming"
2) "18"

自增:

hincrby user:003 age 2

1
2
127.0.0.1:6379> hincrby user:003 age 2
(integer) 20

查看值是否存在:

hexists user:003 name

1
2
127.0.0.1:6379> hexists user:003 name
(integer) 1

获取字段数量:

hlen user:003

hash为user:003的中设置字段的数量

1
2
127.0.0.1:6379> hlen user:003
(integer) 2

删除字段:

del user:003 age

可批量删除

1
2
127.0.0.1:6379> hdel user:003 age
(integer) 1

获取某个hash所有字段:

hkeys user:002

1
2
3
4
127.0.0.1:6379> hkeys user:002
1) "name"
2) "age"
3) "sex"

获取某个hash的所有值:

hvals user:002

1
2
3
4
127.0.0.1:6379> hvals user:002
1) "xiaoming"
2) "18"
3) "girl"

获取hash中所有的字段和值:

hgetall user:002 

1
2
3
4
5
6
7
127.0.0.1:6379> hgetall user:002
1) "name"
2) "xiaoming"
3) "age"
4) "18"
5) "sex"
6) "girl"

三、list类型

list类型可以作为栈(U)也可以作为队列(H)

从list头部压入数据:

lpush list1 hello

1
2
3
4
127.0.0.1:6379> lpush list1 hellow
(integer) 1
127.0.0.1:6379> lpush list1 ord
(integer) 2

获取所有数据:

lrange list1 0 -1

1
2
3
127.0.0.1:6379> lrange list1 0 -1
1) "ord"
2) "hellow"

从list的尾部压入元素:

rpush list2 hello

1
2
3
4
5
6
7
127.0.0.1:6379> rpush list2 hello
(integer) 1
127.0.0.1:6379> rpush list2 word
(integer) 2
127.0.0.1:6379> lrange list2 0 -1
1) "hello"
2) "word"

在某元素前压入元素:

linsert list3 before hello 123

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> lpush list3 hello
(integer) 1
127.0.0.1:6379> lpush list3 word
(integer) 2
127.0.0.1:6379> linsert list3 before hello 123
(integer) 3
127.0.0.1:6379> lrange list3 0 -1
1) "word"
2) "123"
3) "hello"

修改某个值:

lset list3 1 haha

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> lrange list3 0 -1
1) "word"
2) "123"
3) "hello"
127.0.0.1:6379> lset list3 1 haha
OK
127.0.0.1:6379> lrange list3 0 -1
1) "word"
2) "haha"
3) "hello"

批量删除相同的元素:

lrem list3 1 haha

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> lrange list3 0 -1
1) "word"
2) "haha"
3) "hello"
127.0.0.1:6379> lrem list3 1 haha
(integer) 1
127.0.0.1:6379> lrange list3 0 -1
1) "word"
2) "hello"

删除除某值以外的所有值:

ltrim list4 1 3

1 3 表示保留1到3的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> lpush list4 one
(integer) 1
127.0.0.1:6379> lpush list4 two
(integer) 2
127.0.0.1:6379> lpush list4 three
(integer) 3
127.0.0.1:6379> lpush list4 four
(integer) 4
127.0.0.1:6379> lrange list4 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> ltrim list4 1 3
OK
127.0.0.1:6379> lrange list4 0 -1
1) "three"
2) "two"
3) "one"

弹出第一个元素:

lpop list4

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> lrange list4 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lpop list4
"three"
127.0.0.1:6379> lrange list4 0 -1
1) "two"
2) "one"

弹出最后一个元素:

rpop list4

1
2
3
4
5
6
7
127.0.0.1:6379> lrange list4 0 -1
1) "two"
2) "one"
127.0.0.1:6379> rpop list4 
"one"
127.0.0.1:6379> lrange list4 0 -1
1) "two"

a部元素拿出压入到b的第一个:

rpoplpush list3 list 4

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> lrange list4 0 -1
1) "two"
127.0.0.1:6379> lrange list3 0 -1
1) "word"
2) "hello"
127.0.0.1:6379> rpoplpush list3 list4
"hello"
127.0.0.1:6379> lrange list3 0 -1
1) "word"
127.0.0.1:6379> lrange list4 0 -1
1) "hello"
2) "two"

获取指定索引位置的值:

lindex list4 1

1
2
3
4
5
127.0.0.1:6379> lrange list4 0 -1
1) "hello"
2) "two"
127.0.0.1:6379> lindex list4 1
"two"

统计链表元素个数同count():

llen list4

1
2
3
4
5
127.0.0.1:6379> lrange list4 0 -1
1) "hello"
2) "two"
127.0.0.1:6379> llen list4
(integer) 2

三、sets类型,无序集合类型

向集合中添加元素:

sadd myset one

1
2
3
4
5
6
127.0.0.1:6379> sadd myset one
(integer) 1
127.0.0.1:6379> sadd myset two
(integer) 1
127.0.0.1:6379> sadd myset two
(integer) 0

查看集合中的元素:

smembers myset

1
2
3
127.0.0.1:6379> smembers myset
1) "one"
2) "two"

删除集合中的值:

srem myset one

1
2
3
4
127.0.0.1:6379> srem myset one
(integer) 1
127.0.0.1:6379> smembers myset
1) "two"

随机弹出集合中的指定个数的元素:

spop myset 1

1表示个数,可以省略

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> smembers myset
1) "four"
2) "three"
3) "two"
127.0.0.1:6379> spop myset 1
1) "four"
127.0.0.1:6379> smembers myset
1) "three"
2) "two"

取出两个集合的差集:

sdiff myset myset1

如过myset放在前面,则取出myset集合中不相同的元素

如过myset1放在前面,则取出myset1集合中不相同的元素

谁在前则以谁为标准

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> smembers myset
1) "one"
2) "three"
127.0.0.1:6379> smembers myset1
1) "three"
2) "one"
3) "two"
127.0.0.1:6379> sdiff myset myset1
(empty list or set)
127.0.0.1:6379> sdiff myset1 myset
1) "two"

将取出的差集存储到另一个集合中:

sdiffstore myset2 myset1 myset

将myset1与myset的差集存入myset2中

1
2
127.0.0.1:6379> sdiffstore myset2 myset1 myset
(integer) 1

取出两个集合的交集:

sinter myset myset1

1
2
3
127.0.0.1:6379> sinter myset myset1
1) "one"
2) "three"

将两个集合的交集存储到另一个集合:

sinterstore myset3 myset myset1

1
2
3
4
5
127.0.0.1:6379> sinterstore myset3 myset myset1
(integer) 2
127.0.0.1:6379> smembers myset3
1) "three"
2) "one"

取出两个集合的并集:

sunion myset1 myset2

1
2
3
4
127.0.0.1:6379> sunion myset1 myset2
1) "one"
2) "three"
3) "two"

存储两个集合的并集:

sunionstore myset3 myset1 myset2

将myset1与myset2中的并集存入myset3

1
2
3
4
5
6
127.0.0.1:6379> sunionstore myset3 myset1 myset2
(integer) 3
127.0.0.1:6379> smembers myset3
1) "one"
2) "three"
3) "two"

将一个集合中指定的元素移动到另一个集合中:

smove myset1 myset2 two

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> smembers myset1
1) "three"
2) "one"
3) "two"
127.0.0.1:6379> smembers myset2
1) "two"
127.0.0.1:6379> smove myset1 myset2 two
(integer) 1
127.0.0.1:6379> smembers myset1
1) "three"
2) "one"

查看集合元素的个数:

scard myset

1
2
127.0.0.1:6379> scard myset1
(integer) 2

判断某一个元素是否为集合中的元素:

sismember myset one

1
2
3
4
5
127.0.0.1:6379> sismember myset one
(integer) 1
127.0.0.1:6379> smembers myset
1) "one"
2) "three"

随机返回集合中的一个元素:

srandmember myset

1
2
3
4
5
127.0.0.1:6379> smembers myset
1) "one"
2) "three"
127.0.0.1:6379> srandmember myset
"three"

sorted sets有序集合

添加集合元素:

zadd zset 1 one

1为排序值one为元素值

添加相同值可改顺序

1
2
3
4
5
6
7
8
127.0.0.1:6379> zadd zset 1 one
(integer) 1
127.0.0.1:6379> zadd zset 2 two
(integer) 1
127.0.0.1:6379> zadd zset 2 two
(integer) 0
127.0.0.1:6379> zadd zset 3 two
(integer) 0

查看集合元素并显示排序索引:

zrange zset 0 -1 withscores

1
2
3
4
5
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "one"
2) "1"
3) "two"
4) "3"

删除集合中的元素:

zrem zset one

1
2
3
4
5
127.0.0.1:6379> zrem zset one
(integer) 1
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "two"
2) "3"

调整顺序号:

zincrby zset 2 two

2为顺序号加二

1
2
3
4
5
6
7
8
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "two"
2) "3"
127.0.0.1:6379> zincrby zset 2 two
"5"
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "two"
2) "5"

返回一个元素的索引值,不是顺序号:

zrank zset three

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "one"
2) "1"
3) "three"
4) "2"
5) "two"
6) "5"
127.0.0.1:6379> zrank zset three
(integer) 1

返回一个元素的索引值,从大到小排序,不是顺序号:

zrevrank zset two

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> zrevrange zset 0 -1 withscores
1) "two"
2) "5"
3) "three"
4) "2"
5) "one"
6) "1"
127.0.0.1:6379> zrevrank zset two
(integer) 0

将元素降序排序:

zrevrange zset 0 -1 withscores

1
2
3
4
5
6
7
127.0.0.1:6379> zrevrange zset 0 -1 withscores
1) "two"
2) "5"
3) "three"
4) "2"
5) "one"
6) "1"

安排序号返回集合元素:

zrangebyscore zset 1 2 withscores

1
2
3
4
5
127.0.0.1:6379> zrangebyscore zset 1 2 withscores
1) "one"
2) "1"
3) "three"
4) "2"

返回指定排序号区间的数量:

zcount zset 1 2

返回排序号在1到2之间的元素个数

1
2
127.0.0.1:6379> zcount zset 1 2
(integer) 2

返回集合中元素的个数:

zcard zset

1
2
127.0.0.1:6379> zcard zset
(integer) 3

排序并删除指定索引下标区间元素:

zremrangebyrank zset 1 2

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "one"
2) "1"
3) "three"
4) "2"
5) "two"
6) "5"
127.0.0.1:6379> zremrangebyrank zset 1 2
(integer) 2
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "one"
2) "1"

排序并删除指定排序号区间元素:

zremrangebyscore zset 1 2

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
127.0.0.1:6379> zremrangebyscore zset 1 2
(integer) 2
127.0.0.1:6379> zrange zset 0 -1 withscores
1) "three"
2) "3"

redis健值相关命令

返回表达式匹配到的所有健名:

keys *    返回所有健名

keys my*    返回my开头的健名

1
2
3
4
5
6
7
8
127.0.0.1:6379> keys *
 1) "zset"
 2) "list4"
 3) "user:002"
 4) "myset1"
 5) "user:001"
 6) "age"
 7) "phone"

确认一个键是否存在:

exists myset

1
2
127.0.0.1:6379> exists myset
(integer) 1

删除一个键:

del list3

1
2
3
4
127.0.0.1:6379> del list3
(integer) 1
127.0.0.1:6379> exists list3
(integer) 0

设置一个键的过期时间:

expire name 10

查看一个健是否过期:

ttl name 

1
2
3
4
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) -2

选择数据库:

redis默认有0到15共16个数据库,默认选择为0数据库。

select 0

1
2
3
4
5
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]>

将当前数据库中的key转移到其他数据库中:

move age 1

将当前数据库中的age移动到数据库1中

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> keys *
 1) "age"
 2) "zset"
 3) "user:003"
 4) "phone"
127.0.0.1:6379> move age 1
(integer) 1
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "age"

移除过期时间:

persist age

1
2
3
4
5
6
7
8
127.0.0.1:6379[1]> expire age 300
(integer) 1
127.0.0.1:6379[1]> ttl age
(integer) 296
127.0.0.1:6379[1]> persist age
(integer) 1
127.0.0.1:6379[1]> ttl age
(integer) -1

随机返回一个键:

randomkey

1
2
127.0.0.1:6379> randomkey
"myset1"

重命名健名:

rename myset2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> keys *
 1) "zset"
 2) "user:003"
 3) "phone"
 4) "myset2"
 5) "address"
127.0.0.1:6379> rename myset2 hehe
OK
127.0.0.1:6379> keys *
 1) "zset"
 2) "user:003"
 3) "phone"
 4) "address"
 5) "hehe"

返回键的数据类型:

type name

1
2
3
4
5
6
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string
127.0.0.1:6379> type list1
list

测试连接是否存活:

ping

成功返回PONG,不成功返回不能连接到数据库的错误提示

1
2
127.0.0.1:6379> ping
PONG

输出任意字符串:

echo xiaoming

1
2
127.0.0.1:6379> echo xiaoming
"xiaoming"

退出连接:

quitexit

1
2
127.0.0.1:6379> quit
[root@iZ28c6xv2w0Z src]# ./redis-cli

返回当前数据库中所有key的数量:

dbsize

1
2
127.0.0.1:6379> dbsize 
(integer) 17

获取服务器的信息和统计:

info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
127.0.0.1:6379> info
# Server
redis_version:3.2.1
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:2d1ad902a50baa7d
redis_mode:standalone
os:Linux 2.6.32-431.23.3.el6.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:32118
run_id:e3ac0afc0226ecc741b93a41ca9d71bd6354778c
tcp_port:6379
uptime_in_seconds:2446
uptime_in_days:0
hz:10
lru_clock:10529106
executable:/alidata/server/redis-3.2.1/src/./redis-server
config_file:/alidata/server/redis-3.2.1/redis.conf
 
# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
 
# Memory
used_memory:824976
used_memory_human:805.64K
used_memory_rss:2453504
used_memory_rss_human:2.34M
used_memory_peak:824976
used_memory_peak_human:805.64K
total_system_memory:512483328
total_system_memory_human:488.74M
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:2.97
mem_allocator:jemalloc-4.0.3
 
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1470146254
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
 
# Stats
total_connections_received:2
total_commands_processed:28
instantaneous_ops_per_sec:0
total_net_input_bytes:695
total_net_output_bytes:11975284
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:5
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:214
migrate_cached_sockets:0
 
# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
 
# CPU
used_cpu_sys:1.57
used_cpu_user:0.94
used_cpu_sys_children:0.00
used_cpu_user_children:0.00
 
# Cluster
cluster_enabled:0
 
# Keyspace
db0:keys=17,expires=0,avg_ttl=0
db1:keys=1,expires=0,avg_ttl=0

实时转储收到的请求:

config get dir

1
2
3
127.0.0.1:6379> config get dir
1) "dir"
2) "/alidata/server/redis-3.2.1/src"

获取配置信息:

config get *

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> config get *
  1) "dbfilename"
  2) "dump.rdb"
  3) "requirepass"
  4) ""
  5) "masterauth"
  6) ""
  7) "unixsocket"
  8) ""
  9) "logfile"
 10) ""
 11) "pidfile"
 12) "/var/run/redis_6379.pid"
 13) "maxmemory"
 14) "0"
 15) "maxmemory-samples"
 16) "5"
 17) "timeout"
 18) "0"
 .
 .
 .

删除当前数据库中的所有key:

flushdb

1
2
3
4
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty list or set)

删除所有数据库中的所有健:

flushall

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
(empty list or set)
原文地址:https://www.cnblogs.com/huanghanyu/p/13808741.html