web2.0:
由于上网设备与用户的激增使得网络访问数据量增大导致所有互联网平台压力激增。
这使得整个互联网结构从原来的单点架构{
一个网址->一个web服务器->一个数据库
}转变为分布式架构{
一个网址->
多个代理服务器(用于请求分发{负载均衡},执行机制默认为轮循)->
多个服务器的集群(将服务器分成不同集群:数据库集群,缓存数据库集群,文件集群,缓存集群)
}这解决了原来单点架构中存在的{
应用服务器的cpu及缓存压力--集群方式解决;
分布式解决session问题:{
一、cookie(不安全;网络负担效率低)
二、存在文件服务器或数据库里(产生大量io效率问题)
三、session复制(session数据冗余,结点越多浪费越大)
四、缓存数据库,完全在内存中,速度快,数据结构简单(主流)
}
数据库服务器的的io压力{
mysql单表数据存储量上限1000万,当数据量超1000万则考虑拆表(水平切表,垂直切库)
将数据库集群设计为主从复制以实现读写分离,主写分读
以上解决方案破坏了一定的业务逻辑来换取性能
}
}
NoSQL{
特点:快(基于键值对的存储方式)
使用场景{
高并发;数据量大;可扩展性要求高
}不适用{
需要事务支持
需要即席(马上查,马上有结果)查询
要sql的结构化查询存储
}与memcache区别{
memcache:不支持持久化;支持类型单一;采用多线程+锁机制
redis:支持持久化;支持多种数据结构存储;采用单线程+io复用机制
}
}
数据库排行:https://db-engines.com/en/ranking
redis的基础知识{
五大数据类型:string,list,set,zset(sorted set),hash
每一个命令都是原子性的
为了保证效率,数据均存于内存中
会周期性把更新的数据写入磁盘中或者修改操作写入追加的记录文件,在此基础上主从同步
redis的string类型可以存储二进制数据且数据安全
}
redis安装相关命令
- rpm -Uvh *.rpm --nodeps --force
- gcc -v
- g++ -v
-
- make distclean
- make install
-
- cd /usr/local/bin
前台启动:{
redis -server
端口:6379、
}
后台启动
- mkdir /myredis
- cp /opt/redis-3.2.5/redis.conf /myredis
- cd /myredis
- ll
- vim redis.conf
- :set nu
- 61行注释
- 80行改no
- 128改yes
- :wq
-
- redis-server /myredis/redis.conf
- ps -ef|grep redis
- redis-cli
-
-
- redis-cli -p 端口 -h 指定ip
- ping
-
- 在redis中关闭redis服务:shutdown+ctrl+C
- ps -ef|grep redis
- 在用户层关闭redis:redis-cli -p 6379 shutdown
redis效率高的原因{
底层:单线程+io复用
一个线程在检查多个文件就绪状态时,可以使用select,poll,epoll、进行检测
select:循环机制,不停去检查哪一个文件就绪了(效率极低,99%循环可能都是做无用功)
poll:poll改进select机制,由某一个文件的就绪状态通知,从所有的任务中查找到底是哪个就绪了,然后来完成就绪任务
epoll:epoll是对poll的改进,可以直接获取到底是哪个文件就绪,并且直接处理,不用进行任何询问
}
redis的基本使用{
- redis一共16个库,从0号库开始
- 切换库:select 号数
- 查看库中表数据:dbsize
- 建立数据:set 键1 值1
- 清当前库:flushdb
- 请所有库:flushall
- 查看当前库的所有键值:keys *
- 查看某个键值存不存在:exists 键
- 查看键的值类型:exists 键
- 删除某个键值:del 键
- 设置某一键值10秒过期:expire 键 10
- 查看某一键值还剩多少秒过期(过期时返回-2):ttl 键
string类型命令
- 存(或覆盖)键值:
- set 键1 值1
- 存(不覆盖)键值:
- setnx 键1 值1
- 获取值:
- get 键
- 追加值:
- append 键 追加值
- 查看某个键值存不存在(返回数值大于0表示存在):
- exists 键
- 查看值的长度:
- strlen 键
- 对数值类型数据进行自增:
- incr 键
- 对数值类型数据进行自减:
- decr 键
- 对数值类型数据加100:
- incrby 键 100
- 对数值类型数据减100:
- decrby 键 100
- 批量插入键值对:
- mset 键1 值1 键2 值2 键3 值3
- 批量获取键值对:
- mget 键1 值1 键2 值2 键3 值3
- 批量插入(不覆盖)键值(只要插入键有一个存在值,此语句报废,由于原子性):
- msetnx 键1 值1 键2 值2 键3 值3
- 在某个键值对中取得指定开头,指定长度的子串:
- getrange 键 起始索引 指定长度
- 对键值中的子字符进行替换或在字符串末尾添加字符:
- setrange 键 指定索引 替换字符
- 限时存储键值对:
- setex 键 限时(秒为单位) 值
- 取出指定键的值并且给指定键赋新值:
- getset 键 新值
list类型命令{
底层为双向链表
从左边放数据(实际存储格式:第一个值在做右端,最后一个值在最左端):lpush list 值1 值2 值3
从右边放数据(实际存储格式:第一个值在做左端,最后一个值在最右端):rpush list 值1 值2 值3
取(删除)最左端值:lpop list
取(删除)最右端值:rpop list
显示最左端从0号的4号的所有元素:lrange list 0 4
显示所有数据:lrange list 0 -1
显示指定0号的元素:lindex list 0
展示list的长度: llen list
在指定元素前面插入指定数据:linsert list before 指定元素 带插入元素
从左边删除指定个数的元素:lrem list 指定数量 指定元素
}
set类型命令{
redis中的set可以自动排除重复值,set是一个string类型的无序集合,
其底层是一个value为null的hash表,所以增删查复杂度均为o(1)
查看数据:smembers set
向指定集合中放数据:sadd 自定义set 值1 值2 ... 值4
返回数据长度:scard set集合
删除指定元素:srem set集合 指定值1 指定值2
随机删除并且返回被删除的元素:spop myset
随机展示set集合中的指定个数的元素:srandmember 指定set 指定个数
取两个集合的交集:sinter 自定义set1 自定义set2
取两个集合的并集:sunion 自定义set1 自定义set2
取差集,只取自定义set1中独有的元素: sdiff 自定义set1 自定义set2
取差集,只取自定义set2中独有的元素: sdiff 自定义set2 自定义set1
}
hash类型命令{
redis中的hash是string类型的field和value映射表即键值对集合,适用于存储对象
在redis中存储用户数据的方式:map
向hash表中存储一个对象{
单条输入{
hset 对象名称1 name 名字
hset 对象名称1 age 年龄
hset 对象名称1 phone 电话
}
多条输入:hmset 对象名称2 name 名字值 age 年龄值 phone 电话值
}
取hash集合中指定对象的名字:hget 对象名称 name
查看hash集合中指定对象的指定属性是否存在(返回1表示存在):hexists 对象名称 指定属性
查看hash集合中指定对象的属性(不是属性值):hkeys 对象名称
查看hash集合中指定对象的属性值: hvals 对象名称
对hash集合中指定对象的数值属性进行加值(可以加负值)操作:hincrby 对象名称 age 2
对hash集合中指定对象的属性进行添加(不覆盖)操作
(属性有值,命令不执行,属性没有值,命令执行成功):hsetnx 对象名称 age 77
}
Zset类型命令{
zset是一个有顺序不重复的string类型集合,时间复杂度为o(1)
向zset集合中添加元素:zadd zset名称 评分数值1 值1 评分数值2 值2 评分数值3 值3
展示zset集合中指定下标间的元素(默认按数值从小到大展示):zrange zset名称 起始下标 终止下标
展示zset集合中指定下标间的元素和其评分数值(默认按数值从小到大展示):zrange zset名称 起始下标 终止下标 withscores
展示zset中指定评分间的数据:zrangebyscore zset名称 指定评分1 指定评分2
展示zset中指定评分间的数据和其数值:zrangebyscore zset名称 指定评分1 指定评分2 withscores
展示zset中指定评分间的数据和其数值带分页:zrangebyscore zset名称 指定评分1 指定评分2 withscores limit 开始索引 查询条数
展示zset中指定评分间且评分从大到小的数据和其数值带分页:zrevrangebyscore zset名称 指定评分1 指定评分2 withscores limit 开始索引 查询条数
给指定的值加评分:zincrby zset名称 增加的分数 指定的值
删除zset中的值:zrem zset名称 指定的值
统计指定评分间的元素个数:zcount zset名称 起始评分 终止评分
查询指定值在集合中的排名(从小到大排,从0开始计):zrank zset名称 指定值
}
分页中用于计算开始索引的公式:(pagenum-1)*pagesize
}
linux下的四种安装方式{
1.RPM
rpm -qa|grep 目标软件名称 查询所有安装过rpm软件 | 过滤找到目标软件
rpm -e 目标软件名称 卸载rpm的目标软件
rpm -ivh 目标软件名称全称(包含版本) 安装rpm版本的目标软件
2.Yum
yum list|grep 目标软件 查询已经安装的目标软件
yum install 目标软件 安装目标软件
3.tar
tar -zxvf 目标软件 解压即安装(java,tomcat)
4.mank &&make install
如redis
}
在vim中跳到指定行号:
指定行号+shift+g
redis事务{
redis事务的主要作用就是串联多个命令防止别的命令插队
mysql事务是将多个语句串联不可分割;
mysql隔离级别:1(未提交读),2(已提交读),4(重复读),8(可序列化)
Multi:将命令组队
Exec:将组好队的命令执行
discard:取消命令队
实例一{
组队成功,提交成功
set k1 v1
set k2 v2
exec
}
实例二{
组队报错,提交失败
set k1 v1
set k2
exec
}
实例三{
组队成功,提交失败
1.set k1 v1
2.incr k1 //对字符串v1执行++操作
3.set k2 v2
4.exec
执行结果:只有1、3成功,2是失败的,这与mysql区别在于,mysql要么全部执行,要么全都不执行
}
}
redis事务{
悲观锁{
类比串行
在数据库中有行锁,表锁
}
乐观锁{
支持并行,会对数据添加版本号,在同时操作事务后,执行时就看哪一个事务先提交,只要有一个事务提交,数据版本号马上改变,后面的事务便无法再提交因为版本号不同的原因,在提交事务后,如果再有事务同时操作数据,情况同上。乐观锁多用于数据的读,以提高数据的吞吐量。redis是利用check_and_set机制实现事务
}
实例一:{
set balance 100
watch balance //添加乐观锁
multi
decrby balance 10
incrby balance 10
exec //自动释放乐观锁,执行discard也会释放乐观锁
}
当redis执行已经添加乐观锁的事务时,乐观锁中的事务要么一起执行,要么一起不执行
unwatch对所有的key取消乐观锁
事务三特性:单独隔离的操作;无隔离级别的概念;不保证原子性
}
秒杀案例{
库存表
秒杀成功用户表
使用压力测试工具ab发起get请求{
ab -n 1000 -c 100 http://ip:端口/项目名称/请求路径?username=值
}发起post请求{
先写一个postfile无后缀名称的文件内容为{
username=值&
}
再:ab -n 1000 -c 100 -p postfile -T 'application/x-www-form-urlencoded' http://ip:端口/项目名称/请求路径
}
出现问题一{
问题:超卖
解决方法:加乐观锁
}
出现问题二{
问题:请求并发量超过一定数量,服务器拒绝连接
解决(在测试命令中加-r):ab -n 5000 -c 1000 -r -p postfile -T 'application/x-www-form-urlencoded' http://ip:端口/项目名称/请求路径
}
问题三{
问题:连接超时
解决:设置连接池缓解
几个参数:
MaxTotal:最大连接数
maxIdle:最小空闲连接数
MaxWaitMilis:最大等待时间
testOnBorrow:测试连接可用性
}
问题四{
问题:库存遗留问题
原因:乐观锁导致
解决:使用LUA脚本(嵌入式脚本语言,常用于编写插件、外挂)
注:只有redis2.6以上版本才能使用lua脚本
解决实质:redis通过lua解决争抢问题,实际上是redis利用其单线程的特性,用任务队列的方式解决多任务并发问题
}
}
redis持久化RDB(Redis DataBase){
原理:采用时间间隔的方式进行数据的快照存盘
备份原理:redis把内存数据保存到硬盘的过程中会单独复制一个进程,这个进程会先将数据写到一个临时文件中,等持久化过程结束,再用这个临时文件替换上次持久好的文件
注:RDB方式比AOF方式更加高效,RDB的缺点是最后一次持久化后的数据可能丢失
备份操作{
做备份的命令:
cp dump.rdb dump.rdb.bak
在有部分文件的情况下误删了数据正确的做法是马上停止redis服务器,然后执行
cp dump.rdb.bak dump.rdb
即可恢复数据
}
优点:适合大规模数据恢复;对数的完整性、一致性要求不高,节省磁盘空间,恢复速度快
缺点:Fork时,内存中克隆了一份数据,大约2倍的膨胀性需要考虑,在数据庞大是比较消耗性能,会损失最后一次快照后的所有修改
}
redis持久化(Append Of File){
持久化策略:
以日志的形式记录每个写操作(增量保存),只记录写操作,只要一产生写操作,aof马上记录,
只允许追加文件不可以修改文件,因此aof保存的数据最全,
AOF配置:
由于AOF默认不开启,所以要开启需要到redis.cnf中,找到593行appendonly on设置为yes
AOF回复:
1.自然恢复:
cp appendonly.aof.bak appendonly.aof
2.手动恢复:
vim appendonly.aof然后再appendonly.aof中修改特殊内容(如误删命令flushall),
然后保存appendonly.aof,再重启redis服务器
3.appendonly.aof内容修改有误:
redis服务器无法启动,此时可以通过/uer/local/bin/redis-check-aof --fi appendonly.aof
进行修复,但会导致一部分数据丢失
AOF同步频率设置:
appendfsync always:每次redis的写入都立刻记入日志,性能较差,但数据最全
appendfsync everysec:每秒同步,若服务器宕机,本秒数据丢失
appendfsync no:redis不主动进行同步,把同步时机交给操作系统
rewrite重写机制:
合并同类项{把好几项相同添加操作合并为一句添加操作,且数据总数目不变}
触发重写机制:
auto-aof-rewite-peroentage:设置重写基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)
auto-aof-rewite-min-size:设置重写的基本值,最小文件64MB。达到这个值开始重写
}
主从复制:{
将Master写操作主机和Slave读主机分离,扩展性能,缓解数据库压力
"一主二从"环境搭建:
在一个linux操作系统中:
创建三个配置文件{
include /myredis/redis.conf
pidfile /var/run/redis_指定的三个端口号.pid
port 指定的三个端口号
dbfilename dump指定的三个端口号.rdb
slave-priority 10 //设定优先级别,值越小当备用机优先级别越高
实例{
include /myredis/redis.conf
pidfile /myredis/redis6380.pid
port 6380
dbfilename dump6380.rdb
}
启动三个redis服务器:redis-server /myredis/redis指定.conf
查看进程:ps -ef|grep redis
登录redis数据库后,查看主从状态:info replication
配置主从状态-配从不配主:slaveof 192.168.174.125 6379
主机挂机,重启如初,从机挂机需要重新设置:slaveof ip 端口
}
vim中全局替换:
%s/目标字符串/替换字符串
主从复制原理:
当主机有从机时,主机会启用bgsave将数据存在RDB文件中,从机拿主机的RDB文件替换从机原来的RDB文件
注:
当RDB与AOF同时开启,系统默认取AOF的数据(即使指定输入 cp dump.rdb.bak dump.rdb 命令也只会取aof的数据)
AOF是一个文本文件,对数据不敏感可以使用RDB,不建议单独使用aof,因为可能出现bug,禁用RDB{执行save ""}
}