第二部分:关键技术领域的开源数据库实践【开源NoSQL数据库保姆级教程之Redis数据库】

Redis数据库

Redis简介

Redis(Remote Dictionary Server:远程字典服务)

是完全开源并免费的,遵守BSD协议,是一个高性能的key-value数据库中

Redis 与其他key-value 缓存产品有以下三个特点

  1. Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  2. Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  3. Redis支持数据的备份,即master-slave模式的数据备份

Redis 优势

  1. 性能极高,高速缓存– Redis能读的速度是110000次/s,写的速度是81000次/s 。
  2. 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  3. 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  4. 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

MySQL是关系型数据库,是持久化存储的,查询检索的话,会涉及到磁盘IO操作,为了提高性能,可以使用缓存技术,而memcached就是内存数据库,数据存储在内存中(当然也可以进行持久化存储),可以用作缓存数据库。用户首先去memcached查询数据,如果未查询到(即缓存未命中),才去MySQL中查询数据,查询到的数据会更新到缓存数据库中,提供给下次可能进行的查询。提高了数据查询方面的性能。

Redis和memcached都是缓存数据库,可以大大提升高数据量的web访问速度。但是memcached只是提供了简单的数据结构string,而Redis的value可以是string、list、set、hash、sorted set这些,功能更加强大。   web应用中一般采用MySQL+Redis的方式,web应用每次先访问Redis,如果没有找到数据,才去访问MySQL。

Redis是内存数据库,数据保存在内存中,访问速度快。MySQL是关系型数据库,功能强大,存储在磁盘中,数据访问速度慢。像memcached,MongoDB,Redis等,都属于No sql系列。

Redis能干什么

1、内存存储、持久化,内存中是断电即失、所以说持久化很重要 (rdb、aof) 2、效率高,可以用于高速缓存 3、发布订阅系统 4、地图信息分析 5、计时器、计数器(浏览量!) 6、……

Redis安装和配置

下载安装

  1. 官网:https://redis.io
  2. 中文网:http://www.redis.net.cn/
  3. 解压直接可以使用: * redis.windows.conf:配置文件 * redis-cli.exe:redis的客户端 * redis-server.exe:redis服务器端

Windows下安装Redis步骤


1、下载和解压Redis-x64-3.2.100

Github下载地址:https://github.com/MicrosoftArchive/redis/releases

2、打开cmd指令窗口,输入你刚才解压的文件路径,然后输入以下命令

redis-server redis.windows.conf

3、安装Windows服务

./redis-server --service-install redis.windows.conf

4、启动redis服务

#Redis常用的指令
#卸载服务:
./redis-server --service-uninstall
#开启服务:
./redis-server --service-start
或net start  redis
#停止服务:
./redis-server --service-stop
或net stop redis

img

客户端工具推荐

1、TinyRDM

TinyRDM 是一个现代化、轻量级的 Redis 数据库桌面客户端工具。

下载地址:https://github.com/tiny-craft/tiny-rdm/releases

image.png

2、AnotherRedisDesktopManager

下载地址:https://github.com/qishibo/AnotherRedisDesktopManager

或国内:https://gitee.com/qishibo/AnotherRedisDesktopManager/releases/v1.5.0

img

Redis 命令用于在 redis 服务上执行操作。

在本机服务上执行命令

要在 redis 服务上执行命令需要一个 redis 客户端。Redis 客户端在我们之前下载的的 redis 的安装包中。

语法

Redis 客户端的基本语法为:

$ redis-cli

检测 redis 服务是否启动

$ redis-cli
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING

windows上执行命令

img

在远程服务上执行命令

如果需要在远程 redis 服务上执行命令,同样我们使用的也是 redis-cli 命令。

语法

$ redis-cli -h host -p port -a password

注意有时候会有中文乱码。需要在 redis-cli 后面加上 --raw

Redis数据类型及应用场景

img

Redis 数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。


String(字符串)

字符串类型是 Redis 中最基本的数据类型,一个 key 对应一个 value。可以存储二进制数据、图片和 Json 的对象。

string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

string 类型的值最大能存储 512MB。

常用命令:get、set、incr、decr、mget等。

Redis还提供了下面一些操作:

  1. 获取字符串长度
  2. 往字符串append内容
  3. 设置和获取字符串的某一段内容
  4. 设置及获取字符串的某一位(bit)
  5. 批量设置一系列字符串的内容

Hash(哈希)

Redis hash 是一个键值(key=>value)对集合。

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

常用命令:hget,hset,hgetall 等。

比如我们要存储一个用户信息对象数据,包含以下信息:

用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:

img

方法一:将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。

img

方法二:是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口,如下图:

img

也就是说,Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题,很好的解决了问题。

List(列表)

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

Set(集合)

Redis 的 Set 是 string 类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

