Redis入门
概述
Redis是什么?
Redis(Remote Dictionary Server),远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
- redis是一个key-value存储系统。
- Redis 是一个高性能的key-value数据库。
- Redis支持主从同步。
Redis能干什么?
- 内存存储、持久化,内存中的数据是断电即失的,所以说持久化很重要(rdb、aof)
- 效率高,可以用于高速缓存
- 发布订阅系统(MQ消息队列等)
- 地图信息分析
- 计时器、计数器等
- …
学习中需要用的东西
- Redis官网 https://redis.io/
- Redis中文网 http://www.redis.cn/
- 下载地址(通过官网下载即可,Windows版通过Github下载)
安装
Windows安装
-
压缩包解压到指定自定义目录即可
- 双击运行
redis-server.exe
即可启动Redis
- 使用
redis-cli.exe
连接数据库
Redis在Windows的安装确实简单,Redis官方推荐使用Linux环境
Linux安装
- 将压缩包复制到Linux中(推荐/usr/local或者/opt目录下),使用
tar -zxvf 压缩文件名
解压即可
-
基本环境的安装
yum install gcc-c++ #安装gcc环境 make #使用make编译文件 make install #使用make install确认安装(可以不执行)
-
redis的默认安装路径
/usr/local/bin
- 将解压目录中的
redis.conf
配置文件复制到/usr/local/bin/myconf
目录下(myconf目录为新建目录,命名随意)
-
redis不是默认后台启动的,修改配置文件改为后台启动
vi redis.conf #编辑redis配置文件
找到
daemonize no
修改为daemonize yes
即可
-
启动redis服务
redis-server myconf/redis.conf #使用redis-server命令并显式的指定配置文件启动redis服务
使用ps -ef | grep redis
可以查看redis已经启动成功,运行在6379端口
- 使用redis-client进行测试
-
如何关闭redis服务
redis-client连接状态是执行
shutdown
指令
执行后提示not connected
代表已经断开连接,也就是redis-server已经停止
可以使用ps再次查看进程验证
基础知识
- redis有16个数据库
默认使用的是第0个
可以使用select
进行切换
-
redis是单线程的
Redis是很快的,官方表示Redis是基于内存操作,CPU不是Redis的性能瓶颈,Redis的瓶颈在于机器的内存和网络等带宽,既然单线程可以实现功能并且不影响性能,就是没必要使用多线程。(后来支持多线程的原因是在读写内存时会产生内存阻塞,比如删除一个大文件会卡)
Redis是C语言写的,官方提供的数据为100000+的QPS,这不比同样使用key-value的Memecache差!
Redis单线程为什么还这么快?
- 误区一:高性能的服务器一定是多线程的?
- 误区二:多线程(CPU上下文切换)一定比单线程效率高?
核心:redis是将所有的数据全部放在内存中,所以说使用单线程去操作效率就是最高的。(对于内存系统来说如果没有CPU上下文操作,效率就是最高的。)
五大数据类型
官方解释:
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
Redis-key
127.0.0.1:16379> ping
PONG
127.0.0.1:16379> set name zhangsan #设置一个key的值
OK
127.0.0.1:16379> get name #获取指定key的值
"zhangsan"
127.0.0.1:16379> set age 18
OK
127.0.0.1:16379> set sex 0
OK
127.0.0.1:16379> keys * #查看当前数据库中所有的key
1) "name"
2) "sex"
3) "age"
127.0.0.1:16379> del sex #删除当前库中的指定key
(integer) 1
127.0.0.1:16379> keys *
1) "name"
2) "age"
127.0.0.1:16379> move name 1 #移动key到指定数据库中
(integer) 1
127.0.0.1:16379> keys *
1) "age"
127.0.0.1:16379> select 1
OK
127.0.0.1:16379[1]> keys *
1) "name"
127.0.0.1:16379[1]> flushall #清空所有数据库中的key
OK
127.0.0.1:16379[1]> select 0
OK
127.0.0.1:16379> keys *
(empty array)
String
90%的Java程序员使用redis只会使用一个String类型!
127.0.0.1:16379> set key1 v1 #设置值
OK
127.0.0.1:16379> get key1 #获取值
"v1"
127.0.0.1:16379> exists key1 #判断值是否存在
(integer) 1
127.0.0.1:16379> append key1 hello #追加字符串,如果指定的key不存在,则等于set key
(integer) 7
127.0.0.1:16379> get key1
"v1hello"
127.0.0.1:16379> strlen key1 #获取字符串长度
(integer) 7
############################################################################################
127.0.0.1:16379> set count 100
OK
127.0.0.1:16379> get count
"100"
127.0.0.1:16379> incr count #值自增1
(integer) 101
127.0.0.1:16379> incr count
(integer) 102
127.0.0.1:16379> decr count #值自减1
(integer) 101
127.0.0.1:16379> get count
"101"
127.0.0.1:16379> incrby count 10 #自增指定步长
(integer) 111
127.0.0.1:16379> decrby count 5 #自减指定步长
(integer) 106
############################################################################################
127.0.0.1:16379> set key1 "hello, zadaya!"
OK
127.0.0.1:16379> get key1
"hello, zadaya!"
127.0.0.1:16379> getrange key1 0 3 #截取字符串(0 3 代表截取0,1,2,3)
"hell"
127.0.0.1:16379> getrange key1 0 -1
"hello, zadaya!"
127.0.0.1:16379> set key2 "abcdefg"
OK
127.0.0.1:16379> get key2
"abcdefg"
127.0.0.1:16379> setrange key2 2 xyz #从指定位置替换字符串
(integer) 7
127.0.0.1:16379> get key2
"abxyzfg"
############################################################################################
#setex(set with expire) #设置过期时间
#setnx(set if not exist) #不存在再设置 --在分布式锁中会常常使用
127.0.0.1:16379> setex key3 30 "v3-30s" #设置一个key并设置了过期时间
OK
127.0.0.1:16379> ttl key3
(integer) 20
127.0.0.1:16379> get key3
"v3-30s"
127.0.0.1:16379> ttl key3 #key已过期(-1为永不过期)
(integer) -2
127.0.0.1:16379> setnx key3 "value3-nx" #执行结果为1代表该key不存在,设置key成功
(integer) 1
127.0.0.1:16379> keys *
1) "key3"
2) "key2"
3) "key1"
127.0.0.1:16379> setnx key3 "value3-nx-new" #可以看到执行结果为0,设置key失败
(integer) 0
127.0.0.1:16379> get key3
"value3-nx"
############################################################################################
#mset #批量设置key值
#mget #批量获取key值
127.0.0.1:16379> keys *
(empty array)
127.0.0.1:16379> mset k1 v1 k2 v2 k3 v3 k4 v4
OK
127.0.0.1:16379> keys *
1) "k4"
2) "k3"
3) "k2"
4) "k1"
127.0.0.1:16379> mget k1 k3 k4 k5
1) "v1"
2) "v3"
3) "v4"
4) (nil)
127.0.0.1:16379> msetnx k1 vv1 k5 vv5 #虽然k5不存在,但k1存在,所以这次操作失败,说明msetnx是一个原子性操作
(integer) 0
############################################################################################
127.0.0.1:16379> mset user:1:name zhangsan user:1:age 2 #可以通过巧妙设置key值来实现对象属性的存储
OK
127.0.0.1:16379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
127.0.0.1:16379>
############################################################################################
#getset #先获取旧值再设置新值
127.0.0.1:16379> getset total 100
(nil)
127.0.0.1:16379> getset total 200
"100"
127.0.0.1:16379> getset total 300
"200"
############################################################################################
#sdiff #差集 #获取源set与多个set间元素的差集
#sinter #交集 #获取源set与多个set间元素的交集
#sunion #并集 #获取源set与多个set间元素的交集
127.0.0.1:16379> sadd set1 a b c
(integer) 3
127.0.0.1:16379> sadd set2 b c d
(integer) 3
127.0.0.1:16379> sdiff set1 set2 #set1相较set2的差集是a
1) "a"
127.0.0.1:16379> sdiff set2 set1 #set2相较set1的差集是d
1) "d"
127.0.0.1:16379> sinter set1 set2 #set1与set2的交集
1) "c"
2) "b"
127.0.0.1:16379> sunion set1 set2 #set1与set2的并集
1) "a"
2) "b"
3) "d"
4) "c"
#应用场景:朋友圈扩展、共同关注对象、商品推荐等等
以上所有操作使用的数据结构都是相同的
String类似的使用场景:value除了是字符串还可以是数字
- 计数器,计时器
- 统计访客数等
- 对象的缓存
List
基本的数据类型,列表(底层双链表)
使用redis LIst我们可以实现栈、队列
127.0.0.1:16379> lpush list1 one #从left侧push一个值到指定list中
(integer) 1
127.0.0.1:16379> lpush list1 two
(integer) 2
127.0.0.1:16379> lpush list1 three
(integer) 3
127.0.0.1:16379> lrange list1 0 -1 #获取指定list的0到-1位置的所有元素(-1代表最后一个)
1) "three"
2) "two"
3) "one"
127.0.0.1:16379> rpush list1 four five #从right侧push一个值到指定list中
(integer) 5
127.0.0.1:16379> lrange list1 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
5) "five"
############################################################################################
127.0.0.1:16379> lpop list1 2 #从left侧pop出2个元素
1) "three"
2) "two"
127.0.0.1:16379> rpop list1 1 #从right侧pop出1个元素
1) "five"
127.0.0.1:16379> lrange list1 0 -1
1) "one"
2) "four"
############################################################################################
127.0.0.1:16379> rpush list2 one two three four five six seven eight
(integer) 8
127.0.0.1:16379> lindex list2 3 #索引指定位置的元素
"four"
127.0.0.1:16379> lindex list2 0
"one"
############################################################################################
127.0.0.1:16379> llen list2 #获取list的长度
(integer) 8
127.0.0.1:16379> lrange list2 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
6) "six"
7) "seven"
127.0.0.1:16379> lrem list2 2 one #移除list中指定个数的value元素(这里指定移除2个,但list中只有1个)
(integer) 1
127.0.0.1:16379> ltrim list2 2 5 #截断list,通过下标截取指定长度保留
OK
127.0.0.1:16379> lrange list2 0 -1
1) "three"
2) "four"
3) "five"
4) "six"
############################################################################################
127.0.0.1:16379> exists list2
(integer) 1
127.0.0.1:16379> exists list3
(integer) 0
127.0.0.1:16379> lset list2 3 4 #设置list指定下标的值
OK
127.0.0.1:16379> lrange list2 0 -1
1) "one"
2) "two"
3) "three"
4) "4"
5) "five"
6) "six"
7) "seven"
8) "eight"
127.0.0.1:16379>
Set
Set中的值是不能重复的
127.0.0.1:16379> sadd myset1 hello #向指定set中添加值
(integer) 1
127.0.0.1:16379> sadd myset1 zadaya
(integer) 1
127.0.0.1:16379> smembers myset1 #获取set中所有元素
1) "zadaya"
2) "hello"
127.0.0.1:16379> sismember myset1 hello #判断set中是否包含指定元素
(integer) 1
127.0.0.1:16379> sismember myset1 hel
(integer) 0
############################################################################################
127.0.0.1:16379> scard myset1 #获取set中的元素个数
(integer) 2
127.0.0.1:16379> smembers myset1
1) "world"
2) "zadaya"
3) "hello"
127.0.0.1:16379> srem myset1 zadaya #移除set中指定元素
(integer) 1
127.0.0.1:16379> smembers myset1
1) "world"
2) "hello"
############################################################################################
#set无序不重复集合,实现随机抽取功能
127.0.0.1:16379> sadd myset2 a b c d e f g h i g k l m n
(integer) 13
127.0.0.1:16379> srandmember myset2 3 #从set中随机抽取指定个数的元素
1) "b"
2) "k"
3) "f"
127.0.0.1:16379> srandmember myset2 3
1) "m"
2) "b"
3) "h"
Hash
Map集合,key-value中这个value就是一个Map集合,本质和String没有什么区别,内部依然是key-value方式存储
127.0.0.1:16379> hset mh1 name zhangsan age 16 #为指定hash设置字段和值
(integer) 2
127.0.0.1:16379> hget mh1 name #获取指定hash指定字段的值
"zhangsan"
127.0.0.1:16379> hmget mh1 age age name #批量获取指定hash指定字段的值
1) "16"
2) "16"
3) "zhangsan"
127.0.0.1:16379> hgetall mh1 #获取指定hash所有字段和值
1) "name"
2) "zhangsan"
3) "age"
4) "16"
127.0.0.1:16379> keys *
1) "mh1"
127.0.0.1:16379> hset mh1 email 123@163.com
(integer) 1
127.0.0.1:16379> hexists mh1 email #验证指定hash是否包含某一字段
(integer) 1
127.0.0.1:16379> hexists mh1 emailxx
(integer) 0
############################################################################################
#只获取所有的field
#只获取所有的value
127.0.0.1:16379> hkeys mh1 #获取指定hash中所有field
1) "name"
2) "age"
3) "email"
127.0.0.1:16379> hvals mh1 #获取指定hash中所有value
1) "zhangsan"
2) "16"
3) "123@163.com"
############################################################################################
127.0.0.1:16379> hgetall mh1
1) "name"
2) "zhangsan"
3) "age"
4) "16"
5) "email"
6) "123@163.com"
127.0.0.1:16379> hincrby mh1 age 2 #指定hash中指定field(Integer类型)的自增指定步长
(integer) 18
127.0.0.1:16379> hincrby mh1 age -2
(integer) 16
应用场景:Hash适合存储对象,尤其是用户信息之类这种经常变动的信息
所以Hash适合存储对象,Sting更加适合字符串存储
Zset
Zset-有序集合,在Set的基础上增加了一个值作为排序字段(set k1 v1, zset k1 score1 v1)
127.0.0.1:16379> zadd zs1 1 one #为指定zset新增1个值并指定其位置(排序字段)
(integer) 1
127.0.0.1:16379> zadd zs1 2 two 3 three #为指定zset新增多个个值并指定其位置
(integer) 2
127.0.0.1:16379> zrange zs1 0 -1 #获取zset中某一段位置上的值
1) "one"
2) "two"
3) "three"
127.0.0.1:16379>
############################################################################################
#排序命令一定是min~max,也就是两个参数分别是最小值和最大值,不可颠倒
127.0.0.1:16379> zadd salary 2500 xiaohong 5000 zhangsan 1500 lisi
(integer) 3
127.0.0.1:16379> zrange salary 0 -1
1) "lisi"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:16379> zrangebyscore salary -inf +inf #按照排序值从小到大获取指定范围内的值(-INF:无穷小; +INF:无穷大)
1) "lisi"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:16379> zrangebyscore salary 2000 5000 #按照排序值从小到大获取指定范围内的值(2000~5000)
1) "xiaohong"
2) "zhangsan"
127.0.0.1:16379> zrangebyscore salary 2000 5000 withscores #获取指定范围内的值和其排序值
1) "xiaohong"
2) "2500"
3) "zhangsan"
4) "5000"
############################################################################################
127.0.0.1:16379> zrange zs1 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
6) "six"
7) "seven"
8) "eight"
127.0.0.1:16379> zrem zs1 three #从指定zset中移除指定元素
(integer) 1
127.0.0.1:16379> zrange zs1 0 -1 withscores
1) "one"
2) "1"
3) "two"
4) "2"
5) "four"
6) "4"
7) "five"
8) "5"
9) "six"
10) "6"
11) "seven"
12) "7"
13) "eight"
14) "8"
127.0.0.1:16379> zcount zs1 1 5 #获取zset指定区间成员数量
(integer) 4
应用场景:需要排序的数据比如成绩表、工资表,带权重的数据比如重要通知、消息提醒,还有排行榜等等
三种特殊数据类型
Geospatial 地理位置
#geoadd 添加地理位置
#参数 key value<经度 纬度 名称>
127.0.0.1:16379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:16379> geoadd china:city 121.47 32.23 shanghai 106.50 29.53 chongqing
(integer) 2
127.0.0.1:16379> geoadd china:city 114.8 22.54 shenzhen 120.16 30.24 hangzhou 108.94 34.26 xian
(integer) 3
############################################################################################
#geopos 获取指定城市的经纬度
127.0.0.1:16379> geopos china:city chongqing
1) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:16379> geopos china:city beijing shanghai
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "121.47000163793563843"
2) "32.22999976626139329"
127.0.0.1:16379>
#geodist 获取指定城市间的距离
127.0.0.1:16379> geodist china:city beijing shanghai
"966814.3446"
127.0.0.1:16379> geodist china:city beijing shanghai km #可选参数[m|km|ft|mi]默认为m
"966.8143"
############################################################################################
#georadius 获取附近范围内的城市(类似某些软件获取附近的人等)
127.0.0.1:16379> georadius china:city 110 30 500 km #必选参数[m|km|ft|mi]
1) "chongqing"
2) "xian"
Hyperloglog 基数统计
应用场景uv统计,比如用户访问数等
只做了解
Bitmaps 位图场景
位存储,应用场景只有两种状态(0、1)的都可以使用Bitmaps
只做了解
事务
Redis事务的本质:一组命令的集合,一个食物中的所有命令会被序列化,在事务执行过程中,会按照顺序依次执行,不会被外部打断。
--------事务任务队列 set set get del --------
- 一次性
- 顺序性
- 排他性
Redis单条命令是保持原子性的,但是事务不保证原子性!!!
Redis没有隔离级别的概念!!!
Redis的事务
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
- 取消事务(discard)
正常执行事务
127.0.0.1:16379> multi #开启事务
OK
127.0.0.1:16379(TX)> set k1 v1 #命令入队
QUEUED
127.0.0.1:16379(TX)> set k2 v2 #命令入队
QUEUED
127.0.0.1:16379(TX)> get k2 #命令入队
QUEUED
127.0.0.1:16379(TX)> set k3 v3 #命令入队
QUEUED
127.0.0.1:16379(TX)> keys * #命令入队
QUEUED
127.0.0.1:16379(TX)> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK
5) 1) "k1"
2) "k2"
3) "k3"
###################################################
127.0.0.1:16379> multi
OK
127.0.0.1:16379(TX)> set k1 v1
QUEUED
127.0.0.1:16379(TX)> set k2 v2
QUEUED
127.0.0.1:16379(TX)> set k4 v4
QUEUED
127.0.0.1:16379(TX)> discard #取消事务
OK
127.0.0.1:16379> get k4
(nil)
Redis的锁
乐观锁
使用watch命令,可以当做redis的乐观锁,如果加锁期间有其他线程加锁的对象进行了修改,则加锁的事务会执行失败
127.0.0.1:16379> set money 100
OK
127.0.0.1:16379> watch money #开启监控,对money进行监控
OK
127.0.0.1:16379> multi
OK
127.0.0.1:16379(TX)> decrby money 20
QUEUED
127.0.0.1:16379(TX)> exec #执行之前使用其他线程修改money的值,然后执行事务就会失败
(nil)
127.0.0.1:16379> multi
OK
127.0.0.1:16379(TX)> decrby money 20 #开启事务到执行事务时没有其他线程修改money,事务执行成功
QUEUED
127.0.0.1:16379(TX)> exec
1) (integer) 80
127.0.0.1:16379> unwatch #关闭监控