redis基础
简介
redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
redis与其他key-value缓存数据库产品有以下三个特点:
- Redis支持数据库的持久化,可以将内存中的数据保存到磁盘中,重启时可以再次加载使用
- Redis不仅支持简单的k-v数据类型,还提供了list,set,zset,hash等数据结构的存储
- Redis支持数据的备份,即master-slave模式的备份
Redis优势
- 性能极高-redis能读的速度是110000次/s,写的次数是81000次/s。
- 丰富的数据类型-redis支持二进制案例的Strings,Lists,Hashes,Sets及Ordered Sets数据类型的操作。
- 原子-redis的所有操作都是原子性的,要么成功执行要么失败完全不执行。单个操作是原子性的,多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性-Redis还支持publish/subscribe,通知,key过期等特性。
Redis与其他key-value存储有什么不同
- Redis有着更为复杂的数据结构并提供对它们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同是对程序员透明,无需进行额外的抽象。
- Redis运行在内存中但可以持久化到磁盘,所以在对不同数据集进行高速读写时,需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加方式产生的,因为他们并不需要进行随机的访问。
安装
windows安装
下载地址:<https://github.com/MSOpenTech/redis/releases
redis支持32位和64位,根据系统平台的实际情况选择。下载解压后,将文件夹重命名位redis.
打开一个cmd窗口,使用cd命令切换目录到安装路径运行:
redis-server.exe redis.window.conf
如果想方便 的话,可以吧redis的路径加到系统的环境变量里,这样就省得再输路径了。后面的那个redis.windows.conf可以省略,如果省略会启动默认的。
另启动一个cmd窗口,原来的不要关闭,否则无法访问服务端。切换到redis目录下运行:
redis-cli.exe -h 127.0.0.1 -p 6379
设置键值对:
set mykey abd
取出键值对:
get mykey
linux下安装
下载地址:http://redis.io/download 下载最新稳定版本。
教程使用版本为2.8.17,下载并安装:
$ wget http://download.redis.io/releases/redis-2.8.17.tar.gz
$ tar xzf redis-2.8.17.tar.gz
$ cd redis-2.8.17
$ make
make完后redis-2.8.17目录下出现编译后的redis服务程序redis-server,还有用于测试的客户端程序redis-cli,两个程序位于安装目录src目录下。启动redis服务:
$ cd src
$ ./redis-server
注意这种方式启动redis使用的是默认配置,也可以通过启动参数告诉redis使用指定配置文件启动:
$ cd src
$ ./redis-server ../redis.conf
redis.conf是一个默认的配置文件,可以根据需要使用自己的配置文件。启动redis服务进程后,就可以使用测试客户端程序redis-cli和rediis服务交互了:
$ cd src
$ ./redis-cli
redis> set foo bar
OK
redis> get foo
"bar"
ubuntu下安装
在Ubuntu系统安装redis:
$ sudo apt-get update
$ sudo apt-get install redis-server
启动redis
$ redis-server
查看redis是否启动?
$ redis-cli
以上命令将打开一下终端:
redis 127.0.0.1:6379>
127.0.0.1是本机IP,6379是redis服务端口,现在我们输入PING命令:
redis 127.0.0.1:6379>ping
PONG
以上说明我们已经成功安装redis.
在cloud studio中运行redis
如何在cloud studio中安装、使用redis:
step1:访问
注册/登录账户。
step2:在右侧的运行环境菜单选择:Ubuntu
step3:在终端执行命令
sudo apt-get update
sudo apt-get install redis-server
step4:启动redis
redis-server
step5:查看redis是否启动
redis-cli
同Ubuntu系统安装环境的启动验证
redis配置
redis的配置文件位于redis安装目录下,文件名为redis.conf(windows名为redis.windows.conf),可以通过CONFIG命令查看或设置配置项。
语法
redis CONFIG命令格式如下:
redis 127.0.0.1:6379>CONFIG GET CONFIG_SETTING_NNAME
实例
redis 127.0.0.1:6379>CONFIG GET loglevel
1)"loglevel"
2)"notice"
使用*获取所有配置项
编辑配置
可以通过修改redis.conf文件或使用CONFIG set命令来修改配置
语法
CONFIG SET命令基本语法:
redis 127.0.0.1:6379>CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE
实例
redis 127.0.0.1:6379>CONFIG SET loglevel "notice"
OK
redis 127.0.0.1:6379>CONFIG GET loglevel
1)"loglevel"
2)"notice"
参数说明
redis.conf配置项说明如下:
序号 | 配置项 | 说明 |
---|---|---|
1 | daemonize | redis默认不是以守护进程 的方式运行,可通过该配置项修改,使用yes启用守护进程(windows不支持守护线程的配置为no) |
2 | pidfile /var/run/redis.pid | 当redis以守护进程方式运行时,redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定 |
3 | port 6379 | 指定redis监听端口,默认端口为6379(为什么选用6379作为默认端口?因为6397在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字) |
4 | bind 127.0.0.1 | 绑定的主机地址 |
5 | timeout 300 | 当客户端闲置多长时间后关闭连接,如果指定0,表示关闭该功能 |
6 | loglevel notice | 指定日志记录级别,redis总共支持四个级别:debug,verbose,notice,warning,默认为notice |
7 | logfile stdout | 日志记录方式,默认为标准输出,如果配置redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null/ |
8 | database 16 | 设置数据库的数量,默认数据库为0,可以使用SELECT命令在连接上指定数据库id |
9 | save <seconds> <changes> | 指定多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合<br />redis默认提供了三个条件:save 900 1、save 300 10、save 60 10000 分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改、60秒内有10000个更改 |
10 | rdbcompression yes | 指定存储至本地数据库时是否压缩数据,默认为yes,redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变得巨大 |
11 | dbfilename dump.rdb | 指定本地数据库文件名,默认为dump.rdb |
12 | dir ./ | 指定本地数据库存放目录 |
13 | slaveof <masterip> <masterport> | 设置当本机为slav服务时,设置master服务的IP地址及端口,在redis启动时,它会自动从master进行数据同步 |
14 | masterauth <master-password> | 当master服务设置了密码保护时,slave服务连接master的密码 |
15 | requirepass foobared | 设置redis连接密码,如果配置了连接密码,客户端在连接redis时需要通过AUTH <password> 命令提供密码,默认关闭 |
16 | maxclients 128 | 设置同一时间最大客户端连接数,默认无限制,redis可以同时打开的客户端连接数为redis进程可以打开的最大文件描述符数,如果设置maxclients 0,表示不作限制。当客户端连接数达到限制时,redis会关闭新的的连接并向客户端返回max number of clients reached错误信息 |
17 | maxmemory <bytes> | 指定redis最大内存限制,redis在启动时会把数据加载到内存中,达到最大内存后,redis会先尝试清除已到期或即将到期的key,当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。redis新的vm机制,会把key存放内存,value会存放在swap区 |
18 | appendonly no | 指定是否在每次更新操作后进行日志记录,redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电是导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会子啊一段时间内只存在于内存中,默认为no. |
19 | appendfilename appendonly.aof | 指定更新日志文件名,默认为appendonly.aof |
20 | appendfsync everysec | 指定更新日志条件,共有3个可选值:no:表示等操作系统进行数据缓存同步到磁盘(快) always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) everysec:表示美妙同步一次(折中,默认值) |
21 | vm-enabled no | 指定是否启用虚拟内存机制,默认值为no,简单的介绍下,VM机制将数据分页存放,由redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章中会仔细分析redis的VM机制) |
22 | vm-swap-file /tmp/redis.swap | 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个redis实例共享 |
23 | vm-max-memory 0 | 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(redis的索引数据就是keys),也就是说,当vm-max-memory设置为0时,其实是所有的value都存于磁盘,默认值为0. |
24 | vm-page-size 32 | redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大对象则可以使用更大的page,如果不确定,就使用默认值。 |
25 | vm-pages 134217728 | 设置swap文件的page数量,由于页表(一种表示页面空闲或使用的bitmap)是放在内存中的,在磁盘上每8个pages将消耗1byte的内存。 |
26 | vm-max-threads 4 | 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4 |
27 | glueoutputbuf yes | 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启。 |
28 | hash-max-zipmap-entries 64 hash-max-zipmap-value 512 | 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法。 |
29 | activerehashing yes | 指定是否激活重置hash,默认为开启(后面在介绍redis的hash算法时具体介绍) |
30 | include /path/to/local.conf | 指定包含其他的配置文件,可以在同一主机上多个redis实例之间使用同一份配置文件,而同时各个实例又引用由自己的特定配置文件。 |
redis数据类型
reids支持五种数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)及zset(sorted set:有序集合)
String(字符串)
string是redis最基本的类型,可以理解成与Memcahed一模一样的类型,一个key对应一个value。string类型是二进制安全的。意思是redis的string可以包含任何数据。比如JPG图片或者序列化的对象。string类型是redis最基本的数据类型,string类型的值最大能存储512MB。
实例
redis 127.0.0.1:6379>SET name "runoob"
OK
redis 127.0.0.1:6379>GET name
"runoob"
hash(哈希)
redis hash是一个键值(key=>value)对集合。
redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
实例
DEL runoob用于删除前面测试用过的key,不然会报错:(error)WRONGTYPE Operation against a key holding the wrong kind of value
redis 127.0.0.1:6379>DEL runoob
redis 127.0.0.1:6379>HMSET myhash field1 "hello" field2 "world"
OK
redis 127.0.0.1:6379>HGET myhash field1
"hello"
redis 127.0.0.1:6379>HGET myhash field2
"world"
实例中我们使用和redis HMSET、HGET命令,HMSET设置了两个field=>value对,HGET获取对应field对应的value。每个hash可以存储(2^32)-1键值对(40多亿)。
list(列表)
redis列表是简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(坐标)或尾部(右边)。
实例
redis 127.0.0.1:6379>DEL runoob
redis 127.0.0.1:6379>lpush runoob redis
(integer) 1
redis 127.0.0.1:6379>lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379>lpush runoob rabitmq
(integer) 3
redis 127.0.0.1:6379>lrange runoob 0 10
1)"rabitmq"
2)"mongodb"
3)"redis"
redis 127.0.0.1:6379>
列表最多可以存储2^32 -1个元素(4294967295,每个列表可存储40多亿)
set(集合)
redis的set是string类型的无序集合,集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1)。
sadd命令
添加一个string元素到key对应的set集合中,成功返回1,如果元素已经在集合中返回0,如果key对应的set不存在则返回错误。
sadd key member
实例
redis 127.0.0.1:6379>DEL runoob
redis 127.0.0.1:6379>sadd tunoob redis
(integer) 1
redis 127.0.0.1:6379>sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379>sadd runoob rabitmq
(integer) 1
redis 127.0.0.1:6379>sadd runoob rabitmq
(integer) 0
redis 127.0.0.1:6379>smembers runoob
1)"redis"
2)"rabitmq"
3)"mongodb"
注意:以上实例中rabitmq添加了两次,但是genuine集合内元素的唯一想,第二次插入的元素被忽略。
集合中最大的成员数为2^32 -1(4294967295,每个集合可存储40多亿个成员)
zset(sorted set:有序集合)
redis zset和set一样也是string类型元素的集合,切不允许重复的成员,不同的是每个元素都会关联一个double类型的分数。redis正式通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
zadd命令
添加元素到集合,元素在集合中存在则更新对应score
zadd key score member
实例
redis 127.0.0.1:6379>DEL runoob
redis 127.0.0.1:6379>zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379>zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379>zadd runoob 0 rabitmq
(integer) 1
redis 127.0.0.1:6379>zadd runoob 0 rabitmq
(integer) 1
redis 127.0.0.1:6379>ZRANGEBYSCORE runoob 0 100
1)"mongodb"
2)"rabitmq"
3)"redis"
2篇笔记
各个数据类型应用场景:
类型 | 简介 | 特性 | 场景 |
---|---|---|---|
string | 二进制安全 | 可以包含任何数据,比如JPG图片或者序列化的对象,一个键最大能存储512M | -- |
hash | 键值对集合,即编程语言中的Map类型 | 适合存储对象并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要去除这个字符串反序列化成对象修改完再序列化存回去) | 存储、读取、修改用户属性 |
list | 链表(双向链表) | 增删快,提供了操作某一段元素的API | 1.最新消息排行等功能(比如朋友圈的时间线)2.消息队列 |
set | 哈希表实现,元素不重复 | 1.提阿尼啊、删除、查找的复杂度都是O(1)。2.为集合提供了求交集、并集、差集等操作 | 1.共同好友2.利用唯一性,统计访问网站的所有独立IP3.好友推荐时,根据tag求交集,大于某个阈值就可以推荐 |
zset | 将set中的元素增加一个权重参数score,元素按score有序排列 | 数据插入集合时,已经进行天然排序 | 1.排行榜2.带权重的消息队列 |
注意:redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。
redis是一个字典结构的存储服务器,而实际上一个redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与我们熟知的在一个关系数据库实例中可以床下多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。
每个数据对外都是一个从0开始的递增数组命名,redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过配置databases来修改这一数字。客户端与redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库,如果要选择1号数据库:
redis>SELECT 1
OK
redis [1]>GET foo
(nil)
然而这些一数字命名的数据库又与我们理解的数据库又所区别。首先redis不支持自定义数据库的名字,每个数据库都以编号命名,开发者必须自己记录哪些数据库存储了哪些数据。另外redis也不支持为每个数据库设置不同的访问密码,所以一个街护短要么可以访问全部数据库,要么连一个数据库也没有权限访问。最重要的一点是多个数据库之间并不是完全隔离的,比如FLUSHALL命令可以清空一个redis实例中所有数据库的数据。综上,哲学数据库更像是一种命名空间,而不适宜存储不同应用程序的数据。比如可以使用0号数据库存储A应用的数据而使用1号数据库存储B应用的数据,不同的应用应该使用不同的redis实例存储数据。由于redis非常轻量级,一个空redis实例占用的内存只有1M左右,所以不用担心多个redis实例会额外占用很多内存。
redis命令
redis命令
redis命令用于在redis服务上执行操作。要在redis服务上执行命令需要一个redis客户单,redis客户端在我们之前下载的redis的安装包中。
语法
redis客户端的基本语法为:
$ redis-cli
实例
启动redis客户端,打开终端并输入命令redis-cli。该命令会连接本地的redis服务。
$redis-cli
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING
PONG
在远程服务上执行命令
在远程redis服务上执行服务命令,同样适用的也是redis-cli命令
语法
$ redis-cli -h host -p port -a password
实例
$ redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
redis 127.0.0.1:6379>
redis 127.0.0.1:6379>PING
PONG
redis键(key)
redis键命令用于管理redis的键
语法
redis 127.0.0.1:6379>COMMAND KEY_NAME
实例
redis 127.0.0.1:6379>SET runoobkey redis
OK
redis 127.0.0.1:6379>DEL runoobkey
(integer) 1
DEL是一个命令,runoobkey是一个键,如果键被删除成功,命令执行后输出(integer) 1,否则将输出(integer) 0.
redis keys命令
命令 | 描述 |
---|---|
DEL key | 该命令用于在key存在时删除key |
DUMP key | 序列化给定key,并返回被序列化的值 |
EXISTS key | 检查给定key是否存在 |
EXPIRE key seconds | 为给定的可以设置过期时间,以秒计 |
EXPIREAT key timestamp | EXPIREAT的作用和EXPIRE类似,都是用于为key设置过去时间,不同在于EXPIREAT命令接受的时间参数是UNIX时间戳(unix timestamp) |
PEXPIRE key millseconds | 设置key的过期时间以毫秒计 |
PEXPIREAT key millseconds-timestamp | 设置key过期时间的时间戳(unix timestamp)以毫秒计 |
KEYS pattern | 查找所有符合给定模式(pattern)的key. |
MOVE key db | 将当前数据库的key移动到给定的数据库db中 |
PERSIST key | 移除key的过期时间,key将持久保持 |
PTTL key | 以毫秒单位返回key的剩余的过期时间。 |
TTL key | 以秒为单位,返回给定key的剩余生存时间(TTL,time to live) |
RANDOMKEY | 从当前数据库中随机返回一个key. |
RENAME key newkey | 修改key的名称 |
RENAMENX key newkey | 仅当newkey不存在时,将key改名为newkey。 |
TYPE key | 返回key所存储的值的类型 |
redis字符串(string)
redis字符串数据类型的相关命令用于管理redis字符串值,基本语法如下:
redis 127.0.0.1:6379>COMMAND KEY_NAME
实例
redis 127.0.0.1:6379>SET runoobkey redis
OK
redis 127.0.0.1:6379>GET runoobkey
"redis"
redis 字符串命令
命令 | 描述 |
---|---|
SET key value | 设置指定key的值 |
GET key | 获取指定key的值 |
GETRANGE key value | 返回key中字符串值得子字符 |
GETSET key value | 将给定key的值设为value,并返回key的旧值(old value) |
GETBIT key offset | 对可以所存储的字符串值,获取指定偏移量上的位(bit) |
MGET key1[key2...] | 获取所有(一个或多个)给定key的值 |
SETBIT key offset value | 对key所储存的字符串值,设置或清除指定偏移量上的位(bit) |
SETEX key seconds value | 将值value关联到key,并将key的过期时间设置为seconds(以秒为单位) |
SETNX key value | 只有在key不存在时设置key的值 |
SETRANGE key offset value | 用value参数覆写给定key所储存的字符串值,从偏移量offset开始 |
STRLEN key | 返回key所储存的字符串值的长度 |
MSET key value[key value...] | 同时设置一个或多个key-value对 |
MSETNX key value[key value...] | 同时设置一个或多个key-value对,当且仅当所有给定key都不存在 |
PSETEX key millseconds value | 这个命令和SETEX命令相似,但它以毫秒为单位设置key的生存时间,而不是像SETEX命令那样,以秒为单位。 |
INCR key | 将key中储存的数字值增1 |
INCRBY key increment | 将key所储存的值加上给定的增量值(increment) |
INCRBYFLOAT key increment | 将key所储存的值加上给定的浮点增量值(increment) |
DECR key | 将key中储存的数字值减一。 |
DECRBY key decrement | key所储存的值减去给定的减量值(decrement) |
APPEND key value | 如果key已经存在并且是一个字符串,APPEND命令将指定的value追加到该key原来值(value)的末尾。 |
redis哈希(hash)
redis hash是一个string类型的field和value的映射表,hash特别适合用于储存对象。每个hash可以存储2^32 -1个键值对。
实例
127.0.0.1:6379>HMSET runoobkey name "redis tutorial" description "redis basic commands for caching" likes 20 visitors 23000
OK
127.0.0.1:6379>HGETALL runoobkey
1)"name"
2)"redis tutorial"
3)"description"
4)"redis basic commands for caching"
5)"likes"
6)"20"
7)"visitors"
8)"23000"
redis hash命令
命令 | 描述 |
---|---|
HDEL key field1 [field2] | 删除一个或多个哈希表字段 |
HEXISTS key field | 查看哈希表key中,指定的字段是否存在 |
HGET key field | 获取储存在哈希表中指定字段的值 |
HGETALL key | 获取在哈希表中指定key的所有字段和值 |
HINCRBY key field increment | 为哈希表key中的指定字段的整数值加上增量increment |
HINCRBYFLOAT key field increment | 为哈希表key中的指定字段的浮点数值加上增量increment |
HKEYS key | 获取所有哈希表中的字段 |
HLEN key | 获取哈希表中字段的数量 |
HMGET key field1[field2] | 获取所有给定字段的值 |
HMSET key field1 value1[field2 value2] | 同时将多个field-value(域-值)对设置到哈希表key中 |
HSET key field value | 将哈希表key中的字段field的值设置为value |
HSETNX key field value | 只有在字段field不存在时,设置哈希表字段的值 |
HVALS key | 获取哈希表中所有值 |
HSCAN key cursor [MATCH patther] [COUNT count] | 迭代哈希表中的键值对 |
redis列表(list)
redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含2^32 -1个元素。
实例
redis 127.0.0.1:6379>LPUSH runoobkey redis
(integer) 1
redis 127.0.0.1:6379>LPUSH runoobkey mongodb
(integer) 1
redis 127.0.0.1:6379>LPUSH runoobkey mysql
(integer) 1
redis 127.0.0.1:6379>LRANGE runoobkey 0 10
1)"mysql"
2)"mongodb"
3)"redis"
redis列表命令
命令 | 描述 |
---|---|
BLPOP key1 [key2] timeout | 移出并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
BRPOP key1 [key2] timeout | 移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
BRPOPLPUSH source destination timeout | 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它;如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
LINDEX key index | 通过索引获取列表中的元素 |
LINSRT key BEFORE|AFTER pivot value | 在列表的元素前或者后插入元素 |
LLEN key | 获取列表长度 |
LPOP key | 移出并获取列表的第一个元素 |
LPUSH key value1 [value2] | 将一个或多个值插入到列表头部 |
LPUSHX key value | 将一个值插入到已存在的列表头部 |
LRANGE key start stop | 获取列表指定范围内的元素 |
LREM key count value | 移出列表元素 |
LSET key index value | 通过索引设置列表元素的值 |
LTRIM key start stop | 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 |
RPOP key | 移出列表的最后一个元素,返回值为移出的元素 |
RPOPLPUSH source destination | 移出列表的最后一个元素,并将该元素添加到另外一个列表并返回 |
RPUSH key value1 [value2] | 在列表中添加一个或多个值 |
RPUSHX key value | 为已存在的列表添加值 |
redis集合(set)
redis的set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。redis中集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1)。集合中最大的成员数为2^32 -1。
实例
redis 127.0.0.1:6379>SADD runoobkey redis
(integer) 1
redis 127.0.0.1:6379>SMEMBERS runoobkey
1)"redis"
redis集合命令
命令 | 描述 |
---|---|
SADD key member1 [member2] | 向集合添加一个或多个成员 |
SCARD key | 获取集合的成员数 |
SDIFF key1 [key2] | 返回给定所有集合的差集 |
SDIFFSTORE destination key1 [key2] | 返回给定所有集合的差集并存储在destination中 |
SINTER key1 [key2] | 返回给定所有集合的交集 |
SINTERSTORE destination key1 [key2] | 返回给定所有集合的交集并存储在destination中 |
SISMEMBER key member | 判断member元素是否是集合key的成员 |
SMEMBERS key | 返回集合中的所有成员 |
SMOVE source destination member | 将member元素从source集合移动到destination集合 |
SPOP key | 移除并返回集合中的一个随机元素 |
SRANDMEMBER key [cout] | 返回集合中一个或多个随机数 |
SREM key member1 [member2] | 移除集合中一个或多个成员 |
SUNION key1 [key2] | 返回所有给定集合的并集 |
SUNIONSTORE destination key1 [key2] | 所有给定集合的并集存储在destination集合中 |
SSCAN key cursor [MATCH pattern] [COUNT count] | 迭代集合中的元素 |
redis有序集合(sorted set)
redis有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis在正式通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1)。集合中最大的成员数是2^32 -1.
实例
redis 127.0.0.1:6379> ZADD runoobkey 1 redis
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 2 mongodb
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 3 mysql
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 3 mysql
(integer) 0
redis 127.0.0.1:6379> ZADD runoobkey 4 mysql
(integer) 0
redis 127.0.0.1:6379> ZRANGE runoobkey 0 10 WITHSCORES
1) "redis"
2) "1"
3) "mongodb"
4) "2"
5) "mysql"
6) "4"
redis有序集合命令
命令 | 描述 |
---|---|
ZADD key score1 member1 [score2 member2] | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
ZCARD key | 获取有序集合的成员数 |
ZCOUNT key min max | 计算在有序集合中指定区间分数的成员数 |
ZINCRBY key increment member | 有序集合中对指定成员的分数加上增量increment |
ZINTERSTORE destination numkeys key [key...] | 计算给定的一个或多个有序集合的交集并将结果存储在新的有序集合key中 |
ZLEXCOUNT key min max | 在有序集合中计算指定字典区间内成员数量 |
ZRANGE key start stop [WITHSCORES] | 通过索引区间返回有序集合成指定区间内额成员 |
ZRANGEBYLEX key min max [LIMIT offset count] | 通过字典区间返回有序集合的成员 |
ZRANGEBYSCORE key min max [WITHSCORES]...[LIMIT] | 通过分数返回有序集合指定区间内的成员 |
ZRANK key member | 返回有序集合中指定成员的索引 |
ZREM key memer [member...] | 移除有序集合中的一个或多个成员 |
ZREMRANGEBYLEX key min max | 移除有序集合中给定的字典区间的所有成员 |
ZREMRANGEBYRANK key start stop | 移除有序集合中给定的排名区间的所有成员 |
ZREMRANGEBYSCORE key min max | 移除有序集合中给定的分数区间的所有成员 |
ZREVRANGE key start stop [WITHSCORES] | 返回有序集合中指定分数区间内的成员,通过索引,分数从高到低 |
ZREVRANGEBYSCORE key max min [WITHSCORES] | 返回有序集合中指定分数区间内的成员,分数从高到低排序 |
ZREVRANK key member | 返回有序集合中指定成员的排名,有序集合成员按分数值递减(从大到小)排序 |
ZSCORE key member | 返回有序集合中,成员的分数值 |
ZUNIONSTORE destination numkeys key [key...] | 计算给定的一个多个有序集合的并集,并存储在新的key中 |
ZSCAN key cursor [MATCH pattern] [COUNT count] | 迭代有序集合中的元素(包括元素成员和元素分值) |
redis HyperLogLog
在2.8.9版本中添加了HyperLogLog结构。HyperLogLog是用来做基数统计的算法,优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。在redis里面,每个HyperLogLog键只需要花费12kb内存,就可以计算奖金2^64个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明的对比。
但是,因为HyperLogLog只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
什么是基数?
比如数据集{1,3,5,7,5,7,8},那么这个数据集的基数集为{1,3,5,7,8},基数(不重复元素)为5。基数估计就是在误差可接受的范围内,快速计算基数。
实例
redis 127.0.0.1:6379> PFADD runoobkey "redis"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mongodb"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mysql"
1) (integer) 1
redis 127.0.0.1:6379> PFCOUNT runoobkey
(integer) 3
redis HyperLogLog 命令
命令 | 描述 |
---|---|
PFADD key element [element...] | 添加指定元素到HyperLogLog中 |
PFCOUNT key [key...] | 返回给定HyperLogLog的基数估算值 |
PFMERGE destkey sourcekey [sourcekey...] | 将多个HyperLogLog合并为一个HyperLogLog |
redis 发布订阅
redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接受消息。redis客户端可以订阅任意数量的频道。
实例
redis 127.0.0.1:6379>SUBSCRIBE redisChat
Reading messages...(press Ctrl-C to quit)
1)"subscribe"
2)"redisChat"
3)(integer) 1
重新开启一个redis客户端,然后在同一个频道redisChat发布两次消息,订阅者就能接受到消息
redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"
(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"
(integer) 1
# 订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"
redis发布订阅命令
命令 | 描述 |
---|---|
PSUBSCRIBE pattern [pattern...] | 订阅一个或多个符合给定模式的频道 |
PUBSUB subcommand [argument [argument...]] | 查看订阅与发布系统状态 |
PUBLISH channel message | 将信息发送到指定的频道 |
PUNSUBSCRIBE [pattern [pattern ...]] | 退订所有给定模式的频道 |
SUBSCRIBE channel [channel ...] | 订阅给定的一个或多个频道的信息 |
UNSUBSCRIBE [channel [channel...]] | 指退订给定的频道 |
redis事务
redis事务可以一次执行多个命令,并且有以下两个重要的保证:
- 批量操作在发送EXEC命令前被放入队列缓存
- 收到EXEC命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中
一个事务从开始到执行会经历以下三个阶段:
- 开始事务
- 命令入队
- 执行事务
实例
以下是一个事务的例子,先以MULTI开始一个事务,然后将多个命令入队到事务中,最后由EXEC命令触发事务,一并执行事务中的所有命令。
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
单个redis命令的执行时原子性的,但redis没有在事务上增加任何维持原子性的机制,所以redis事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面一座指令的回滚,也不会造成后续的指令不执行。
redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> set b bbb
QUEUED
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) OK
3) OK
redis事务命令
命令 | 描述 |
---|---|
DISCARD | 取消事务,放弃执行事务块内的所有命令 |
EXEC | 执行所有事务块内的命令 |
MULTI | 标记一个事务块的开始 |
UNWATCH | 取消WATCH命令对所有key的监视 |
WATCH key[key...] | 监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断 |
redis脚本
redis脚本使用lua解释器来执行脚本,redis2.6版本通过内嵌支持lua环境。执行脚本的常用命令为EVAL
语法
redis 127.0.0.1:6379>EVAL script numkeys key [key ...] arg[arg ...]
实例
redis 127.0.0.1:6379> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
redis脚本命令
命令 | 描述 |
---|---|
EVAL script numkeys key [key ...] arg [arg ...] | 执行Lua脚本 |
EVALSHA sha1 numkeys key [key ...] arg [arg ...] | 执行Lua脚本 |
SCRIPT EXISTS script [script ...] | 查看指定的脚本是否已经被保存在缓存当中 |
SCRIPT FLUSH | 从脚本缓存中移除所有脚本 |
SCRIPT KILL | 杀死当前这在运行的Lua脚本 |
SCRIPT LOAD script | 将脚本script添加到脚本缓存中,但并不立即执行这个脚本 |
redis连接
redis连接命令主要是用于连接redis服务。
实例
redis 127.0.0.1:6379> AUTH "password"
OK
redis 127.0.0.1:6379> PING
PONG
redis连接命令
命令 | 描述 |
---|---|
AUTH password | 验证密码是否正确 |
ECHO message | 打印字符串 |
PING | 查看服务是否运行 |
QUIT | 关闭当前连接 |
SELECT index | 切换到指定的数据库 |
redis服务器
redis服务器命令主要是用于管理redis服务
redis服务命令
命令 | 描述 |
---|---|
BGREWRITEAOF | 异步执行一个AOF(AppendOnly File)文件重写操作 |
BGSAVE | 在后台异步保存当前数据库的数据到磁盘 |
CLIENT KILL [ip:port] [ID client-id] | 关闭客户端连接 |
CLIENT LIST | 获取连接到服务器的客户端连接列表 |
CLIENT GETNAME | 获取连接的名称 |
CLIENT PAUSE timeout | 在指定时间内终止运行来自客户端的命令 |
CLIENT SETNAME connection-name | 设置当前连接的名称 |
CLUSTER SLOTS | 获取集群节点的映射数组 |
COMMAND | 获取redis命令详情数组 |
COMMAND COUNT | 获取redis命令总数 |
COMMAND GETKEYS | 获取给定命令的所有键 |
TIME | 返回当前服务器时间 |
COMMAND INFO command-name [command-name ...] | 获取指定redis命令描述的数组 |
CONFIG GET parameter | 获取指定配置参数的值 |
CONFIG REWRITE | 对启动redis服务区时所指定的redis.conf配置文件进行改写 |
CONFIG SET parameter value | 修改redis配置参数,无需重启 |
CONFIG RESETSTAT | 重置INFO命令中的某些统计数据 |
DBSIZE | 返回当前数据库的key的数量 |
DEBUG OBJECT key | 获取key的调试信息 |
DEBUG SEGFAULT | 让redis服务崩溃 |
FLUSHALL | 删除所有数据库的key |
FLUSHDB | 删除当前数据库的所有key |
INFO [section] | 获取redis服务器的各种信息和统计数值 |
LASTSAVE | 返回最近一次redis成功将数据保存到磁盘上的时间,以UNIX时间戳格式表示 |
MONITOR | 实时打印出redis服务器接收到的命令,调试用 |
ROLE | 返回主从实例所属的角色 |
SAVE | 同步保存数据到硬盘 |
SHUNTDOWN [NOSAVE] [SAVE] | 异步保存数据到硬盘,并关闭服务器 |
SLAVEOF host port | 将当前服务器转变为指定服务器的从属服务器(slave server) |
SLOWLOG subcommand [argument] | 管理redis的慢日志 |
SYNC | 用于复制功能(replication)的内部命令 |
redis高级教程
redis数据备份与恢复
redis SAVE 命令用于创建当前数据库的备份
语法
redis 127.0.0.1:6379> SAVE
恢复数据
如果需要恢复数据,只需将备份文件(dump.rdb)移动到redis安装目录并启动服务即可。获取redis目录可以使用CONFIG命令
redis 127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/usr/local/redis/bin"
以上命令CONFIG GET dir输出的redis安装目录为/usr/local/redis/bin
Bgsave
创建redis备份文件也可以使用命令BGSAVE,该命令在后台执行。
实例
127.0.0.1:6379> BGSAVE
Background saving started
redis安全
通过redis的配置文件设置密码参数,客户端连接到redis服务就需要密码验证,这样就可以让redis服务更安全。
实例
127.0.0.1:6379> CONFIG get requirepass
1)"requirepass"
2)""
默认情况下requirepass参数是空的,意味着无需通过密码验证就可以连接到redis服务。
127.0.0.1:6379> CONFIG set requirepass "runoob"
OK
127.0.0.1:6379> CONFIG get requirepass
1)"requirepass"
2)"runoob"
设置密码后,客户端连接redis服务就需要密码验证,否则无法执行命令。
语法
127.0.0.1:6379> AUTH password
实例
127.0.0.1:6379> AUTH "runoob"
OK
127.0.0.1:6379> SET mykey "test value"
OK
127.0.0.1:6379> GET mykey
"test value"
redis性能测试
redis性能测试时通过同时执行多个命令实现的
语法
redis-benchmark [option] [option value]
注意:该命令是在redis的目录下执行的,而不是redis客户端的内部指令
实例
以下实例同时执行10000个请求来检测性能:
$ redis-benchmark -n 10000 -q
redis性能检测工具可选参数
选项 | 描述 | 默认值 |
---|---|---|
-h | 指定服务器主机名 | 127.0.0.1 |
-p | 指定服务器端口 | 6379 |
-s | 指定服务器socket | |
-c | 指定并发连接数 | 50 |
-n | 指定请求数 | 10000 |
-d | 以字节的形式指定SET/GET值得数据大小 | 2 |
-k | 1=keep alive 0=reconnection | 1 |
-r | SET/GET/INCR 使用随机 key,SADD使用随机值 | |
-p | 通过管道传输<numreq>请求 | 1 |
-q | 强制退出redis,仅显示query/sec值 | |
-csv | 以csv格式输出 | |
-l | 生成循环,永久执行测试 | |
-t | 仅运行以逗号跟个的测试命令列表 | |
-I | Idle模式,仅打开了N个idle连接并等待 |
实例
$ redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 10000 -q
SET: 146198.83 requests per second
LPUSH: 145560.41 requests per second
实例中主机为127.0.0.1,端口为6379,执行的命令为set,lpush,请求数为10000,通过-q参数让结果只显示每秒执行的请求数。
redis客户端连接
redis通过监听一个TCP端口或者Unix socket的方式来接受来自客户端的连接,当一个连接建立后,redis内部会进行一下一些操作:
- 首先,客户端socket会被设置为非阻塞式,因为redis在网络事件处理上采用的是非阻塞多路复用模型
- 然后为这个socket设置TCP_NODELAY属性,禁用Nagle算法
- 然后创建一个可读的文件事件用于监听这个客户端socket的数据发送
最大连接数
在redis 2.4中,最大连接数是被直接硬编码在代码里面的,而在2.6版本中这个值变成可配置的。maxclients的默认值是10000,也可以在redis.conf中对这个值进行修改。
config get maxclients
1)"maxclients"
2)"10000"
实例
redis-server --maxclients 100000
客户端命令
命令 | 描述 |
---|---|
CLIENT LIST | 返回连接到redis服务的客户端列表 |
CLIENT SETNAME | 设置当前连接的名称 |
CLIENT GETNAME | 获取通过CLIENT SETNAME命令设置的服务名称 |
CLIENT PAUSE | 挂起客户端连接,指定挂起的时间以毫秒计 |
CLIENT KILL | 关闭客户端连接 |
redis管道技术
redis是一种基于客户端-服务端模型以及请求/相应协议的TCP服务,意味着通常情况下一个请求会遵循以下步骤:
- 客户端像服务端发送一个查询请求,并监听socket返回,通常是以阻塞模式,等待服务端响应
- 服务端处理命令,并将结果返回给客户端
redis管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
实例
查看redis管道,只需要启动redis实例并输入以下命令:
$(echo -en "PING\r\n SET runoobkey redis\r\nGET runoobkey\r\nINCR visitor\r\nINCR visitor\r\nINCR visitor\r\n"; sleep 10) | nc localhost 6379
+PONG
+OK
redis
:1
:2
:3
实例中通过使用PING命令查看redis服务是否可用,之后我们设置了runoobkey的值为redis,然后获取runoobkey的值并使得visitor自增3次。
在返回的结果中可以看到这些命令一次性向redis服务提交,并最终一次性读取所有服务端的响应
管道技术的优势
管道技术最显著的优势是提高了redis服务的性能
一些测试数据,使用redis的ruby客户端,支持管道技术特性,测试管道技术对速度的提升效果
require 'rubygems'
require 'redis'
def bench(descr)
start = Time.now
yield
puts "#{descr} #{Time.now-start} seconds"
end
def without_pipelining
r = Redis.new
10000.times {
r.ping
}
end
def with_pipelining
r = Redis.new
r.pipelined {
10000.times {
r.ping
}
}
end
bench("without pipelining") {
without_pipelining
}
bench("with pipelining") {
with_pipelining
}
从处于局域网中的mac OS X系统上执行这个简单的数据表明,开启了管道操作后,往返延时已经被改善的相当低了
reids分区
分区是分割数据到多个redis实例的处理过程,因此每个实例只保存key的一个子集
分区的优势:
- 通过利用多台计算机的内存和值,允许我们构造更大的数据库
- 通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽
分区的不足
- 涉及多个key的操作通常是不被支持的(当两个set映射到不同的redis实例上时,就不能对这个两个set执行交集操作)
- 涉及多个key的redis事务不能使用
- 当使用分区是,数据处理比较复杂,比如需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件
- 增加或删除容量也比较复杂,redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端的分区、代理等其他系统则不支持这项特性。然而,一种叫做presharding的技术对此是有帮助的。
分区类型
redis有两种类型分区,假设有4个redis实例R0,R1,R2,R3和类似user:1 , user:2 这样的表示用户的多个key,对既定的key有多种不同的方式来选择这个key存放在哪个实例中,也就是说有不同的系统来映射某个key到某个redis服务
范围分区
最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的redis实例。比如,ID从0到10000的用户会保存到实例R0,ID从10001到20000的用户会保存到R1,以此类推。这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表,这个表要被管理,同时还需要各种对象的映射表,通常对redis来说并非是最好的方法。
哈希分区
另外一种分区方法是hash分区,这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:
- 用一个hash函数将key转化为一个数字,比如适用crc32 hash函数,对key foobar执行crc32(foobar)会输出蕾西93024922的整数
- 对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个redis实例中的一个了。9302422%4=2,就是说key foobar应该被存放到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。
Java使用redis
安装
在Java中使用redis前,需要确保已经安装了redis服务及java redis驱动,且你的机器上能正常使用Java,Java的安装配置可以参考配置教程。安装Java redis驱动:
下载驱动包
在你的classpath中包含该驱动包
连接到redis服务
import redis.clients.jedis.Jedis;
public class RedisJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
//查看服务是否运行
System.out.println("服务正在运行: "+jedis.ping());
}
}
编译以上java程序,确保驱动包的路径是正确的
连接成功
服务正在运行:PONG
redis java String(字符串)实例
import redis.clients.jedis.Jedis;
public class RedisStringJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
//设置 redis 字符串数据
jedis.set("runoobkey", "www.runoob.com");
// 获取存储的数据并输出
System.out.println("redis 存储的字符串为: "+ jedis.get("runoobkey"));
}
}
编译以上程序
连接成功
redis 存储的字符串为: www.runoob.com
redis java List(列表)实例
import java.util.List;
import redis.clients.jedis.Jedis;
public class RedisListJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
//存储数据到列表中
jedis.lpush("site-list", "Runoob");
jedis.lpush("site-list", "Google");
jedis.lpush("site-list", "Taobao");
// 获取存储的数据并输出
List<String> list = jedis.lrange("site-list", 0 ,2);
for(int i=0; i<list.size(); i++) {
System.out.println("列表项为: "+list.get(i));
}
}
}
编译以上程序
连接成功
列表项为: Taobao
列表项为: Google
列表项为: Runoob
reids java keys 实例
import java.util.Iterator;
import java.util.Set;
import redis.clients.jedis.Jedis;
public class RedisKeyJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
// 获取数据并输出
Set<String> keys = jedis.keys("*");
Iterator<String> it=keys.iterator() ;
while(it.hasNext()){
String key = it.next();
System.out.println(key);
}
}
}
编译以上程序
连接成功
runoobkey
site-list