sadd 命令

添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。

注意: 以上实例中 rabbitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。

集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。


zset(sorted set:有序集合)

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

Redis基本操作

key操作命令

获得符合规则的键名列表

KEYS pattern

pattern 支持 glob 风格通配符:

img

set 设置key的值语法

set key value
# 示例 127.0.0.1:6379> set name zhangsan
 OK 
127.0.0.1:6379> set age 25
OK

get 获取key的值

get key
# 示例 127.0.0.1:6379> get name "zhangsan"

del 删除key

del key
# 示例 127.0.0.1:6379> del name (integer) 1

exists 判断key是否存在

exists key
# 判断key是否存在,存在返回1,不存在返回0 
# 示例 127.0.0.1:6379> exists name (integer) 1
 127.0.0.1:6379> exists title
 (integer) 0

type 获取key类型

type key
# 获取key存储的值的类型 
# 示例 127.0.0.1:6379> type name string 
127.0.0.1:6379> type age string

expire 设置key有效期

expire key
# 设置key的生命周期 # expire key 表示以秒为单位设置声明周期 
# 示例 127.0.0.1:6379[1]> expire login 60 
(integer) 1 
127.0.0.1:6379[1]> ttl login
 (integer) 47

tll 查看key有效期

ttl key
# 查询key的生命周期 # 大于0 :生命周期单位为秒, # 等于-1:永久有效 # 等于-2:该key不存在 # pttl key表示秒为单位 
# 示例 127.0.0.1:6379> ttl name
 (integer) -1 
127.0.0.1:6379> ttl title
 (integer) -2

rename 重命名key

rename key newkey
# 重命名key,如果newkey已经存在,修改后则替换新key的值 
# 示例 127.0.0.1:6379> set title "redis test" 
OK 
127.0.0.1:6379> exists title
(integer) 1 
127.0.0.1:6379> rename title biaoti 
OK 
127.0.0.1:6379> get biaoti
 "redis test"

renamenx 重命名不存在的key

renamenx key newkey
# 重命名key,如果newkey已经存在则不修改。 # nx表示not exists 
# 示例 127.0.0.1:6379> keys * 
1) "biaoti" 
2) "age" 
3) "name" 
127.0.0.1:6379> renamenx biaoti name
 (integer) 0

persist 设置key永久有效

persist key
# 设置key永久有效 
# 示例 127.0.0.1:6379> set login on
 OK 127.0.0.1:6379> expire login 60 
(integer) 1 
127.0.0.1:6379> ttl login
 (integer) 55 
127.0.0.1:6379> persist login 
(integer) 1 
127.0.0.1:6379> ttl login
 (integer) -1

move 把key移动到其他库

move key db
# 把key移动到另一个数据库,db为整数 # 示例

库操作命令

dbsize 查看当前有多少个key

dbsize
# 查看当前有多少个key 
# 示例 127.0.0.1:6379> dbsize 12

select 选择库

select db
# 选择使用哪个数据库,db为整数 # 默认有16个数据库0~15,如果想修改数据库数量,修改redis.conf配置文件的databases值 
# 示例 127.0.0.1:6379> select 1 
OK 
127.0.0.1:6379[2]> select 15 
OK

flushdb 删除选中数据库中的key

flushdb
# 删除当前选择数据库中的所有key 
# 示例 127.0.0.1:6379[1]> keys * 
1) "biaoti" 
127.0.0.1:6379[1]> flushdb 
OK 
127.0.0.1:6379[1]> keys *
 (empty list or set)

flushall 删除所有库的key

flushall
# 删除所有数据库中的key 
# 示例 127.0.0.1:6379[1]> flushall 
OK 127.0.0.1:6379[1]> select 0 
OK 
127.0.0.1:6379> keys * 
(empty list or set)

字符串类型(String)常用命令:

img

2.命名建议:“对象类型:对象ID:对象属性”命名一个键,如:“user:1:friends”存储 ID 为 1 的用户的的好友列表。对于多个单词则推荐使用 “.” 进行分隔。

3.应用:

(1)访问量统计:每次访问博客和文章使用 INCR 命令进行递增;

(2)将数据以二进制序列化的方式进行存储。

set 设置kv、效期、判断key是否存在

set key value [ex 秒数]|[px 毫秒数] [nx]|[xx]

# 设置kv时也可以设置有效期和判断key是否存在
# ex和px不要同时写,否则以后面有效期为准
# nx表示key不存在时执行操作
# xx表示key存在时执行操作
​
​
# 示例

mset 一次性输入多个kv

mset key1 value1 key2 value2......
# 一次性输入多个key-value
​
# 示例
127.0.0.1:6379> mset x 1 y 2 z 3
OK
127.0.0.1:6379> keys *
1) "y"
2) "z"
3) "x"

