NoSQL的简介
- 解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN
- 解决扩展性的问题:Struts、Spring、SpringMVC、Hibernate、Mybatis
- 解决性能的问题:NoSQL、Java线程、Hadoop、Nginx、MQ、ElasticSearch
-常见的NoSQL:memcache(不能持久化)、redis、mongoDB(文档型数据库,JSON)
使用NoSQL解决CPU及内存压力和IO压力:
集群中session对象的存储问题,两台服务器需要共享session。
1.存储在客户端(即cookie),这个做法有点牙白,很危险
2.session复制,会造成内存浪费
3.使用NoSQL数据库,不需要IO操作,存在内存中快速访问
切分分离等可能会破坏业务逻辑,所以使用NoSQL作为缓存解决IO压力。
NoSQL数据库特点
非关系数据库,只是简单由key-value的形式存储。不遵循SQL标准、不支持ACID(但支持事务哦!!!)、高性能。
使用场景
适用:对数据高并发的读写、海量数据的读写、对数据高可扩展性的(如秒杀)。\
不适用:需要事务支持、基于sql的结构化查询存储,处理复杂的关系,需要即席查询。
(用不着sql 的和用了sql 也不行的情况,请考虑用NoSql )
tips
for Linux/安装时需要C语言编译环境,其实只需要gcc,直接yum install gcc.
查看默认安装目录:
- redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
- redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲
- redis-check-dump:修复有问题的dump.rdb文件
- redis-sentinel:Redis集群使用
- redis-server:Redis服务器启动命令
- redis-cli:客户端,操作入口
后台启动
cp /opt/redis-3.2.5/redis.conf /myredis
#修改redis.conf(128行)文件将里面的daemonize no 改成 yes
vi redis.conf
#找数据
deamonize
#启动
/user/local/bin
redis-server /etc/redis.conf
#查看进程
ps -ef | grep redis
redis-cli(查看ip
#关闭方法
shutdown
kill 5780(杀掉进程
端口6379,0——15(16个数据库),select 1,所有库密码都一样。
单线程+多路IO复用技术
key操作
1. keys *查看当前库所有key (匹配:keys *1)
2. exists key判断某个key是否存在
3. type key 查看你的key是什么类型
4. del key 删除指定的key数据
5. unlink key 根据value选择非阻塞删除,
仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。
6. expire key 10 10秒钟:为给定的key设置过期时间
7. ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
8. select命令切换数据库
9. dbsize查看当前数据库的key的数量
10. flushdb清空当前库
11. flushall通杀全部库
Redis的常用五大数据类型
String
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。最大不超过512M。
set k1 v100
(返回ok
get k1
(返回“v100”
set k1 v1000
(会覆盖掉
append k1 abc
(返回(interger) 8
strlen k1
(返回(interger) 8
setnx k1 v1
(返回(interger) 0,这个命令不能覆盖或者替换,只有不存在的情况下才能set成功
set k4 500
incr k4
get k4
(返回501
decr k4
(返回500
incrby k4 10
(返回510
decrby k4 20
(返回490
flushdb
(先清空一下)
mset k1 v1 k2 v2 k3 v3
mget k1 k2 k3
msetnx k11 v11 k12 v12
msetnx k11 v11 k13 v13(会失败,因为k11存在,k13也不会存储成功
set name lucymary
get range name 0 3
(返回lucy
set name 3 abc
get name
(返回lucabcry,覆盖相应位置
setex age 20 value30
ttl age
(返回过期时间
getset name jack
(返回lucabcry,但同时会替换
get name
(返回jack
这些是原子操作,因为redis是单线程,它们不会产生影响。
memcache是多线程加锁,所以可能会产生那些脏读幻读等等问题。
List
存储单键多值,是双向链表。
lpush k1 v1 v2 v3
lrange k1 0 2
rpush k2 v11 v12 v13
rpoplpush k1 k2
lrange k2 0 -1
(返回v1 v11 v12 v13, 0到-1就是所有值
linsert k2 after(后面或者before前面 "v11" "new11"
lrange k2 0 -1
(返回v1 new11 v11 v12 v13,在后面插入
linsert k2 before "v13" "new11"
linsert k2 before "v12" "new11"
lrem k2 2 "new11"
lrange k2 0 -1
(返回(返回v1 v11 v12 new11 v13,删掉了两个new11
lpush
转换为
lpop就是v3,rpop就是v1,值在键在,值光键亡。
rpush
转化为
数据结构
快速链表:
首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。当数据量比较多的时候才会改成quicklist。
因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。
多个压缩链表构成一个quicklist。
Set
spop <key>**随机从该集合中吐出一个值。**
srandmember <key><n>随机从该集合中取出n个值。不会从集合中删除 。
smove <source><destination>value把集合中一个值从一个集合移动到另一个集合
sinter <key1><key2>返回两个集合的交集元素。
sunion <key1><key2>返回两个集合的并集元素。
sdiff <key1><key2>返回两个集合的**差集**元素(key1中的,不包含key2中的)
Set数据结构是dict字典,字典是用哈希表实现的。
Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。
Hash
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
类似Java里面的Map<String,Object>
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储。
第一种修改麻烦,第二种存储麻烦,所以用第三种,有key和field两个来确定一个值。
相关命令操作
Zset
Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分( score ) ,这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。
因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
跳跃表实现快速查找
从第2层开始,1节点比51节点小,向后比较。
21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层
在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
在第0层,51节点为要查找的节点,节点被找到,共查找4次。
从此可以看出跳跃表比有序链表效率要高
配置文件详解
默认情况bind=127.0.0.1只能接受本机的访问请求
不写的情况下,无限制接受任何ip地址的访问
生产环境肯定要写你应用服务器的地址;服务器是需要远程访问的,所以需要将其注释掉
如果开启了protected-mode,那么在没有设定bind ip且没有设密码的情况下,Redis只允许接受本机的响应 保存配置,停止服务,重启启动查看进程,不再是本机访问了。
设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。
在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。
注意Linux内核会将这个值减小到/proc/sys/net/core/somaxconn的值(128),所以需要确认增大/proc/sys/net/core/somaxconn和/proc/sys/net/ipv4/tcp_max_syn_backlog(128)两个值来达到想要的效果
axmemory
建议必须设置,否则,将内存占满,造成服务器宕机
设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。
Redis6版本的新数据类型
Bitmaps/HyperLogLog/Geospatial
- Bitmaps
setbit<key><offset><value>设置Bitmaps中某个偏移量的值(0或1)
# 计算出两天都访问过网站的用户数量
bitop and unique:users:and:2020110403 unique:users:and:2020110403
Bitmaps并不是万金油, 假如该网站每天的独立访问用户很少, 例如只有10万(大量的僵尸用户) , 那么两者的对比如下表所示, 很显然, 这时候使用Bitmaps就不太合适了, 因为基本上大部分位都是0。
- HyperLogLog 在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站PV(PageView页面访问量),可以使用Redis的incr、incrby轻松实现。
但像UV(UniqueVisitor,独立访客)、独立IP数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题称为基数问题。
解决基数问题有很多种方案:
(1)数据存储在MySQL表中,使用distinct count计算不重复个数
(2)使用Redis提供的hash、set、bitmaps等数据结构来处理
以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。
能否能够降低一定的精度来平衡存储空间?Redis推出了HyperLogLog
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
用很少的空间计算更多的数据,计算不重复数据,即基数。
pfadd program "java"
pfadd program "php"
pfadd program "java"
(会返回0,因为重复了不增加
pfcount program
((integer) 0
pfmerge k100 k1 program
-geoadd 地理信息的缩写,是二维坐标,就是经纬度。
geoadd china:city 121.47 31.23 shanghai
geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing
geopos [member...] 获得指定地区的坐标值
两极无法直接添加,一般会下载城市数据,直接通过 Java 程序一次性导入。
有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。
当坐标位置超出指定范围时,该命令将会返回一个错误。
已经添加的数据,是无法再次往里面添加的