Redis
什么是nosql
not only SQL
不仅仅是SQL语句
是一个非关系型数据库
泛指非关系型数据库的 随之web2.0互联网的诞生 传统的关系型数据库很难对付web2.0时代
尤其是超大规模的高并发的社区 暴露出来很多的问题 nosql在现在大数据环境下发展迅速
Redis是最快的 而且使我们当下必须要找我的技术
很多非关系型用户的个人信息 社交网络 地理位置 这些数据类型的存储不需要固定的格式
不需要多月的操作就可以横向发展 Map<String,Object> 键值对控制
特点
解耦
1.方便扩展 数据之间没有关系 很好扩展
2.大数据量高性能(Redis一秒写8万次 读11万次 nosql的缓存记录集是细粒度的缓存 性能会比较高)
3.数据类型的多样性(不需要事先设计数据库 随取随用 如果数据量十分大的表 很多人无法设计的)
4.传统的RDBMS和nosql
传统的RDBMS
-结构化组织
-sql
-数据和关系都存在单独的表中
-操作数据定义语言
-严格的一致性
-基础的事务
-.......
nosql:
-不仅仅是数据库
-没有固定的查询语言
-键值对存储 列存储 文档存储 图形数据库
-最终一致性
-CAP定理和base(一定多活) 初级架构师 会这个
-高性能高可用 高可扩展
redis四大类
kv键值对
-
新浪:Redis
-
美团:Redis+tair
-
阿里,百度:Redis+memacache
文档数据库(bson格式和json一样)
- MongoDB 掌握
- 是一个基于分布式文件存储的数据库 c++编写 主要是处理大量的文档
- 是一个介于关系型数据库和非关系型数据库之间的中间的产品.
- 是非关系型数据库中最像关系型数据库的非关系型数据库
列存储数据库
- HBASE
- 分布式文件系统
图关系数据库
存储的不是图形存的是关系 比如:朋友 同时关系
Redis概念
远程字典服务 支持多种语言使用
免费开源 当下最热门的nosql技术之一 被称为结构化数据库
能干嘛
1.内存存储 持久化 内存中的数据是断电即失 所以说持久化很重要(RDB APF)
2.效率高 高速缓存
3.发布订阅系统
4.地图信息分析
5.定时器 计时器 (浏览量)
特性
1.多样的数据类型
2.持久化
3.集群
4.事务
基础命令
启动方式
1.打开Redis配置文件
2.启动 并设置端口号是6379
[root@izbp12nmkl044l6gs1yldoz bin]# redis-server ./zconfig/redis.conf
[root@izbp12nmkl044l6gs1yldoz bin]# redis-cli -p 6379
命令
Redis默认有16个数据库 默认是第0个 各个数据库之间是不相同的 你在零号数据库存入数据在其他数据库是没有这个数据的
基础:
select
DBSIZE
set key value
get key =>value
keys *
127.0.0.1:6379> select 3 //切换数据库
OK
127.0.0.1:6379[3]> DBSIZE//查看数据库中数据条数
(integer) 0
127.0.0.1:6379[3]> set name zxh //往数据库里存入数据 键值对方式
OK
127.0.0.1:6379[3]> DBSIZE
(integer) 1
127.0.0.1:6379[3]> select 7
OK
127.0.0.1:6379[7]> DBSIZE
(integer) 0
127.0.0.1:6379[7]> select 3
OK
127.0.0.1:6379[3]> get name //获取value值
"zxh"
127.0.0.1:6379[3]> keys * //查看当前数据库中所有的key
1) "name"
清空数据库
flushdb 清空当前的数据库中所有的数据
127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> DBSIZE
(integer) 0
FLUSHALL清空所有数据库中的数据
127.0.0.1:6379[3]> set name zxh
OK
127.0.0.1:6379[3]> select 0
OK
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> DBSIZE
(integer) 0
Redis是单线程的
Redis是基于内存操作的 CPU不是Redis的瓶颈 Redis的瓶颈是和机器的内存的网络带宽决定的 既然可以使用单线程来实现 就用单线程了
为什么单线程还那么快?
Redis是将所有的数据全部放在内存中的 所有说 使用单线程去操作的效率就是最高的 多线程存在CPU的上下文切换 这个操作耗时很高。对于内存系统来说说 如果没有上下文切换效率就是最高的 多次读写都是发生在一个CPU下的,在内存情况好的情况下这个方案最佳
Redis-key
expire key time 设置当前的key的过期时间 单位为秒
ttl key 查看当前的key剩余的存活时间
ExISTS key 判断当前的key是否存在
move key 1 移除这个key
127.0.0.1:6379[3]> set name zxh
OK
127.0.0.1:6379[3]> set age 13
OK
127.0.0.1:6379[3]> exists name
(integer) 1
127.0.0.1:6379[3]> type name
string
127.0.0.1:6379[3]> move name 1
(integer) 1
127.0.0.1:6379[3]> keys *
1) "age"
127.0.0.1:6379[3]> expire age 10
(integer) 1
127.0.0.1:6379[3]> ttl age
(integer) 7
127.0.0.1:6379[3]> ttl age
(integer) 5
127.0.0.1:6379[3]> ttl age
(integer) 3
127.0.0.1:6379[3]> ttl age
(integer) -2
127.0.0.1:6379[3]> keys *
(empty array)
i++ i–方法
incr key
decr key
incrby key 10 key+10
decrby key 5 key-5
type key查看当前类型
获取字符串的长度 strlen key
追加字符串 append key “内容” 如果key不存在就新建
127.0.0.1:6379[3]> set amount 0
OK
127.0.0.1:6379[3]> get amount
"0"
127.0.0.1:6379[3]> incr amount
(integer) 1
127.0.0.1:6379[3]> get amount
"1"
127.0.0.1:6379[3]> decr amount
(integer) 0
127.0.0.1:6379[3]> incrby amount 10
(integer) 10
127.0.0.1:6379[3]> get amount
"10"
127.0.0.1:6379[3]> set name zxh
OK
127.0.0.1:6379[3]> strlen zxh
(integer) 0
127.0.0.1:6379[3]> strlen name
(integer) 3
127.0.0.1:6379[3]> append name ",hello"
(integer) 9
127.0.0.1:6379[3]> strlen name
(integer) 9
127.0.0.1:6379[3]>
截取字符串 GETRANGE key head last
替换 setrange key 位置 字符
127.0.0.1:6379[3]> set name zxzxxzx
OK
127.0.0.1:6379[3]> getrange name 0 3
"zxzx"
127.0.0.1:6379[3]> setrange name 1 hhh
(integer) 7
127.0.0.1:6379[3]> get name
"zhhhxzx"
setex 设置一个临时的数据 存在时间
setnx 当这个key不存在时候才能设置 保存原有的数据不被改变
127.0.0.1:6379[3]> set name zxh
OK
127.0.0.1:6379[3]> setnx name hhh
(integer) 0
127.0.0.1:6379[3]> get name
"zxh"
127.0.0.1:6379[3]> setex age 10 10
OK
127.0.0.1:6379[3]> ttl age
(integer) 6
127.0.0.1:6379[3]> ttl age
(integer) 3
127.0.0.1:6379[3]> ttl age
(integer) 1
127.0.0.1:6379[3]> ttl age
(integer) -2
127.0.0.1:6379[3]> keys *
1) "name"
mset 批量创建值
mget 批量获取值
msetnx 注意原子性 如果创建的值不是同时成立 就全部失败
127.0.0.1:6379[3]> mset k1 v1 k2 v2
OK
127.0.0.1:6379[3]> mget k1 k2
1) "v1"
2) "v2"
127.0.0.1:6379[3]> msetnx k1 v3 k3 v3
(integer) 0
127.0.0.1:6379[3]> get k1
"v1"
127.0.0.1:6379[3]> get k3
(nil)
创建一个对象 set user:1{name:zxh,age:2} 设置一个user:1对象 值为json的字符串
这里的key是一个user:{id}:{filed} 如此设计在 Redis是可行的
127.0.0.1:6379[3]> mset user:1:name zxh user:1:age 12
OK
127.0.0.1:6379[3]> mget user:1:name user:1:age
1) "zxh"
2) "12"
getset 先get后set方法 常用与更新
127.0.0.1:6379[3]> getset db redis
(nil)
127.0.0.1:6379[3]> getset db java
"redis"
127.0.0.1:6379[3]> get db
"java"
list
命令与string的命令相似 大部分都是前面加一个L
127.0.0.1:6379> lpush list one two three#存入一个列表 表中存在三个元素
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379> rpush list zero #向右插入一个元素
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
lpop 移除list中第一个元素
rpop 移除最后一个元素
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> rpop list
"zero"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
lindex 通过下标找值
llen返回列表长度
移除指定值 lrem
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> llen list
(integer) 2
127.0.0.1:6379> lpush list three four
(integer) 4
127.0.0.1:6379> llen list
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 four
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
截取元素 reim
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> ltrim list 0 1
OK
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
rpoplpush 移除元素再添加元素到另一个表里
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> rpoplpush list mylist
"two"
127.0.0.1:6379> lrange mylist 0 -1
1) "two"
127.0.0.1:6379> lrange list 0 -1
1) "three"
lset将列表中指定下标的值替换为另外一个值 相当于更新操作
127.0.0.1:6379> lrange list 0 -1
1) "three"
127.0.0.1:6379> lset list 0 item
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
在指定的位置插入一个元素
127.0.0.1:6379> lrange list 0 -1
1) "item"
127.0.0.1:6379> linsert list before item pus
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "pus"
2) "item"
set 集合
基本命令:
127.0.0.1:6379> sadd myset hello world zxh
(integer) 3
127.0.0.1:6379> smembers myset
1) "zxh"
2) "hello"
3) "world"
127.0.0.1:6379> sismember myset hello #判断是否存在这个值 如果存在就返回1
(integer) 1
127.0.0.1:6379> sismember myset mmm
(integer) 0
scard 查询set中元素个数
srem 移除元素
srandmember 随机数
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> srem myset hello
(integer) 1
127.0.0.1:6379> smembers myset
1) "zxh"
2) "world"
127.0.0.1:6379> srandmember myset
"zxh"
127.0.0.1:6379> srandmember myset
"zxh"
127.0.0.1:6379> srandmember myset
"zxh"
127.0.0.1:6379> srandmember myset
"world"
spop 随机弹出一个元素
smove 移除指定元素到另一个set中
127.0.0.1:6379> spop myset
"zxh"
127.0.0.1:6379> smembers myset
1) "world"
127.0.0.1:6379> sadd myset new today
(integer) 2
127.0.0.1:6379> smembers myset
1) "new"
2) "today"
3) "world"
127.0.0.1:6379> smove myset myset2 new
(integer) 1
127.0.0.1:6379> smembers myset2
1) "new"
127.0.0.1:6379> smembers myset
1) "today"
2) "world"
数学集合:
差集 sdiff
交集 sinter
并集sunion
127.0.0.1:6379> sadd key1 a b c d
(integer) 4
127.0.0.1:6379> sadd key2 a b e f
(integer) 4
127.0.0.1:6379> sdiff
(error) ERR wrong number of arguments for 'sdiff' command
127.0.0.1:6379> sdiff key1 key2
1) "d"
2) "c"
127.0.0.1:6379> sinter key1 key2
1) "b"
2) "a"
127.0.0.1:6379> sunion key1 key2
1) "d"
2) "b"
3) "c"
4) "f"
5) "a"
6) "e"
hash表
底层是map集合
127.0.0.1:6379> hset myhash k1 v1 #创建键值对
(integer) 1
127.0.0.1:6379> hget myhash k1
"v1"
127.0.0.1:6379> hmset myhash k1 m1 k2 m2 k3 m3#创建多个键值对
OK
127.0.0.1:6379> hmget myhash k1 k2 k3#显示指定键值对们
1) "m1"
2) "m2"
3) "m3"
127.0.0.1:6379> hgetall myhash #显示所有键值对
1) "k1"
2) "m1"
3) "k2"
4) "m2"
5) "k3"
6) "m3"
127.0.0.1:6379> hdel myhash k1#移除指定键值对
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "k2"
2) "m2"
3) "k3"
4) "m3"
127.0.0.1:6379> hlen myhash#当前表的元素个数
(integer) 2
127.0.0.1:6379> hexists myhash k2#判断键是否存在
(integer) 1
127.0.0.1:6379> hexists myhash k1
(integer) 0
127.0.0.1:6379> hkeys myhash #显示所有的key
1) "k2"
2) "k3"
127.0.0.1:6379> hvals myhash #显示所有的vals
1) "m2"
2) "m3"
zset (有序集合)
在set的基础上添加了一个值
127.0.0.1:6379> zadd salary 2000 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 200 zhang
(integer) 1
127.0.0.1:6379> zadd salary 1000 hui
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf #按工资大小输出值
1) "zhang"
2) "hui"
3) "xiaohong"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "zhang"
2) "200"
3) "hui"
4) "1000"
5) "xiaohong"
6) "2000"
127.0.0.1:6379> zrange salary 0 -1 #显示所有的人
1) "zhang"
2) "hui"
3) "xiaohong"
127.0.0.1:6379> zrem salary hui#移除hui指定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zhang"
2) "xiaohong"
127.0.0.1:6379> zadd myset 1 hello 2 world 3 name
(integer) 3
127.0.0.1:6379> zcount myset 1 3 #获取区间的长度
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2
三大特殊类型
geospatial地理位置
geoadd:添加地理位置和地名 维度 精度
geodist:二地之间的距离 单位自己定义
geohash:不用
geopos: 查看
georadius:以给定的经纬度为中心 找出某一个半径内的元素
georadiusbymember:找出位于指定元素周围的其他元素
hypeloglog基数统计
常用与统计人数 数量上 存在大数据是可能有误差 传统的方式set保存用户id 然后统计元素很麻烦 我们的目的是计数不是保存id
pfadd 添加
pfcount 统计数
pfmerge 将二表中基数放到新表中
127.0.0.1:6379> pfadd key1 a s x c v d g h
(integer) 1
127.0.0.1:6379> pfcount key1
(integer) 8
127.0.0.1:6379> pfadd key2 a b x d c e f
(integer) 1
127.0.0.1:6379> pfcount key2
(integer) 7
127.0.0.1:6379> pfmerge key3 key1 key2
OK
127.0.0.1:6379> pfcount key3
(integer) 11
bitmap位图场景
当你想统计的值只存在两种状态 就可以使用这个位图统计
比如:上班打卡 活不活跃 在不在线等等
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> getbit sign 1
(integer) 0
127.0.0.1:6379> getbit sign 0
(integer) 1
127.0.0.1:6379> bitcount sign
(integer) 2
事务
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中 会按照顺序执行,一次性 顺序性 排他性 执行一些列命令
------------队列 set set set 执行----------
Redis事务没有隔离级别的概念
所有的命令在事务中 并没有直接被执行 只有发起执行命令的时候 才会执行 exec
Redis只有单条命令保存原子性,但是事务不保证原子性
Redis事务:
开启事务 multi
命令入队 …
执行事务 exec
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v1"
4) OK
放弃事务 discard 当你数据添加有问题 可以放弃这次事务提交
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get k4
(nil)
事务提交或者放弃之后 需要重新开始事务 因为Redis事务提交或者放弃等于结束事务
两种错误:
编译型异常:代码有问题 命令有错 事务不会执行
运行时异常:比如逻辑错误 1/0 逻辑错误不管继续执行其他命令
悲观锁和乐观锁
悲观锁:
- 顾名思义 很悲观 认为什么时候都会出问题 所以无论做什么都加锁
乐观锁:
- 很乐观 认为什么都不会出问题 所以不上锁 更新数据时候去判断一下再此期间是否有人改动数据
- 获取version
- 更新时比较version是否前后一致
Redis检测模式:watch
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
当你的数据执行失败时候 很大可能是因为你锁住的数据发生了改变 导致的
这个时候 需要unwatch 放弃锁 然后再次watch 就可以了
jedis
我们使用java操作Redis
什么是jedis?
是Redis官方推荐的java连接开发工具 使用java操作Redis中间件 如果你要使用java操作Redis 那么必须对jedis熟悉
测试:
1.导入依赖
2.编码测试
package com.zxh;
import redis.clients.jedis.Jedis;
public class TestPing {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
}
}
事务:
package com.zxh;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TestPing {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
Transaction multi = jedis.multi();
try {
multi.set("user1","hello");
multi.set("user2","world");
multi.exec();
}catch (Exception e){
multi.discard();
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();
}
}
}
配置文件中常用的一些配置
常用配置 | 意义 |
---|---|
bind 127.0.0.1 | 绑定的ip |
protected-mode yes | 保护模式 |
port 6379 | 端口设置 |
daemonize yes | 守护进程的方式运行 默认是no 需要开启 |
pidfile /var/run/redis_6379.pid | 后台方式运行需要指定一个pid文件 |
loglevel notice | 日志 |
logfile “” | 日志的文件名 |
database 16 | 数据库的数量 默认是16个 |
always-show-logo yes | 是否显示log |
save 900 1 / 300 10 /60 1000 | 快照 判断什么时候进行持久化操作 |
stop-writes-on-bgsave-error yes | 持久化出错时 是否还要继续工作 |
rdbcompression yes | 是否压缩rdb文件 需要消耗CPU资源 |
rdbchecksum yes | 保存rdb文件时 进行错误的检测校验 |
dir ./ | rdb文件保存的目录 |