setrange 修改偏移字节值为valuev

setrange key offset value
# 把字符串的偏移字节改为value
# 如果偏移量大于字符长度,中间字符自动补0x00
​
# 示例
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> setrange name 5 ***
(integer) 8
127.0.0.1:6379> get name
"zhang***"

append 在key的值后面追加字符串

append key value
# 在key的值后面追加value字符串
​
# 示例
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> append name "@126.com"
(integer) 16
127.0.0.1:6379> get name
"zhangsan@126.com"

getrange 获取key值的部分内容

getrange key start stop
# 获取key值的一部分内容
# start表示起始位置
# stop表示结束位置,可以为为负数,表示从最后数起
# start>length 空字符串
# stop>length 截取到结尾
​
# 示例
127.0.0.1:6379> set title "hello world"
OK
127.0.0.1:6379> getrange title 6 11
"world"
127.0.0.1:6379> getrange title 0 -7
"hello"

getset 设置新值返回旧值

getset key newvalue
# 设置新值,并返回旧值
​
# 示例
127.0.0.1:6379> set login on
OK
127.0.0.1:6379> get login
"on"
127.0.0.1:6379> getset login off
"on"
127.0.0.1:6379> get login
"off"

incr/decr 指定key的值加/减1

incr/decr key
# 指定key的值加/减1,返回结果
# key不存在时,自动创建并加减1
# key的值为字符串时无效
​
# 示例
127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incr num
(integer) 101
127.0.0.1:6379> decr num 
(integer) 100

incrby/decrby 指定key的值加/减number

incrby/decrby key number
# 指定key的值加减number大小
​
# 示例
127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incrby num 50
(integer) 150
127.0.0.1:6379> decrby num 100
(integer) 50

incrbyfloat 指定key的值加浮点数

incrbyfloat key floatnumber
# 指定key的值加浮点数
​
# 示例
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> incrbyfloat num 0.5
"10.5"
127.0.0.1:6379> incrbyfloat num -1.5
"9"

setbit 设置二进制位上的值

setbit key offset value
# 设置offset对应二进制位上的值
# 返回改位的旧值
# 如果offset过大则会在中间填充0
# offset最大为2^32-1,即512M
​
# 示例
127.0.0.1:6379> set letter A
OK
127.0.0.1:6379> setbit letter 2 1
(integer) 0
127.0.0.1:6379> get letter
"a"
# 把0100 0001(65)改为0110 0001(97)即把大写A改为了小写a

getbit 获取二进制位上的值

getbit key offset
# 获取二进制offset对应位的值
​
# 示例
127.0.0.1:6379> set letter A
OK
127.0.0.1:6379> getbit letter 0
(integer) 0
127.0.0.1:6379> getbit letter 1
(integer) 1
127.0.0.1:6379> getbit letter 7
(integer) 1

bitop 对多个key逻辑操作

bitop operation destkey key1 [key2 ......]
# 对key1 key2 keyN做operation,并把结果保存到destkey 上
# operation有AND、OR、NOT、XOR
​
# 示例
127.0.0.1:6379> setbit lower 2 1
(integer) 0
127.0.0.1:6379> setbit upper 2 0
(integer) 0
127.0.0.1:6379> set letter A
OK
127.0.0.1:6379> bitop or letter letter lower
(integer) 1
127.0.0.1:6379> get letter
"a"

散列类型

采用了字典结构(k-v)进行存储。散列类型适合存储对象。可以采用这样的命名方式:对象类别和 ID 构成键名,使用字段表示对象的属性,而字段值则存储属性值。如:存储 ID 为 2 的汽车对象。

img

img

例如文章内容存储:

img

1. hset 设置哈希field域的值

hset key field value
# 把key中 filed域的值设为value
# 注:如果没有field域,直接添加,如果有,则覆盖原field域的值
​
# 示例
127.0.0.1:6379> hset user name zhangsan
(integer) 1
127.0.0.1:6379> hset user age 25
(integer) 1
127.0.0.1:6379> hset user gender male
(integer) 1

2. hmset 设置哈希多个field域的值

hmset key field1 value1 [field2 value2 field3 value3 ......fieldn valuen]
# 一次设置多个field和对应的value
​
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK

3. hget 获取field域的值

hget key field
# 获取field域的值
​
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK
127.0.0.1:6379> hget user age
"26

4. hmget 获取多个field域的值

hget key field
# 获取多个field域的值

# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK
127.0.0.1:6379> hmget user name age
1) "lisi"
2) "26"

5. hgetall 获取所有field域和值

hgetall key
# 获取哈希key的所有field域和值

# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
127.0.0.1:6379> hgetall user
1) "name"
2) "lisi"
3) "age"
4) "26"
5) "gender"
6) "male"

