Redis
- NoSql,非关系型数据库
数据库发展历程
单机数据库时代
- 一个应用,一个数据库实例
缓存,水平切分时代
- 增减读写数据库缓存
- 单表数据量过大,采用表水平切分,把一张表分成几张表
读写分离时代
- 有不同的数据库实例,分别负责读写
- 当写数据库完成写数据时,读数据库要完成数据同步
分库分表时代(集群)
- 如果一张表的数据量过大,把一张表的数据分开,用不同的数据库存储
- 访问该表,可能会使用多个数据库,需要切换不同的数据源
非关系型数据库时代(NoSql时代)
- 上面的都是基于关系型数据库实现的:Mysql,Oracle,DB2,sqlserver…
- 如今迎来了非关系型数据库:Redis,Hbasc,MongoDB
- 底层存储机制被改变,不在采用仅以表为存储机制的存储方式,而是采用聚合数据结构存储数据。
- 不仅支持key-value结构,还支持hash,set,list等数据结构。
- Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
Redis特点
- Remote dictionary server,远程字典服务
- 是C语言编写的,开源的,基于内存运行的并支持持久化的,高性能的NoSql数据库,是当前热门的NoSql数据库之一。
- 支持备份
安装
Windows
-
**下载地址:**https://github.com/tporadowski/redis/releases
-
打开一个 cmd 窗口 使用 cd 命令切换目录到 C:\redis 运行:
redis-server.exe redis.windows.conf
-
如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的.
-
这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。
-
切换到 redis 目录下运行:
redis-cli.exe -h 127.0.0.1 -p 6379 //启动服务,指定IP和端口port
-
设置键值对:
set myKey abc
-
取出键值对:
get myKey
Linux
-
**下载地址:**http://redis.io/download,下载最新稳定版本。
-
# wget http://download.redis.io/releases/redis-6.0.8.tar.gz # tar xzf redis-6.0.8.tar.gz # cd redis-6.0.8 # make //需要gcc编译器
-
执行完 make 命令后,redis-6.0.8 的 src 目录下会出现编译后的 redis 服务程序 redis-server,还有用于测试的客户端程序 redis-cli:
make:
Linux 下 make 命令是系统管理员和程序员用的最频繁的命令之一。管理员用它通过命令行来编译和安装很多开源的工具,程序员用它来管理他们大型复杂的项目编译问题。
make 命令像命令行参数一样接收目标。这些目标通常存放在以 “Makefile” 来命名的特殊文件中,同时文件也包含与目标相对应的操作。
当 make 命令第一次执行时,它扫描 Makefile 找到目标以及其依赖。如果这些依赖自身也是目标,继续为这些依赖扫描 Makefile 建立其依赖关系,然后编译它们。一旦主依赖编译之后,然后就编译主目标(这是通过 make 命令传入的)。
现在,假设你对某个源文件进行了修改,你再次执行 make 命令,它将只编译与该源文件相关的目标文件,因此,编译完最终的可执行文件节省了大量的时间。
-
下面启动 redis 服务:
# cd src # ./redis-server
-
注意这种方式启动 redis 使用的是默认配置。也可以通过启动参数告诉 redis 使用指定配置文件使用下面命令启动。
# cd src # ./redis-server ../redis.conf
-
redis.conf 是一个默认的配置文件。我们可以根据需要使用自己的配置文件。
-
启动 redis 服务进程后,就可以使用测试客户端程序 redis-cli 和 redis 服务交互了。 比如:
# cd src # ./redis-cli redis> set foo bar OK redis> get foo "bar"
gcc编译错误?
-
安装好的虚拟机(用磁盘iso文件),其中自带的gcc版本是4.8.5的,版本太低。安装后redis后,执行make命令,会导致编译错误,原因是gcc版本太低导致的。
-
升级gcc版本:
-
[root@localhost redis-6.0.1]# gcc -v # 查看gcc版本 [root@localhost redis-6.0.1]# yum -y install centos-release-scl # 升级到9.1版本 [root@localhost redis-6.0.1]# yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils [root@localhost redis-6.0.1]# scl enable devtoolset-9 bash 以上为临时启用,如果要长期使用gcc 9.1的话: [root@localhost redis-6.0.1]# echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
-
-
再次重新编译即可。
-
或者在安装redis之前再安装一边gcc,也不会出现这个问题。
设置全局启动
#cd redis-6.0.8
#make install //复制命令到usr/local/bin
//启动命令:
redis-server
- 将redis命令,放到bin目录下,以便可以在任何一个目录下启动redis服务。
- 命令被放到了:/usr/local/bin 目录中,其中的命令已经被设置到了环境变量中。
redis基本操作
启动-停止
- 启动:# redis-server [&]
- 停止:kill -9 pid 或者 redis-cli shutdown,通过客户端向服务器发送一个命令,shutdown命令。
按照配置文件启动服务
-
redis提供了服务的配置文件,启动的时候指定配置文件,就可以按照配置文件的设置启动服务
-
[root@bogon /]# redis-server redis-conf & //& 表示后台启动,一般使用,不会占用整个终端
客户端连接服务
- 先启动服务 [root@bogon /]# redis-server &
- 执行命令连接客户端[root@bogon /]# redis-cli ,默认连接本机(127.0.0.1)6379端口的redis服务。
指定连接IP和端口
[root@bogon /]# redis-cli -h Ip -p port
,指定连接的服务器的IP和端口。
退出客户端
127.0.0.1:6379> exit
基本知识
1)测试redis服务的性能
# redis-benchmark
2)查看redis服务是否正常运行
- 127.0.0.1:6379> ping
- 测试正常返回PONG
3)查看redis服务器统计信息
- 127.0.0.1:6379> info [指定段名称]
- 查看[指定段]全部信息
4)redis数据库实例
- 类似mysql的一个个database,redis也有自己的数据库实例。
- 但是redis的数据库实例,只能通过redis服务创建和维护,开发人员不能修改和自行创建数据库实例。
- 默认情况下,redis服务启动的时候,会创建16个数据库实例,并且给这些数据库进行编号,从0开始。可以通过配置文件(redis-conf)指定redis服务启动的时候创建多少个数据库实例。
- 开发使用数据库实例的时候,通过编号进行使用。
- redis的数据库实例,不用维护表结构,本身的内存占用十分少,大约几十kb,所以及时使用不完数据库实例,也不会造成多大的空间浪费。
如何切换数据库实例?
-
redis服务启动默认使用0号数据库实例
-
切换数据库实例:
-
127.0.0.1:6379> select index //idnex是数据量实例编号,从0开始 //切换后 select 1
127.0.0.1:6379[1]>
### 5)数据库基本操作
- ```js
127.0.0.1:6379> dbsize //查看数据库实例key的个数
127.0.0.1:6379> keys * //查看当前数据库实例的所有key
127.0.0.1:6379> flushdb //清空当前数据库实例的所有key
127.0.0.1:6379> flushall //清空所有数据库的key
6)查看数据库配置信息
-
127.0.0.1:6379> config get * //查看所有配置信息 127.0.0.1:6379> config get 配置名称 //查看指定配置信息
5种数据结构
- string ,list(有序),set(无序),hash,zset(排序)
- string是最基本的数据结构,它能存储任何数据类型的数据。包括二进制数据,序列化后的数据,json话的对象甚至是一张图片(最大512M)。
- list是简单的字符串列表,按照列表插入顺序排序。底层是一个链表结构(C语言实现)。
- set无序,不重复
- hash,是一个string类型的key和value映射表,特别适合存储实体类对象。
- zset,也是string类型的元素的集合,不重复。zset的每一个元素都会关联一个分数,redis默认按照分数升序排序集合种元素。
如何存储?
1)关于key的操作命令
keys pattern
- pattern是通配符
- ’ * ',匹配0个或多个字符
- ’ ? ',匹配一个字符
- ’ [ ] ',匹配中括号中的一个字符
- 查看数据库实例中的指定的key们。
exist key [key…]
- 判断key在数据库中是否存在。存在返回1,否则返回0。
- 判断多个key的时候,返回存在的key的数量
move key index
- 移动key到index库中
ttl
- 查看指定key的剩余生存时间
- time to life ,生存时间
- 返回值:
- 如果key不存在,返回-2
- 如果没有设置key的生存时间,返回-1,表示永远不会过期。
expire key xxxxx[s]
- 时间单位是秒,s
- 给指定key设置最大生存时间 。
- 设置成功返回 1
type key
- 查看指定key的数据类型。
rename key newkey
- 重命名指定key
del key [key…]
- 删除指定的key,返回成功删除的key的个数
2)操作数据结构
Sting
String
set key value
- 设置key,值是value
- 如果key已经存在,那么会产生覆盖
setnx key value
- 不重复key设置,如果存在相同的key,则放弃该次操作
- 重复了,返回 0
mset k1 v1 k2 v2 [key value…]
- 批量设置键值对,key和value之间用空格间隔
msetnx k1 v1 k2 v2 [key value…]
- 批量设置,如果有一个重复的key,就放弃整个操作
get key
- 获取key
mget k1 k2 k3 [key…]
- 批量获取key的值,没有返回(nil)
append key value
- 追加value到key中的值上
- 返回追加后的value长度
- 如果key不存在,则创建一个新的key,值为value
strlen key
- 获取指定key的字符串长度
incr key
- 对数值型的字符串,执行+1操作
- 返回+1后的数值大小
- 如果key不存在,则创建一个值为0的key,然后加1,返回1。
- 如果可以不是数值类型的,那么报错:ERR value is not an integer or out of range
decr key
- -1,返回-1后的数值大小
incrby/decrby key offset
- offset,设置的偏移量,对数值型的key进行+/- offset的操作
getrange key startIndex endIndex
- [startIndex,endIndex],闭区间截取
- 截取key对应的字符串从startIndex到endIndex结束的子字符串。下标从0开始
- 返回截取后的子字符串
- 注意:下标可以是负数,负下标表示从右往左开始,从-1开始,依次减小。-5 -4 -3 -2 -1[0 1 2 3 4]
- 例如:name:zhangsan ,getrange name 0 4 == getrange 0 -4
setrange key startIndex value
- 覆盖key从 strartInde 开始 的内容。包括startIndex
- 返回覆盖后的字符串长度。
setex key seconds value
- 设置字符串的同时,设置其最大生存时间
List
List
- 一个key,对应多个value
- 每个元素都有下标,下标从0开始,下标也可以用负数表示,-1表示最后一个元素,往前负数越小。
lpsuh key value [ value …]
- 放入list集合多个value值,底层采用双链表实现。插入顺序采用头插法
- 每次插入值都是从表头插入
lrange key startIndex endIndex
- 获取集合[ startIndex,endIndex ]的数值
- 下标从0开始,支持负下标。-1表示最后一个数。
rpush key elemet element…
- 每次都从尾部插入数值,尾插。
lpop/rpop key
- 从左/右删除一个元素,返回删除元素的值。
lindex key index
- 获取指定key对应集合的index下标处的元素
- 支持负下标,没有的返回(nil)
llen key
- 获取指定key对应集合的长度
lrem key count value
- 当count>0时,从左侧删除集合count个value,否则从右侧删除count个value。
- 当count==0时,从集合删除所有值为value的元素。
ltrim key strartIndex endIndex
- 截取[ strartIndex,endIndex ]的元素,组成新的列表,并重新赋值给key。
- 相当于删除[ strartIndex,endIndex ]外的元素
lset key index value
- 将指定key对应的集合的index下标处的数值设置为value
linsert key before/after pivot value
- 将value插入在指定集合的pivot元素之前/后。
- pivot时第一个遇到的指定数值处,进行插入操作。
Set
Set
- 单key,多value,无序,不可重复
sadd key value [ value ]
- 放入指定key对应的集合value,批量插入
- 返回成功插入的元素个数,重复的值不会被插入
smembers key
- 获取指定集合中的全部元素,元素顺序无序
sismember key member
- 判断集合中member元素是否存在,存在返回 1 ,不存在返回 0
scard key
- 获取集合的长度
srem key member [ member …]
- 删除集合一个或多个值为member的元素
srandmember key [ count ]
- 随机从集合中获取count个元素
- count<0 ,获取的元素可以重复,count>0,获取的元素不重复
smove source dest member
- 将source集合的member元素移动到dest集合中
sdiff key1 key2 [key…]
- 获取第一个key1集合中独有的元素,其他集合都没用的元素
sinter key1 key2 [key…]
- 获取所有集合的交集
sunion key1 key2 [ key…]
- 获取所有集合的并集
Hash
Hash
- 单key-{ field-value,field-value,…}
h[m]set key field1 value1 field2 value2 [ field value…]
- 放入hash集合,多个field-value键值对。
hget key field
- 获取集合的指定field域的数值。
hmget key field1 field2 [feiled…]
- 批量获取filed的值
hgetall key
- 获取集合所有的field-value对
hlen key
- 获取集合的键值对个数
hdel key field field [ filed…]
- 删除集合中一个或多个filed
hexists key field
- 判断集合中是否存在field
hkeys/kvals key
- 获取集合中所有的key/value值
hincrby[ float ] key field int[ float ]
- 将集合中field的值进行加int/float运算,可以整数/浮点数加
hsetnx key field value
- 保护性加入集合中一个field-value
- 如果已经存在,则放弃本次操作,不会产生覆盖。
zset
Zset
- 单key,有序,不可重复,每一个元素都有一个score分数
- 分数可以重复,redis默认按照score升序排序。
zadd key score1 value1 score2 value2 [ score value…]
- 添加一个或多个分数为score值为value的元素加入集合
zrange key startIndex endIndex [ withscores ]
- 查看集合[ startIndex,endIndex ] 的元素。0 -1 查看全部
- 带上withscores,显示分数
z[rev]rangebyscore key min max [ withscore ]
- 根据分数在[min ,max]范围的元素。withscores决定是否显示在结果集中分数。
zcount key min max
- 获取集合分数在[ min,max ]的元素个数
zrem key member1 member2 [ member…]
- 删除集合中一个或多个元素
zcard key
- 获取集合的元素个数
zrank key member
- 获取集合member元素的排名,从0开始,分数从小到大
zrevrank key member
- 分数从大到小,获取集合member元素的排名
zscore key member
- 获取集合member元素的分数
配置文件redis-conf
- 配置一些redis服务端运行时的一些参数
- 如果不使用配置文件,那么redis会按照默认的参数运行
- 如果使用配置文件,在启动redis服务时必须指定所使用的配置文件
1)关于网络配置
port
- redis所在端口号,默认是6379
bind
-
配置客户端连接redis服务时,所能使用的ip地址,默认可以使用redis服务所在主机上任何一个IP都可以;一般情况下,都会配置一个ip,而且通常是一个真实ip
-
如果配置了port和bind,服务端连接服务器的时候,必须指定ip和port。因为redis-cli 默认使用6379端口号连接。
-
redis-cli -h ip -p port //指定ip和端口号连接服务器
-
同理使用,redis-cli关闭服务器也需要指定IP和port。
tcp-keepalive
- 设置服务器的保活时间
- 设置服务器每隔多少秒向客户端发送ACK请求,测试客户端是否挂掉。对于无响应的客户端,则会关闭其连接。
- 如果设置为0,则不会进行保活检测。
2)常规配置
loglevel
- 设置日志级别,开发阶段可以使用debug,生产阶段通常设置notice/warning。默认时notice。
- log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
ALL 最低等级的,用于打开所有日志记录。
TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日志级别,一般不会使用。
DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。
ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
OFF 最高等级的,用于关闭所有日志记录。
-
如果将log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来。例如,如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出,而INFO、DEBUG、TRACE、 ALL级别的log则会被忽略。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。
-
从我们实验的结果可以看出,log4j默认的优先级为ERROR或者WARN(实际上是ERROR)。
logfile
- 设置日志文件
databases
- 设置redis的数据库个数。默认时16个。
3)安全配置
- redis,默认客户端登录时不需要密码的,只要指定IP和port就行了
requirepass
- 默认不设置密码
- 设置登录redis服务器的密码,设置后客户端登录需要指定密码(-a指定)
protected-mode
- 安全模式,默认时yes
- yes/no,当为yes的时候,开启权限和密码等安全验证。
Redis持久化
- redis提供了持久化策略,在适当的实际采用适当的手段把内存中的数据持久化到磁盘中,每次redis服务启动的时候,都会再次从磁盘中记载这些数据。
RDB策略
-
在指定的时间间隔内,redis服务执行指定次数的写操作后,会自动触发一次持久化操作。
-
RDB策略时redis的默认策略,redis服务开启的时候,这种持久化策略就默认开启了。
-
默认策略是:
-
save
-
save 900 1 //15分钟,执行1此写操作 save 300 10 //5分钟,10次 save 60 10000 //1分钟,1万次写操作
-
dbfilename
- 数据持久化到磁盘后,保存数据的文件名称。
- 默认是dump.rdb,也可以自己配置。
dir
-
持久化后保存数据的文件所在的目录。
-
默认是./ 就是redis启动的目录。
-
修改文件名称,save,dir都是在redis.conf配置文件中配置的。
AOF策略
- 采用操作日志来记录每一次的写操作,每次启动redis服务的时候,都会重新执行一边操作日志中的指令。
- 效率很低,redis默认不开启。
appendonly
- 配置AOF是否开启。yes/no
appendfilename
-
AOF策略的操作日志文件名称。
-
# At the date of writing these commands are: set setnx setex append appendonly no # The name of the append only file (default: "appendonly.aof") appendfilename "appendonly.aof"
事务
- 把一组数据库操作放在一起执行,保证操作的原子性,要么同时成功,要么同时失败。
redis事务
- 允许把一组数据库操作(redis命令)放在一起执行,把命令进行序列化,然后一起执行吧,保证部分原子性。
- 他不能完全保证原子化操作,只能保证部分原子性。
1)multi:
- 用来标记一个事务的开始。
- 单独执行这个命令表示开启事务。
- 之后执行的命令都会加入命令队列,等待exec,然后统一执行。
2)exec:
- 用来事务队列中所有的命令
部分原子性
- 1)如果一组命令中,在压入命令队列的过程中,发生了致命错误,那么整个队列的命令都不会执行。比如,在压入队列过程中,发现了命令语法错误,就会导致整个队列命令的不执行。
- 2)如何命令在压入过程中正常,而在执行过程中发送错误。那么只会影响发送错误的命令,而不会影响其他的命令。
- 3)编译过程错误具有原子性,而运行过程错误不具有原子性。
3)discard :
-
放弃队列中的命令。弹出队列中的命令,不再执行。
-
清除所有已经压入队列的命令,并且结束整个事务。
-
multi set k1 v1 set k2 v2 discard //放弃前面的命令,并结束事务。
4)防止并发修改 watch
-
mysql中:乐观锁
-
对重要的表中的记录都加上一个“锁”字段,用来防止并发修改同一条记录。
-
id balance version(锁字段) //修改余额的操作 update table set balacne=balance-countMoney,version=vsersoin+1 where version=先前查出的version
-
如果version和先去取出的不一样,那么此次操作将不会执行。每次执行成功都会修改veriosn。
-
只有version和修改前取出的version一致的时候才会执行修改操作。保证一次只有一个用户修改一个记录。
-
-
redis中:watch key命令
-
监控某一个键,在事务的执行时,如果这个键发生了变化,则本次事务的放弃执行,所有命令都不会执行。
-
监控的键可能会被其他客户端在本客户端压入事务命令队列的时候,修改了这个键,那么本客户端的事务将会被放弃执行。
-
//客户端1 >set balance 100 >set balance 200 >set version 1 >watch version //开启监控 >multi //开启事务 decrby balcane 80 incr version >exec //客户端2 >incr verison //另一个客户端修改了监控的键version //结果:在客户端1中 >exec (nil)
-
就类似乐观锁,在开启监控的时候取出这个键version,在exec的时候,检查当前的version和之前的是否相同,如果不同,则放弃当前事务。
-
5)unwatch
- 放弃监控所有的键
- unwatch //放弃所有监控的键
笔记
- redis的命令是具有隔离性的,每个客户端之间互不影响,每个客户端执行命令的时候,不会被其他客户端发来的命令打断。
- 除非在使用watch监控的时候,其他客户端修改监控的键,会导致监控该键的客户端事务的放弃。
消息的订阅和发布
- 客户端之间的通信
- redis客户端订阅频道,发布者往频道上发布消息。这样所有订阅该频道的客户端都会收到消息
subscribe
-
subscribe ch1 ch2 …
-
订阅一个或多个频道。
-
psubscribe:支持频道使用通配符
publish
- publish ch message
- 向指定频道发生message消息。
更好的消息订阅和通信:activeMQ2,Rabbit