6. hlen 获取field的数量

hlen key
# 获取field的数量

# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
127.0.0.1:6379> hlen user
(integer) 3

7. hdel 删除field域

hdel key field
# 删除key中 field域

# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hgetall user
1) "name"
2) "lisi"
3) "gender"
4) "male"

8. hexists 判断field域是否存在

hexists key field
# 判断key中有没有field域

# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK
127.0.0.1:6379> hexists user age
(integer) 1
127.0.0.1:6379> hexists user height
(integer) 0

9. hincrby 使field域的值加上整数

hincrby key field value
# 使key中的field域的值加上整型值value
​
# 示例
127.0.0.1:6379> hmset user name zhangsan height 158
OK
127.0.0.1:6379> hincrby user height 2
(integer) 160

10. hincrbyfloat 使field域的值加上浮点数

hincrbyfloat key field value
# 使key中的field域的值加上浮点值value
​
# 示例
127.0.0.1:6379> hmset user name zhangsan height 158
OK
127.0.0.1:6379> hincrbyfloat user height 5.5
"165.5"

11. hkeys 获取所有所有field域的名字

hkeys key
# 获取key中所有的field
​
# 示例
127.0.0.1:6379> hmset user name zhangsan age 25 gender male
OK
127.0.0.1:6379> hkeys user
1) "name"
2) "age"
3) "gender"

12. hvals 获取所有所有field域的值

hvals key
# 返回key中所有的value

# 示例
127.0.0.1:6379> hmset user name zhangsan age 25 gender male
OK
127.0.0.1:6379> hvals user
1) "zhangsan"
2) "25"
3) "male"

列表类型(List)

列表类型(list)可以存储一个有序的字符串列表,常用的操作是向两端添加元素。

列表类型内部是使用双向链表实现的,也就是说,获取越接近两端的元素速度越快,代价是通过索引访问元素比较慢。

img

应用场景:

(1)显示社交网站的新鲜事、热门评论和新闻等;

(2)当队列使用;

(3)记录日志。

lpush/rpush 在链表头/尾增加一个成员

lpush/rpush key value
# 在链表头/尾增加一个成员,返回链表成员的个数

向列表的左边添加元素“1”,再依次加入“2”、“3”

在列表的右边依次加入两个元素“0”、“-1”:

img

# 示例实现
127.0.0.1:6379> lpush numbers 1
(integer) 1
127.0.0.1:6379> lpush numbers 2 3
(integer) 3
127.0.0.1:6379> rpush numbers 0 -1
(integer) 5

lrange 获取链表成员

lrange key start stop
# 返回链表中[start,stop]范围的成员
# 规律: 左数从0开始,右数从-1开始

# 示例
127.0.0.1:6379> lrange letters 0 -1
1) "A"
2) "B"
3) "C"
4) "D"
127.0.0.1:6379> lrange letters 1 2
1) "B"
2) "C"

lpop/rpop 弹出链表中头/尾的成员

lpop/rpop key
# 弹出链表中头/尾的成员

# 示例
127.0.0.1:6379> lrange letters 0 -1
1) "A"
2) "B"
3) "C"
4) "D"
127.0.0.1:6379> lpop letters
"A"
127.0.0.1:6379> rpop letters
"D"
127.0.0.1:6379> lrange letters 0 -1
1) "B"
2) "C"

lrem 删除链表成员

lrem key count value
# 从key链表中删除 value值
# 删除count的绝对值个value后结束
# count>0 从表头删除
# count<0 从表尾删除

# 示例
127.0.0.1:6379> rpush letters A B C D A B C D A B C D
(integer) 12
127.0.0.1:6379> lrem letters 2 A
(integer) 2
127.0.0.1:6379> lrange letters 0 -1
 1) "B"
 2) "C"
 3) "D"
 4) "B"
 5) "C"
 6) "D"
 7) "A"
 8) "B"
 9) "C"
10) "D"
127.0.0.1:6379> lrem letters -3 D
(integer) 3
127.0.0.1:6379> lrange letters 0 -1
1) "B"
2) "C"
3) "B"
4) "C"
5) "A"
6) "B"
7) "C"

lindex 获取链表索引对应的值

lindex key index
# 获取链表索引index对应的值

# 示例
127.0.0.1:6379> rpush letters A B C D
(integer) 4
127.0.0.1:6379> lindex letters 1
"B"
127.0.0.1:6379> lindex letters 2
"C"

llen key 获取链表成员个数

BASHllen key
# 获取链表成员个数

# 示例
127.0.0.1:6379> rpush letters A B C D
(integer) 4
127.0.0.1:6379> llen letters
(integer) 4

linsert 在链表中指定位置插入成员

linsert key after|before search value
# 在key链表中寻找"search",并在search值之前|之后插入value
# 如果没有找到,不插入值
# 如果找到一个search后,命令就结束了,因此不会插入多个value
​
# 示例
127.0.0.1:6379> rpush id 1 3 5 7
(integer) 4
127.0.0.1:6379> linsert id before 3 2
(integer) 5
127.0.0.1:6379> lrange id 0 -1
1) "1"
2) "2"
3) "3"
4) "5"
5) "7"
127.0.0.1:6379> linsert id after 5 6
(integer) 6
127.0.0.1:6379> lrange id 0 -1
1) "1"
2) "2"
3) "3"
4) "5"
5) "6"
6) "7"

blpop/brpop 一直等待弹出头/尾成员

blpop/brpop key timeout
# 等待弹出key的头/尾成员
# Timeout为等待超时时间
# 如果timeout为0,则一直等待
# 应用s场景: 长轮询Ajax,在线聊天时,能够用到
​
# 示例
# 第一个终端操作:
127.0.0.1:6379> brpop chat 0
1) "chat"
2) "hello"
(40.97s)
​
# 第二个终端操作:
127.0.0.1:6379> rpush chat "hello"
(integer) 1

无序集合

集合特性:无序性、唯一性、确定性

字符串的无序集合,不允许存在重复的成员。

多个集合类型之间可以进行并集、交集和差集运算。

img

图解交、并、差集:

img

img

sadd 往集合添加成员

sadd key value1 value2 ...
# 往集合key中增加成员
# 增加相同成员时只会添加一个(唯一性)# 示例实现
127.0.0.1:6379> sadd letters a 
(integer) 1
127.0.0.1:6379> sadd letters a b c
(integer) 2127.0.0.1:6379> smembers letters1)"c"2)"b"3)"a"127.0.0.1:6379> sadd setA 1 3 5 7(integer) 4127.0.0.1:6379> sadd setB 2 3 4 5(integer) 4127.0.0.1:6379> sdiff setB setA1)"2"2)"4"127.0.0.1:6379> sadd setA setB1)"1"2)"7"

srem 删除集合成员

srem key value1 value2 ...
# 删除集合中为value1 value2...成员
# 返回真正删除掉的成员个数(不包括不存在的成员)

# 示例
127.0.0.1:6379> sadd names zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> srem names zhangsan lisi
(integer) 2
127.0.0.1:6379> smembers names
1) "wangwu"

spop 随机删除集合一个成员

spop key
# 随机删除集合key中的一个成员
# 应用场景:抽奖,抽中的人已经排除,不可能会被再次抽中了

# 示例
127.0.0.1:6379> sadd letters A B C D E F
(integer) 6
127.0.0.1:6379> spop letters
"A"
127.0.0.1:6379> spop letters
"F"
127.0.0.1:6379> spop letters
"B"
127.0.0.1:6379> spop letters
"D"

srandmember 随机获取集合成员

srandmember key [count]
# 随机获取集合key的count个成员,默认count是1

# 示例
127.0.0.1:6379> srandmember letters
"C"
127.0.0.1:6379> srandmember letters 2
1) "E"
2) "B"
127.0.0.1:6379> srandmember letters 3
1) "D"
2) "C"
3) "E"

smembers 获取集合所有的成员

smembers key
# 返回集合所有的成员
# 返回值的顺序不一定是添加成员的顺序(无序性)

# 示例
127.0.0.1:6379> sadd names zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> smembers names
1) "lisi"
2) "wangwu"
3) "zhangsan"

sismember 判断成员是否存在集合中

sismember key value
# 判断value是否存在集合key中,存在返回1,不存在返回0

# 示例
127.0.0.1:6379> sadd names zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> sismember names lisi
(integer) 1
127.0.0.1:6379> sismember names zhaoliu
(integer) 0

scard 获取集合成员的个数

scard key
# 获取集合成员的个数
​
# 示例
127.0.0.1:6379> sadd letters A B C D 
(integer) 4
127.0.0.1:6379> sadd letters E F
(integer) 2
127.0.0.1:6379> scard letters
(integer) 6

smove 把一个集合中成员移动到另一个集合

smove <source> <dest> value
# 把集合source中的value删除,并添加到集合dest中
​
# 示例
127.0.0.1:6379> sadd letters A B C
(integer) 3
127.0.0.1:6379> sadd num 1 2 3
(integer) 3
127.0.0.1:6379> smove letters num A
(integer) 1
127.0.0.1:6379> smembers letters
1) "C"
2) "B"
127.0.0.1:6379> smembers num
1) "3"
2) "1"
3) "A"
4) "2"

sunion 获取多个集合的并集

sunion key1 key2 ...
# 获取多个集合的并集
​
# 示例
127.0.0.1:6379> sadd zhangsan A E G
(integer) 3
127.0.0.1:6379> sadd lisi B E F
(integer) 3
127.0.0.1:6379> sadd wangwu C D E
(integer) 3
127.0.0.1:6379> sunion zhangsan lisi wangwu
1) "B"
2) "G"
3) "D"
4) "C"
5) "E"
6) "F"
7) "A"

sdiff 获取多个集合的差集

sdiff key1 key2 ...
# 获取key1与key2...的差集
# 即key1-key2...(key1有其他集合没有的成员)

# 示例
127.0.0.1:6379> sadd zhangsan A B C
(integer) 3
127.0.0.1:6379> sadd lisi B D E
(integer) 3
127.0.0.1:6379> sadd wangwu C E F
(integer) 3
127.0.0.1:6379> sdiff zhangsan lisi wangwu
1) "A"

sinterstore 获取多个集合的交集并储存

sinterstore dest key1 key2 ...
# 求出key1 key2 ...集合中的交集,并赋给dest

# 示例
127.0.0.1:6379> sadd zhangsan A C D
(integer) 3
127.0.0.1:6379> sadd lisi B D E
(integer) 3
127.0.0.1:6379> sadd wangwu D E G
(integer) 3
127.0.0.1:6379> sinterstore class zhangsan lisi wangwu
(integer) 1
127.0.0.1:6379> smembers class
1) "D"

有序集合

在集合类型的基础上添加了排序的功能。

img

例如点击量排序

img

1. zadd 往有序集合添加成员

zadd key score1 key2 score2 key2 ...
# 往有序集合key添加成员
​
# 示例
127.0.0.1:6379> zadd ages 28 zhangsan 24 lisi 26 wangwu
(integer) 0
127.0.0.1:6379> zrange ages 0 -1
1) "lisi"
2) "wangwu"
3) "zhangsan"

2. zrange 按名次取成员

zrange key start stop [WITHSCORES]
# 把集合排序后,返回名次[start,stop]的成员按名次取成员
# 默认是升续排列,withscores 是把score也打印出来
​
# 示例
127.0.0.1:6379> zrange ages 0 -1 withscores
1) "lisi"
2) "24"
3) "wangwu"
4) "26"
5) "zhangsan"
6) "28"

3. zrangebyscore 按分数取成员

zrangebyscore  key min max [withscores] limit offset N
# 集合(升序)排序后,取score在[min,max]内的成员,并跳过offset个,取出N个,按分数取成员
​
# 示例
127.0.0.1:6379> zadd ages 28 zhangsan 24 lisi 26 wangwu
(integer) 3
127.0.0.1:6379> zrangebyscore ages 25 30
1) "wangwu"
2) "zhangsan"
127.0.0.1:6379> zrangebyscore ages 25 30 limit 1 1
1) "zhangsan"

4. zscore 获取指定成员的分数

ZSCORE key member
# 获取指定成员的分数

# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu
(integer) 3
127.0.0.1:6379> zscore height lisi
"167"

5. zcount 计算分数区间成员个数

zcount key min max
# 计算[min,max]区间内成员的数量

# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu
(integer) 3
127.0.0.1:6379> zcount height 170 180
(integer) 1

6. zrank/zrevrank 获取成员升序/降序的排名

zrank/zrevrank key member
# 查询member的升序/降序排名,名次从0开始
​
# 示例
127.0.0.1:6379> zadd ages 28 zhangsan 24 lisi 26 wangwu
(integer) 0
127.0.0.1:6379> zrange ages 0 -1
1) "lisi"
2) "wangwu"
3) "zhangsan"
127.0.0.1:6379> zrank ages zhangsan
(integer) 2
127.0.0.1:6379> zrevrank ages zhangsan
(integer) 0

7. zrem 删除有序集合成员

zrem key value1 value2 ..
# 删除集合中的成员
​
# 示例
127.0.0.1:6379> zrem ages wangwu
(integer) 1
127.0.0.1:6379> zrange ages 0 -1
1) "lisi"
2) "zhangsan"

8. zremrangebyrank 按排名删除成员

zremrangebyrank key start end
# 按排名删除成员,删除名次在[start,end]之间的
​
# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu 178 zhaoliu
(integer) 1
127.0.0.1:6379> zremrangebyrank height 0 1
(integer) 2
127.0.0.1:6379> zrange height 0 -1
1) "zhaoliu"
2) "wangwu"

9. zremrangebyscore 按分数删除成员

zremrangebyscore key min max
# 按照socre来删除成员,删除score在[min,max]之间的
​
# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu 178 zhaoliu
(integer) 2
127.0.0.1:6379> zremrangebyscore height 170 180
(integer) 2
127.0.0.1:6379> zrange height 0 -1
1) "lisi"
2) "wangwu"

10. zinterstore 求交集再计算

zinterstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
# 求key1、key2...的交集,key1、key2...的权重分别是 weight1、weight2...
# 聚合方法用: sum|min|max
# 聚合的结果保存在destination集合内
​
# 示例
127.0.0.1:6379> zadd zhangsan 5 iphone6s 7 galaxyS7 6 huaweiP9
(integer) 3
127.0.0.1:6379> zadd lisi 3 iphone6s 9 galaxyS7 4 huaweiP9 2 HTC10
(integer) 4
127.0.0.1:6379> zinterstore result 2 zhangsan lisi
(integer) 3
127.0.0.1:6379> zrange result 0 -1 withscores
1) "iphone6s"
2) "8"
3) "huaweiP9"
4) "10"
5) "galaxyS7"
6) "16"
127.0.0.1:6379> zinterstore result 2 zhangsan lisi aggregate max
(integer) 3
127.0.0.1:6379> zrange result 0 -1 withscores
1) "iphone6s"
2) "5"
3) "huaweiP9"
4) "6"
5) "galaxyS7"
6) "9"

11. zunionstore 求并集再计算

zunionstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
# 求key1、key2...的并集,key1、key2...的权重分别是 weight1、weight2...
# 聚合方法用: sum|min|max
# 聚合的结果保存在destination集合内
​
# 示例
127.0.0.1:6379> zadd zhangsan 4 iphone6s 6 huaweiP9 8 xiaomi5
(integer) 3
127.0.0.1:6379> zadd lisi 2 iphone6s 8 galaxS7 5 meizu6
(integer) 3
127.0.0.1:6379> zunionstore result 2 zhangsan lisi
(integer) 5
127.0.0.1:6379> zrange result 0 -1 withscores
 1) "meizu6"
 2) "5"
 3) "huaweiP9"
 4) "6"
 5) "iphone6s"
 6) "6"
 7) "galaxS7"
 8) "8"
 9) "xiaomi5"
10) "8"
127.0.0.1:6379> zunionstore result 2 zhangsan lisi aggregate max
(integer) 5
127.0.0.1:6379> zrange result 0 -1 withscores
 1) "iphone6s"
 2) "4"
 3) "meizu6"
 4) "5"
 5) "huaweiP9"
 6) "6"
 7) "galaxS7"
 8) "8"
 9) "xiaomi5"
10) "8"

事务操作

1. 事务命令

命令 说明
multi 开启事务命令
command 普通命令
discard 在提交前取消
exec 提交

注:discard只是结束本次事务,前2条语句已经执行,造成的影响仍然还在。

语句出错有两种情况: - 语法有问题,exec时报错,所有语句取消执行,没有对数据造成影响。 - 语法本身没错,但适用对象有问题(比如 zadd 操作list对象),exec之后,会执行正确的语句,并跳过有不适当的语句,对数据会造成影响,这点由程序员负责。

2. 乐观锁

redis的事务中启用的是乐观锁,只负责监测key没有被改动,如果在事务中发现key被改动,则取消事务。使用watch命令来监控一个或多个key,使用unwatch命令来取消监控所有key。

# 示例
watch key
muitl
操作数据...
exec
unwatch

模拟抢票,场景:用户买一张票,扣掉100元

# 在zhangsan买票过程中,在提交事务前一瞬间,有人成功买到票,ticket已经改变(即使ticket还有票),导致zhangsan抢票失败。
127.0.0.1:6379> watch ticket 
OK 
127.0.0.1:6379> multi 
OK 
127.0.0.1:6379> decr ticket
 QUEUED 
127.0.0.1:6379> decrby zhangsan 100 
QUEUED 
127.0.0.1:6379> exec
 (nil) 
127.0.0.1:6379> get zhangsan "1000" 
127.0.0.1:6379> get ticket "2"
 127.0.0.1:6379> unwatch 
OK
# lisi在买票整个过程都没有人抢票,所以lisi一次抢票成功。 
127.0.0.1:6379> watch ticket 
OK 127.0.0.1:6379> multi
 OK 127.0.0.1:6379> decrby lisi 100 
QUEUED
 127.0.0.1:6379> decr ticket
 QUEUED 127.0.0.1:6379> exec 
1) (integer) 700
 2) (integer) 1 
127.0.0.1:6379> unwatch

Redis系统 管理

redis属于内存数据库,速度很快,对于有键值对数据存储需求的是非常便利的。

redis数据库的备份和还原也非常的方便。

数据库的备份

1、redis数据库备份(导出)

首先进入redis数据库的控制台。

redis-cli

然后输入备份命令

save

查看一下导出的文件(dump.rdb)

2、redis数据库还原(导入)

首先进入redis数据库的控制台。

redis-cli

然后输入如下命令,获取redis的安装路径

CONFIG GET dir

然后在bin的同级目录下找到var,把备份的数据文件(dump.rdb)复制到var目录下,然后重启redis服务即可。

redis修改密码

redis安装后,默认密码为空,如果要修改密码

设置验证密码的方式有两种

第一种直接在redis里面设置

[root@localhost ~]# redis-cli 
#执行配置命令
127.0.0.1:6379> config set requirepass root
OK
 #返回OK后即代表配置成功,这个时候再执行查看密码命令
127.0.0.1:6379> auth  root
OK

查看密码:

打开redis-cli.exe文件,输入下面的命令

auth "root"  //auth是认证的意思; "root"是redis的密码
config get requirepass //查看redis密码
这种配置方式存在一个很严重的问题,就是当我们将Redis服务器关掉之后,这些配置就会失效,
下次再启动服务器,需要重新设置!

第二种方法:永久有效

打开redis.windows-service.conf配置文件中找到requirepass这个参数,设置参数密码,然后保存配置文件,重启Redis。

img

本来这种方式非常简单,但是在实际过程中,却遇见了一些问题,那就是配置不生效,

明明配置文件里都已经配置了密码,但是还会报错,后来在Redis启动时发现,Redis报错了:

后来查阅之后才知道,原来Redis启动时需要指定配置文件,否则还会使用默认配置,

而我在Windows里启动.exe应用程序时,还是习惯性的双击应用程序启动,导致Redis一直使用的是默认配置。 这样我们就需要在命令行窗口通过命令行的方式来启动并指定配置文件:

img

技能抽查专项

学生管理系统》学生信息数据如表6-2-1所示,其中每条数据都是以sno为key的哈希数据。请按以下设计完成数据的导入以及数据操作等任务。

sno sex class name
202015310301 310301 Male 1 Jim
202015310302 310302 Female 3 Polo
202015310303 310303 Male 1 Green
202015310304 310304 Male 2 Iron
202015310305 310305 Female 1 Kyo
202015310306 310306 Male 4 Ryo
202015310307 310301 Female 5 Lily
202015310308 310302 Male 1 Lucy
202015310309 310303 Female 2 Kobe
2020153103100 310304 Female 3 Oneal
202015310311310305 Female 2 Jane
202015310312 310306 Male 1 Sio
202015310313 Male 5 Leo
202015310314 Female 4 Nemo
202015310315 Male 1 Pac
202015310316 Male 2 Dime
202015310317 Male 4 Shady
202015310318 Female 3 Slim
202015310319 Male 4 Dre
202015310320 Male 2 Snoopy
202015310321 Male 1 Jay

任务一:非关系型数据库的读写

  • 导入学生信息数据到redis;
  • 查询字段sno为“202015310312”的sex;

新增一条数据,sno为“202015310322”,sex为“Male”,class为“3”,name为“Jone”;

创建事务,将学号尾号为“13”,“14”,“16”的class设置为“1”,并查询这些数据的name,运行事务。

任务二:非关系型数据库的备份与恢复

  • 备份当前redis数据库的数据至“rdb”;
  • 将“rdb”中备份的数据恢复至redis数据库中。

非关系数据库读写

# 示例 
127.0.0.1:6379>hmset 202015310322  sex  Male  class 3  name Jone
OK
127.0.0.1:6379>hgset 202015310322  sex 
127.0.0.1:6379>multi
127.0.0.1:6379>hmset 202015310313 class 1
127.0.0.1:6379>hmset 202015310314 class 1
queue
127.0.0.1:6379> hmset 202015310316 class 1
queue
127.0.0.1:6379>exec
OK
127.0.0.1:6379>discard

Redis压测

redis做压测可以用自带的redis-benchmark工具

img

压测命令:redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000

img

压测需要一段时间,因为它需要依次压测多个命令的结果,如:get、set、incr、lpush等等,所以我们需要耐心等待,如果只需要压测某个命令,如:get,那么可以在以上的命令后加一个参数-t(红色部分):

以下实例同时执行50个并发,10000 个请求来检测性能:

redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -t get

img

set请求

redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -t set

如果我们只想看最终的结果,可以带上参数-q,完整的命令如下:

redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -q

只测试部分数值存取的性能。

redis-benchmark -n 100000 -q script load "redis.call('set','foo','bar')"

执行netstat -an命令,查看Redis相关端口是否有很多。如果很多,重启Redis实例释放连接数,然后重新测试即可。

如果使用.\redis-benchmark -h localhost -p 6379 -c 50 -n 10000

会报如下错误:把本机localhost更改为127.0.0.1

posted @ 2024-07-24 21:43  TechLearn  阅读(3)  评论(0编辑  收藏  举报
写代码、聊产品、懂设计、热爱阅读和分享
http://www.budaos.com