mysql、mybatis、Redis

目录

  • Redis
      • 什么是Redis
      • redis和memcached对比
      • 衍生新数据结构
      • Redis应用场景
      • Redis可执行文件说明
      • Redis三种启动方式
      • Redis客户端连接
      • Redis客户端返回值
      • Redis常用配置
      • 通用命令
      • Redis为什么这么快
      • Jedis
        • jedis连接池
        • 连接池配置
        • timeout waiting for idle object解决思路
      • geo
      • BitMap
      • 发布订阅
      • pipeline
      • 慢查询
        • 命令执行生命周期
      • zset(key->score,element)
      • set
      • 列表
      • hash
      • 字符串
      • Redis持久化
      • 过期键删除和淘汰策略
      • Redis主从复制
      • Redis Sentinel
      • RedisCluster
      • 缓存的使用与设计
      • 分布式唯一id
      • 延时任务
      • 缓存穿透解决方案布隆过滤器
  • mybatis
      • mapper.xml的namespace作用
      • 怎样才能不写实体类的全路径名
      • mapperJava接口中的方法名、参数、返回值对应哪些属性
      • #{值}取的是什么内容
      • 为什么要用resultMap
      • resultMap可以有哪些子标签,可以有哪些属性
      • resultMap的下子标签的property和column属性有什么不同
      • 标签id、result、association、collection可以有哪些属性
      • 关联多表查询时需要用到association与collection,association与collection的区别
      • 一对多和多对一查询有什么不同
      • 一对多使用collection,collection里的对象一般是我们自定义对象,那一对多string时呢,resultmap怎么写,collection怎么写
      • collection标签位置要注意什么
      • insert的parameterType属性和parameterMap属性区别
      • 逻辑分页和物理分页区别
      • MySQL的limit使用分页
      • 动态SQL之if使用
      • where 标签是解决什么问题的,怎么用
      • 什么时候直接使用where而不使用where标签
      • update、set、if配合使用,set标签的好处
      • foreach可以将任何可迭代的对象List、set、()、()
      • foreach标签有哪些属性
      • foreach遍历map
      • foreach遍历list和array有什么不同,什么时候需要open和close
      • mybatis在使用like时要加两个百分号吗
      • where标签和set标签各解决了什么问题
      • @Param怎么用
      • userMapper.searchUser(map)时,select标签要有parameterType属性吗
      • #与$$区别,你什么时候用到过美元符
      • mybatis-plus只需要继承()接口,就能获取各种功能
      • mybatis-plus
      • 标签作用
      • mybatis的insert后返回主键
      • 一级缓存失效情况
      • 二级缓存作用范围
      • 二级缓存数据来源
      • 什么时候用不到二级缓存
      • 二级缓存使用
      • 缓存回收策略有哪些:最近最少使用
      • readonly属性为true或false代表什么
      • 增删改为什么会清空一级缓存,会清空二级缓存吗
      • 新会话SQLsession是先看一级缓存,还是二级缓存
      • mybatis整合第三方缓存
      • 整合ehcache时,配置的实现了cache接口的ehcacheCache是由mybatis实现的还是ehcache实现的
  • MySQL
    • SQL
      • replace into 与 insert into 区别
      • drop、truncate、delete三者删除的区别
      • 学生表、课程表、成绩表,查询student表中重名的学生,结果包含id和name,按name升序
      • 在student_course表中查询平均分不及格的学生,列出学生id和平均分
      • 在student_course表中查询每门课成绩都不低于80的学生id
      • 总成绩最高的学生,cid=1 成绩第三高的学生
      • 在student_course表查询各科成绩最高的学生,结果列出学生id、课程id和对应的成绩
      • 在student_course表中查询每门课的前2名,结果按课程id升序,同一课程按成绩降序 这个问题也就是取每组的前N条纪录
      • 题目如下:年、季度、销售
    • 创建高性能索引
      • B-Tree对索引列是顺序组织存储的,所以很适合查找()数据
      • 在三列上建了一个B-Tree索引,根据最后一列的条件查询会用到索引吗
      • 哈希索引只有()才有效
      • 索引的效果:对于非常小的表(),中大型表(),特大型表()
      • 对于某些存储很长字符串的列可以使用()索引
      • 为什么在多个列上建立独立的单列索引大部分情况不能提高查询性能
      • 如何选择索引列的顺序,怎么选择第一列
      • 建索引的列的数据应该尽量()
    • schema与数据类型优化
      • 计数表与汇总表可以优化查询,但是()
      • 整型与字符型,哪个代价更低
      • 字符串存储时间类型好还是timestamp等内建数据类型存储时间类型好
      • 什么时候应该避免列为空
      • datetime 和 timestamp 精确到()
      • timestamp 与 datetime优势比较
      • int 与 bigint 占多少位,换算成多少字节
      • int 有()属性,表示不允许(),可提升一倍上限
      • 为什么浮点运算比decimal快
      • 为什么说float精度丢失
      • decimal开销比较大,可以用()代替
      • 哪些情况使用varchar,哪些情况使用char
      • 为什么列的更新少使用varchar较好
      • timestamp只能表示到()年,datetime可以表示到()
      • 如果要存储比秒更小粒度的时间怎么做
      • 统计24小时内发送的消息数
      • 当有多个事务并发更新计数器会串行执行,怎么解决效率问题
    • mysql优化
      • 外键会增加成本,可以()
      • 怎么理解汇总表,缓存表提升查询性能,举例
      • 缓存可以减少数据库的访问,提升数据库性能,举例哪些缓存

Redis

什么是Redis

  • key-value
  • 数据库
  • 缓存
  • 中间件

redis和memcached对比

Redis支持持久化,memcached不支持
memcached所有值都是简单字符串,redis支持更多数据结构

衍生新数据结构

  • BitMaps:位图
  • HyperLogLog:超小内存唯一值计数
  • GEO:地理位置信息经纬度

Redis应用场景

  • 缓存
  • 计数器
  • 消息队列
  • 排行榜
  • 社交网络
  • 实时

Redis可执行文件说明

  • redis-server:启动Redis服务器
  • Redis-cli:启动命令行客户端
  • Redis-benchMark:性能测试
  • Redis-check-aof:aof文件修复工具
  • Redis-check-dump:rdb文件修复工具
  • Redis-sentinel:

Redis三种启动方式

查看启动情况
ps -ef | grep redis
netstat -antpl | grep redis

  1. 最简单启动:Redis-server,使用Redis默认配置
  2. 动态参数启动:Redis-server – port 1234,设置端口,默认是6379
  3. Redis-server configPath:配置文件启动,将配置写在configpath文件里,推荐使用

Redis客户端连接

Redis-cli -h 192.157.11.8 -p 6379
ping  返回:pong
set  hello  valueworld  返回:ok
get hello  返回:valueworld  

Redis客户端返回值

状态回复

ping
返回:pong

错误回复

hget hello filed1
(error)wrongType operation against

整数回复

incr  hello 
(integer) 1

字符串回复

get hello
world

get hello  foo
world
bar

Redis常用配置

redis.conf文件
./redis-server redis.conf

  • daemonize:是否是守护进程,推荐使用yes
  • port
  • logfile:日志文件名
  • dir:工作目录

通用命令

keys *

MSET key1 "Hello" key2 "World" haha "hehe"
keys ke*
返回:key1,key2
keys ha[g-i]* //第三个字母在g~i之间
返回:haha
keys key? //?代表一个位置长度
返回:key1,key2


dbsize // 计算key的总数
exist keyName
del key [key...]
expire key seconds
ttl key // 查看key还有多长时间过期,若为-1代表key存在且没有过期时间,-2代表已经不存在了
persist key //去掉key的过期时间
type key // 查看key的数据类型,可能是none、string、hash、list、set、zset

Redis为什么这么快

  • 纯内存
  • 非阻塞IO
  • 单线程避免线程间切换

Jedis

<groupId> redis.clients
<artifactId> jedis
<scope> compile
Jedis jedis = new Jedis("127.0.0.1",6379)
jedis.set("hello","world")
jedis.hset("myhash","field1","value1")
jedis.rpush
...

jedis连接池

  • 直连的话需要创建new,关闭jedis对象
  • 连接池只需要归还jedis对象即可,减少开销
GenericObjectPoolConfig poolConfig = new ...
JedisPool jedisPool = new JedisPool(poolConfig, ip, port)

Jedis jedis = jedisPool.getResource()
jedis.close() //pool中的连接close是归还资源,而不是关闭连接

连接池配置

// 应用个数 * maxtotal 不能超过RedisServer的最大连接数
maxTotal:最大连接数,默认8,建议50个,实际可以偏大
maxIdle:允许最大空闲数,默认8,建议等于maxTotal
minIdle:默认为0,可以预热先让连接池有一些连接
jmxEnabled:开启jmx监控,建议开启,默认为true
blockWhenExhausted:资源池用尽之后,调用者是否等待,默认true
maxWaitMillis:最大等待时间,-1表示永不超时,不建议使用-1
testOnBorrow/return:借还连接时,是否做有效性检测ping/pong,默认false,建议false

timeout waiting for idle object解决思路

1.池子连接都被hang住,例如所有连接都执行keys *
2.QPS高,池子太小

geo

// geoadd key longitude latitude member
geoadd cities:locations 116.7 78.9 beijin

BitMap

字符串big对应的二进制
01010101 101010100 01010100
set hello big
getbit hello 23  //24位,最后一位是0

setbit key offset value // value只能是0和1,offset是偏移量
  • 记录活跃用户
userId最好为自增类型的,数值不要超过4294967295(2^32)-1
01000101001001 // 0代表今天没登录,1代表登录了

当活跃用户占总用户数很少的时候,例如:1亿用户只有一个活跃用户,这个时候还不如使用set直接保存活跃用户id即可

发布订阅

新加入的订阅者不能收到之前发布的消息,订阅状态,处于此状态下客户端不能使用除subscribe、unsubscribe、psubscribe和punsubscribe这四个属于"发布/订阅"之外的命令,否则会报错

// publish channel message
publish souhu:tv "hello world"
返回:3,订阅者个数

// subcribe [channel] 一个或多个频道
subscribe souhu:tv

// unsubscribe [channel] 取消一个或多个频道
  • 模拟消息队列,抢消息,只有一个订阅者能收到消息
    由于Redis会将消息发送给所有订阅者,比较棘手
可以使用列表的阻塞拉取

pipeline

mget、mset类似于一次发生多条get、set命令,节省网络开销
而hmset、hmget这样的命令是没有的,这个时候可以考虑pipeline

Jedis jedis = new ...
PipeLine pipeline = jedis.pipelined()
pipelined.hset...
...
pipeline.syncAndReturnAll()
  • 与原生m操作比较
原生m操作都是原子的,不会被插队
pipeline子命令可能会被其他命令插入到之间,但是返回结果是顺序的
集群中,pipeline每次只能作用到一个Redis节点

慢查询

命令执行生命周期

1.client发送命令
2.命令排队
3.执行命令
4.返回结果
  • 慢查询发生在第三阶段,命令执行,例如keys *

当一条命令被定义为慢查询时,会进入到一个队列,先进先出固定长度的队列,队列保存在内存中,不会持久化

// 若想记录所有命令,设置为0,通常不会这么做,<0不记录任何命令
slowlog-log-slower-than=10000,单位是微秒
slowlog-max-len=128
// 配置方法
1.修改配置文件重启,不建议
2.动态配置
config set slowlog-max-len 1000  // 建议设置为1000
config set slowlog-log-slower-than 1000  // 单位是微妙,默认值是10000,建议1毫秒即1000
config rewrite
// 获取慢查询队列,n是多少个命令
slowlog get [n]

// 获取慢查询队列长度
slowlog leng

// 清空
slowlog reset
  • 定期持久化慢查询

zset(key->score,element)

  • 有序集合无重复元素是score还是value,还是value+score
score可以重复,element不能重复
zadd key score1 element1 score2 element2...
zrem key element1...
zscore key element
zincrby key increScore element
zcard key 
zrank key element // 获得元素排名
// zrange key start end [withscores]
zrange key 0 -1 withscores // 获得0到最后一个元素以及分值
zrangebyscore key minscore maxscore [withscores] //升序
zcount key minscore maxscore
// zremrangebyrank key start end
zremrangebyrank key 1 3
zremrangebyscore
  • 实战排行榜
score 可以是 timstamp(新度)、saleCount、followCount
  • 不常用命令
zrevrank 跟zrank相反
...
zinterstore、zunionstore

set

  • 不允许重复元素
  • 无序
  • 支持集合间操作
sadd、srem
sinter、sdiff、sunion
// 将结果不仅返回,而且保存到destKey中
sinter、sdiff、sunion + store destKey
scard // 计算集合大小
sismember // 判断元素是否存在集合
srandmember key count // 随机取出count个元素
spop // 随机弹出一个元素
smembers // 取出所有元素,小心使用,用sscan代替
  • 实战:抽奖系统、点赞、踩…
  • 实战:给用户添加标签

列表

rpush key value1 value2 ...
// lpush
// 在list指定的值前|后插入newvalue
// 思考:当有两个value的时候
linsert key before|after value newValue
lpop key // rpop
// count > 0,删除count个value相等的项,从左到右
// count < 0,从右到左
// count = 0,所有
lrem key count value
// 只保留索引为 start ~ end的值
ltrim key start end
// end 为-1代表最后一个元素
lrange key start end
// i = -1时表示最后一个元素
lindex key i
llen key
lset key i newvalue
  • 实战:TimeLine
// 疑问:会阻塞其他命令吗
blpop key timeout // 阻塞等待
Stack = lpush + lpop
Queue = lpush + rpop
CappedCollection(固定容量) = lpush + ltrim
messageQueue = lpush + brpop

hash

userinfo:1 // key
name = tom // filed = value
age = 40
Date = 201
  • 特点:
    map的map
    small Redis
    所有命令以H开头
hset key filed value
hget key field

hgetall key 所有filed和value
hvals key // 只返回所有value
hdel key field
hkeys key // 返回hash key对应所有的field
hexist key field // 判断key是否有field
hlen key // 获取key的field的数量
hmget key filed1 field2...
hmset key filed1 value1 filed2 value2
  • 现在使用hash实现记录网站 每个用户个人主页访问量
hincrby userinfo:1 pageviewcount count
  • 小心使用hgetall
    牢记单线程
  • string和hash对比
    相似api
get		hget
set setnx		hset hsetnx
del		hde
setnx		hsetnx key filed value
incrby		hincrby
...

字符串

  • 最大存储512M
  • 使用场景:缓存(json串)、计数器、分布式锁、
  • 命令:get、set、incr key、decr key、incrby key num
// 记录网站每个用户个人主页的访问量
incr pageview:userid
// 不管key是否存在,都设置
set key value
// key不存在才设置
setnx key value
// key存在才设置
set key value xx
// mget,mset,批量获取key,同时也是原子操作,比单次get,set效率高,减少网络传输时间
mget key1,key2...
mset key1 value1 key2 value2...
// 但是也不要同时获取或设置10W个key,要分批成1000更好
getset key newvalue // 设置新的值,并返回旧的值
append key value // 将value追加到旧的value
strlen hello // hello键对应的值得字节数

  • 不常用:
// incrbyfloat、getrange
  • 命令时间复杂度
// O(1)
// O(n)
mget、mset

Redis持久化

开启服务的时候,先通过aof恢复,若aof没开启,则载入rdb文件恢复。
将内存数据异步保存到磁盘

  • 两个方式
// 快照
例如:MySQL的dump、Redis的RDB
// 日志
数据更新操作记录到日志
例如:mysql的binlog、Redis的AOF
  • RDB
    rdb备份文件
// 触发机制-三种方式
// 方式一:save。同步,其他命令阻塞等待save执行
// 方式二:bgsave,异步,
可以正常响应客户端,但是fork出新的进程,更多开销,copy-on-write策略
// 方式三:自动,条件达到后触发
例如;save 900 10,900秒有10条改变则生成rdb文件
缺点:频繁生成rdb
配置:
× save 900 10  // 不推荐自动
dbfilename dump-${port}.rdb
dir /bigdiskpath  // 大的硬盘路径
stop-writes-on-bgsave-error yes
rdbcompression yes  // 压缩,建议开启
rdbchecksum yes //采用校验和
可能触发rdb文件生成方式:
1.主从复制,主会自动生成rdb文件
2.debug reload,不需要清空内存的重启
3.shutdown save
  • AOF
RDB现存问题:
耗时耗性能、不可控容易丢失数据
aof三种策略:always、everysec、no
先将命令放到缓冲区,而不是直接写入磁盘,再考虑策略将缓冲区的命令写入到磁盘
always:每条命令都写入到磁盘
everysec:每秒fsync到硬盘,默认是这个,可能会丢失一秒数据
no:使用策略根据操作系统决定
aof重写:文件太大

例子:
1.set hello world
set hello java
set hello hehe
重写:set hello hehe

2.incr count
incr count
重写:set count 2

3.rpush mylist a
rpush mylist b
rpush mylist c
rpush mylist a b c

4.过期失效的不写
触发aof重写的两种方式:
1.客服端发送命令bgrewriteaof,类似于bgsave完成rdb,都是fork出一个子进程去干
从当前数据库中的数据出发,而不是aof文件的改变
2.auto-aof-rewrite-min-size,文件重写需要的大小
auto-aof-rewrite-percentage,aof文件增长率,若为百分之百,则跟上一次大小一样时重写
配置:
appendonly yes
appendfilename “appendonly-${port}.aof”
appendfsync everysec
dir /找一个目录
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 100
  • rdb和aof选择
1.建议关掉rdb,主从复制又会打开
2.若Redis只是当做缓存,数据丢失还可以从mysql加载,其实aof也可以关闭
3.预留部分内存给fork进程做aof文件重写之类的操作
4.建议使用everysec

过期键删除和淘汰策略

Redis可以设置键的过期时间,键过期之后,没有从内存清除,而是存在过期字典中,依然占用内存,Redis提供了三种清除策略:
1.定时删除,设置过期键同时设置一个定时器,对键执行删除操作。
缺点:对CPU造成压力,当很多键同时删除时候

2.惰性删除,放任键过期不管,每次获取键时,检查键是否过期,若过期才删除
缺点:若大量键过期,又没有业务访问,造成内存浪费

3.定期删除:每隔一段时间,对数据库进行一次检查,删除里面过期的键,至于删除多少键,以及检查多少个数据库,由算法决定。
默认100毫秒检查是否有过期的key,有过期的key则删除,这个检查是随机抽查配置个数的key,而不是所有key
难点:频率难以设定
淘汰策略:配置maxmemory设置,当Redis内存使用达到最大内存使用值时,采取一定策略进行内存释放,不单单是过期的数据,而是所有数据

volatile-lru -> 淘汰上次使用时间最早的且使用次数最少的key,只淘汰设定了有效期的key
allkeys-lru -> 所有的key都可以被淘汰
lru默认随机挑5个键,并且从中选取一个最近最久未使用的key进行淘汰,可以通过
maxmemory-samples来设置Redis需要检查key的个数

volatile-lfu -> 驱逐使用频率最少的键,配置了过期时间的key
allkeys-lfu

volatile-random -》 随机淘汰数据,配置了过期时间的key
allkeys-random 

volatile-ttl -> 只淘汰剩余有效期最短的key

noeviction -> 不删除任何数据,不建议使用,内存不够,返回错误

Redis主从复制

单机的问题:
1.机器故障
2.容量瓶颈
3.QPS瓶颈

以下只讨论高可用

数据副本
扩展读性能
数据流向只能是单向,master》slave
实现主从复制两种方式
1.slaveof命令
从Redis执行
slaveof 主Redis的ip+port // 成为从节点
slaveof no one
2.配置,需要重启
slaveof ip port
slave-read-only yes 

不想成为从节点了:slaveof no one

Redis Sentinel

没有Redis Sentinel时,解决服务器故障

主发生故障:
选取一台从成为新的主

从发生故障:
将读压力迁移到另一台从
sentinel monitor myMaster ip port 2
// ping30秒还不通,主观下线
sentinel down-after-milliseconds mymaster 300000
Redis Sentinel

1)每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个PING命令。
2)如果一个实例(instance)距离最后一次有效回复PING命令的时间超过 own-after-milliseconds 选项所指定的值,则这个实例会被Sentinel标记为主观下线。 
3)如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。 
4)当有足够数量的Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态,则Master会被标记为客观下线。
5)在一般情况下,每个Sentinel 会以每10秒一次的频率向它已知的所有Master,Slave发送 INFO 命令。
6)当Master被Sentinel标记为客观下线时,Sentinel 向下线的 Master 的所有Slave发送 INFO命令的频率会从10秒一次改为每秒一次。 
7)若没有足够数量的Sentinel同意Master已经下线,Master的客观下线状态就会被移除。 若 Master重新向Sentinel 的PING命令返回有效回复,Master的主观下线状态就会被移除。
8)确认master已下线后:
9)选举出一个sentinel作为领导,因为一个sentinel就能完成切换
10)通过这个sentinel选举出一个slave作为新的mater,通知其他slave成为新mater的slave,通知客户端主从变化
11)等待master复活成为新的mater的slave
一套sentinel可监控多套master-slave,用master-name作为标识

new JedisSentinelPool(matername,sentinelSet)
客户端并不是操作的sentinel,其实还是操作的Redis-server

master才有客观下线,slave主观下线就好了

sentinel只是配置中心,而不是代理
1.10秒每个sentinel对master和slave执行info
目的:发现slave节点,确认主从关系

2.2秒每个sentinel通过master节点的channel交换信息(pub/sub)
交换对节点的看法和自身的信息,通过_sentinel_:hello频道交互

3.每一秒每个sentinel对其他sentinel和Redis执行ping
心跳检测,失败判定的依据
sentinel内部选举出一个节点完成故障转移

Asentinel向B、C发送命令要求将他设置为领导者
B若没有收到或同意C发来的命令,那么将票投给A,同理C
如果A发现自己的票数超过sentinel集合半数且超过quorum = 2,那么A将成为领导者
如果此过程中多个节点成为领导者,那么等待一段时间重新选举
服务端完成故障转移之后 pub/sub 客户端如何操作

重新初始化:遍历所有sentinel,找出一个可用的,然后获取新master的连接
从节点下线怎么处理:

自定义一个类似于JedisSentinelPool的客户端,订阅三个消息:
从节点晋升为主节点、原主节点降为从节点、主观下线

RedisCluster

  • 为什么需要cluster
1.跟高并发量
2.更大的数据量
3.更宽的网络流量
  • 常用分区方式
顺序分区:例如:Bigtable、hbase
 容易发生数据倾斜,例如前1-100的用户更加活跃
哈希分区:memcache、Rediscluster
 节点取余分区、一致性hash分区、虚拟槽分区(Rediscluster)
节点取余分区:
问题:新增节点导致大量数据迁移
一致性哈希:
所有节点围成一个圈,顺时针寻找自己对应的节点
缺点:依然会有数据迁移,若新增一个节点512之间,5节点只会分摊2节点的数据量,对其他节点不公平
虚拟槽:
16384个槽,0~16383,每个节点管理部分槽,每个节点之间互相通信,知道节点负责哪个槽
CRC16(KEY)&16383找到自己对应的槽
节点有一个配置属性:cluster-enabled=true

A meet B,B响应A
A meet C,C响应A
这个时候A、B、C就是通的了,能够互相感知到对方,可以交换消息,所有节点共享消息

指派槽:
例如:给A指派0-8000,给B指派8000-13000,给C指派13001-16383
原生命令的安装:
1.配置开启节点
cluster-enabled  yes
cluster-config-file  nodes-${port}.conf
cluster-node-timeout  15000  // 15秒
cluster-require-full-coverage yes //若为yes,则需要集群所有节点健康才提供服务,一般为no

启动6个节点:7000~7005
2.meet
7000 cluster meet 7001
7000 cluster meet 7002
...
7000 cluster meet 7005
meet之后,所有节点都能相互感知

3.指派槽
以第一台7000为例:
cluster addslots {01、 。。。5461}
16384个槽都要分配完
4.主从
将7003节点作为7000节点的从
7003 cluster replicate ${node-id-7000}
官方工具安装:rubby
安装ruby
安装Redis-trib	

可视化工具安装:
...
扩容集群:
1.准备新节点
2.加入集群
cluster meet完成

加入新节点的作用
A。迁移槽和数据实现扩容,作为主节点
一:槽迁移计划:
平均槽数量,例如之前三个节点	,每个平均分摊到5460个槽,现在加入一个新节点,
每个节点应该分摊4096个槽,然后三个老节点将部分槽拿出来凑成4096个给新节点
二:迁移数据
B。作为从节点实现故障转移
收缩集群:
1.下线迁移槽
迁移槽到其他节点,通知其他节点忘记该节点,因为该节点已下线
2.忘记节点

3.关闭节点
集群客户端路由:
moved重定向
ask重定向
smart客户端
moved重定向
客户端发送命令到任意节点
根据key计算槽和槽对应的节点
若计算结果就是在本节点,那么直接返回结果,槽命中
若槽不是在本节点,那么回复moved让客户端重定向到正确的节点
ask重定向:
move和ask的区别:
moved,槽已确定迁移
ask,槽还在迁移中
smart客户端原理
1.从集群中选一个可运行的节点,使用cluster slots初始化槽和节点映射
2.将结果保存到本地,为每个节点创建jedispool
3.执行命令,jediscluster缓存了key -》slot -》node关系
一般执行成功,
出错的话:
4.随机选取一个节点,发送命令,一般不能命中,返回moved
5。重新初始化slot-》node缓存,再次发送命令,一般成功
若命令发送超过5次还不成功,则抛出too many cluster redirection
jediscluster基本使用:
Set<HostAndPort> nodeList = new HashSet<HostAndPort>();
nodeList.add(一个cluster节点);
...直到将所有cluster节点添加完毕
JedisCluster jediscluster = new (noedList,timeOut,poolConfig);
jediscluster.执行命令

使用技巧:单例
批量操作:
meget、mset必须在一个槽,这个条件相当苛刻

四种思路:
1.串行mget、mset,即串行get、set每一个key
2.
  • Rediscluster的故障转移
故障发现
故障恢复

缓存的使用与设计

  • 缓存的收益与成本
1.加速读写
2.降低后端负载
例如:前端缓存降低后端服务器负载,业务使用Redis降低mysql负载
大量写合并为批量写,例如,先Redis累加计数器,再批量写入db

成本:
3.数据不一致
和更新策略有关
4.代码维护成本,多了一层缓存逻辑
5.运维成本,Rediscluster
  • 缓存更新策略
1.lru、lfu、FIFO算法剔除,设置maxmemory
2.超时剔除,expire
3.主动更新,订阅消息,发生变化的时候接受到消息主动更新,不是强一致,而是最终一致

建议:超时剔除和主动更新结合,为什么还需要超时剔除呢,如果发送消息的逻辑出意外,超时剔除保证最终也会得到更新
缓存粒度问题:
是缓存部分字段还是所有字段
  • 缓存穿透:大量请求不命中
原因:
1.业务代码自身问题
2.恶意攻击,爬虫等等
如何发现:

解决方法:
缓存空对象
布隆过滤器
缓存空对象:
问题:需要更多的键,设置键的过期时间;
缓存层和存储层短期不一致。可能出现这样的情况,我的存储层短暂出现了问题,这个时候依然返回的是空,数据不一致

伪代码:
cacheValue = cache.get(key)
if(value为空){
	storageValue = storage.get(key)
	cache.set(key,storageValue)

	if(从storage中查出的value是null){
		cache.expire(key,60*5)
	}
}else {
	return cacheValue;
}
布隆过滤器:检索一个元素是否存在一个集合中
问题;现有50亿个电话号码,现在有10万个电话号码,快速准确判断这10万电话号码是否存在于50亿中
类似问题,垃圾邮件过滤
x通过随机映射函数f1、f2、f3,将二进制数组的三处位置将0改为1
y也同样如此
现在要查询z是否在x,y的集合里,z若通过三个映射函数映射到的位置有一处不为1,则z不存在集合里
guava:布隆过滤器工具,单机布隆过滤器
Redis布隆过滤器
Redis分布式布隆过滤器
  • 缓存雪崩
待续
无底洞问题:更多的机器不能保证更高的性能
  • 热点key重建问题:
问题:一万个人同时查询数据,先从缓存查询,然后10000个人都发现缓存没数据,然后10000个人同时查询数据库,造成数据库崩溃
解决1:查询数据库之前加锁,只允许一个人查询数据库,然后再判断缓存是否有数据,若没数据再查询数据库

解决2:永不过期
mysql、mybatis、Redis_第1张图片

分布式唯一id

延时任务

缓存穿透解决方案布隆过滤器

mybatis

mapper.xml的namespace作用

namespace必须对应相应的mapper.java,每一个mapper.xml都会对应一个mapper.java,使用namespace关联起来

怎样才能不写实体类的全路径名

使用alias别名功能

mapperJava接口中的方法名、参数、返回值对应哪些属性

方法名对应id,参数值类型对应parameterType=“int”,返回值类型对应resultType例如User,返回值类型是多表查询的结果时,需要自定义resultMap,这个时候就不是resultType了,而是resultMap

#{值}取的是什么内容

取的是方法参数传进来的变量名称,若参数是对象例如User,则取user中的属性#{username}

为什么要用resultMap

单表查询不需要自定义resultMap,多表查询出现集合,或者一个对象的属性引用另一个对象的时候就需要自定义resultMap了

resultMap可以有哪些子标签,可以有哪些属性

result、collection、association
id:被引用,例如被select的resultMap属性引用
type:指定一个java类型

resultMap的下子标签的property和column属性有什么不同

property指的是Java类的属性,column指的是数据库列名

标签id、result、association、collection可以有哪些属性

id、result、association、collection:都有property,因为必须对应一个pojo的属性
id、result会有column对应数据库的列,collection和association不会有,因为他们对应的是一个java对象,对应的是多个列
id和result对应是一列和一个属性,所以在java这边会有一个javaType属性,在数据库那边有一个jdbcType属性,而association只有java这边的类型,所以会有一个javaType属性,collection也只有java这边的类型,但是是一个ofType的属性

关联多表查询时需要用到association与collection,association与collection的区别

当属性只是一个pojo对象时,选择association,当属性是一个集合时,选择collection,他们里面都使用id和result标签
表示类型的时候,id和result使用jdbcType,association使用javaType,collection使用ofType
collection和association只有property属性,不像id和result既有property属性还有column属性

一对多和多对一查询有什么不同

例如一个user对应多个post,一对多则是一个user对象里面有一个post集合,多对一则是一个post对象里有一个user对象;一对多时,resultMap里面会用到collection子标签表示集合属性,多对一时,resultMap里面会用到association表示另一个对象属性

一对多使用collection,collection里的对象一般是我们自定义对象,那一对多string时呢,resultmap怎么写,collection怎么写


		

themes是list 的属性名,content是数据库字段名

collection标签位置要注意什么

放在最下面,放在result下

insert的parameterType属性和parameterMap属性区别

parameterType是一个java类型,parameterMap是一个定义的parameterMap标签,parameterMap标签有一个type属性指向一个java类型,但是依然要选择java类型的部分属性作为子元素,parameterMap使用parameter作为子标签,parameter有一个property属性,作用就不言而喻了

逻辑分页和物理分页区别

逻辑分页是我们的程序在显示每页的数据时,首先查询得到表中的1000条数据,然后根据当前页的“页码”选出其中的100条数据来显示。
物理分页是程序先判断出该选出这1000条的第几条到第几条,然后数据库根据程序给出的信息查询出程序需要的100条返回

MySQL的limit使用分页

limit 0 10,指的是从0行开始的10条记录
limit 5 20,指的是从5行开始的20条记录

分页思想是查出所有,减掉前面

动态SQL之if使用

if标签有一个test属性,属性值为true或false


    AND author_name like #{author.name}

where 标签是解决什么问题的,怎么用


如果这些条件没有一个能匹配上将会怎样?最终这条 SQL 会变成这样

SELECT * FROM BLOG
WHERE

修改:


where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除

什么时候直接使用where而不使用where标签

where id = #{id}

update、set、if配合使用,set标签的好处

update Author
    
      username=#{username},
      password=#{password},
      email=#{email},
      bio=#{bio}
    
where id=#{id}

set标签好处:帮助省略无关逗号

foreach可以将任何可迭代的对象List、set、()、()

map、数组

foreach标签有哪些属性

item,index,collection,open,separator,close

item表示集合中每一个元素进行迭代时的别名,
index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置,
open表示该语句以什么开始,
separator表示在每次进行迭代之间以什么符号作为分隔 符,
close表示以什么结束

foreach遍历map

List ids = new ArrayList();

ids.add(1);
ids.add(2);
ids.add(3);
ids.add(6);
ids.add(7);
ids.add(9);

Map params = new HashMap();
params.put("ids", ids);
params.put("title", "中国");
 

可以看到collection=“ids”,遍历的是map其中一个为list的key,而不是map的keyset

foreach遍历list和array有什么不同,什么时候需要open和close

collection=“array”
collection=“list”
in (a, b, c)这种需要

mybatis在使用like时要加两个百分号吗

方式一出错:name like “%” #{xxx, jdbcType = varchar} “%”
方式二出错:like ‘%${xxx}%’
方式三:LIKE CONCAT(’%’,#{poetryName},’%’)

因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ’

where标签和set标签各解决了什么问题

where 1 = 1 and name = #{name} ,使用where标签就不用加 1=1了

	  
          
            username = #{username},  
          
          
           sex = #{sex},  
          
          
            birthday = #{birthday},  
          
     

set标签解决了逗号问题

@Param怎么用

@Param(“account”)String account
param没什么用,可以用Map map = new HashMap<>代替

userMapper.searchUser(map)时,select标签要有parameterType属性吗

要有,属性值为 parameterType=“java.util.Map”

#与$$区别,你什么时候用到过美元符

#会预编译,再填入值,$是拼接字符串执行

select ${column} from ${table} where id = #{id}

上述情况就必须使用$拼接了,如果使用#,查出来的是填入的别名,甚至报错

mybatis-plus只需要继承()接口,就能获取各种功能

basemapper

mybatis-plus

标签作用

定义代码片段,然后由引用
例如:

		SELECT
            
        FROM
             apc
        WHERE id = #{id}
	
        apc.id, apc.name, apc.sequence, apc.version_code, apc.status, apc.create_time , apc.type
    

mybatis的insert后返回主键

insert标签加入下面两个属性
keyProperty=“id” useGeneratedKeys=“true”

一级缓存失效情况

SQLsession不同
SQLsession相同,查询条件不同
SQLsession相同,两次查询之间,有增删改
手动清空缓存

二级缓存作用范围

基于namespace

二级缓存数据来源

SQLsession关闭,数据转移到二级缓存中
先从一级缓存中找,一级缓存关闭才去二级缓存

什么时候用不到二级缓存

一级缓存还没有关闭

A1 =  session1.getMapper(A)
A2 = session2.getMapper(A)
A1.getElementById(1)
A2.getElementById(2)

session1.close
session2.close

两个不同的session,用不到一级缓存
两个session都没关闭,也用不到二级缓存
所以会执行两条SQL

二级缓存使用

全局配置,setting-cachedenabled=true
在每个mapper.xml中加cache标签
在select标签使用usercache = true

缓存回收策略有哪些:最近最少使用

先进先出
软引用
弱引用

readonly属性为true或false代表什么

true:只读,mybatis认为用户只读,会直接将引用交给用户,不安全,速度快
false:非只读,mybatis会利用序列化技术clone一份新的数据,安全,低效,pojo需要实现序列化接口

增删改为什么会清空一级缓存,会清空二级缓存吗

标签中默认带flushcache = true
二级缓存也会清空

新会话SQLsession是先看一级缓存,还是二级缓存

二级缓存
先看二级缓存,再看一级缓存

mybatis整合第三方缓存

mybatis提供了一个cache接口,自己实现put,get等方法,默认使用perpetualcache

整合ehcache时,配置的实现了cache接口的ehcacheCache是由mybatis实现的还是ehcache实现的

由mybatis实现的,需要导入mybatis的这个适配器,在mapperXML的cache标签配置

MySQL

SQL

replace into 与 insert into 区别

insert插入一条记录,发现这条记录重复则插入失败
replace插入一条记录,若发现重复,则先删除重复的,再插入

重复判断是根据主键或唯一索引判断

drop、truncate、delete三者删除的区别

drop:drop table 表名

  删除内容和定义,并释放空间。执行drop语句,将使此表的结构一起删除

truncate (清空表中的数据):truncate table 表名

  删除内容、释放空间但不删除定义(也就是保留表的数据结构)。与drop不同的是,只是清空表数据而已。

  truncate不能删除行数据,虽然只删除数据,但是比delete彻底,它只删除表数据。

delete:delete from 表名 (where 列名 = 值)

   与truncate类似,delete也只删除内容、释放空间但不删除定义;但是delete即可以对行数据进行删除,也可以对整表数据进行删除。

1.delete语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存,以便进行进行回滚操作。

2.执行速度一般来说:drop>truncate>delete

3.delete语句是数据库操作语言(dml),这个操作会放到 rollback segement 中,事务提交之后才生效;如果有相应的 trigger,执行的时候将被触发。

4.truncate、drop 是数据库定义语言(ddl),操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发trigger。

5.id为自增时,delete from 整个表,然后再插入记录,id值与之前id保持连续,而truncate重新从1开始

学生表、课程表、成绩表,查询student表中重名的学生,结果包含id和name,按name升序

方式一:group by ‘name’,不行这样只能查出不同的名字和相应组上聚合结果
方式二:

select id,name
from student
where name in (
	select name from student group by name having(count(*) > 1)
) order by name;

先利用group by查出重复的名字,再查出重复名字的其他信息

在student_course表中查询平均分不及格的学生,列出学生id和平均分

select sid,avg(score) as avg_score
from student_course
group by sid having(avg_score<60);

根据每个学生分组,查出其平均分,然后having筛选出平均分

在student_course表中查询每门课成绩都不低于80的学生id

select distinct sid
from student_course
where sid not in (select sid from student_course where score < 80);

查询出有课程分数低于80的学生,再取反

总成绩最高的学生,cid=1 成绩第三高的学生

select sid,sum(score) as sum_score
from student_course 
group by sid
order by sum_score desc 
limit 1;

排序,查第一条记录

select * from student_course
where cid=1 and score = (
    select score from student_course 
    where cid = 1 
    group by score 
    order by score desc limit 2,1
);

将分数排序,使用limit 查出第三高的分数,再查出等于这个分数的学生

在student_course表查询各科成绩最高的学生,结果列出学生id、课程id和对应的成绩

select * from student_course as x 
where score>=(
    select max(score) from student_course as y 
    where cid=x.cid
    );

遍历每条记录,当score大于等于这条记录的课程的最大分数返回

在student_course表中查询每门课的前2名,结果按课程id升序,同一课程按成绩降序 这个问题也就是取每组的前N条纪录

select * from student_course x 
 where 2>(
     select count(*) from student_course y 
     where y.cid=x.cid and y.score>x.score
     ) 
order by cid,score desc;

对于每一个分数,如果同一门课程下只有0个、1个分数比这个分数还高,那么这个分数肯定是前2名之一

题目如下:年、季度、销售

年 季度 销售
1991 1 11
1991 2 12
1991 3 13
1991 4 14
1992 1 21
1992 2 22
1992 3 23
1992 4 24
要求:写一个SQL语句查询出如下所示的结果。

年 一季度 二季度 三季度 四季度
1991 11 12 13 14
1992 21 22 23 24

SQL:

select 年, 
sum(case when 季度=1 then 销售量 else 0 end) as 一季度, 
sum(case when 季度=2 then 销售量 else 0 end) as 二季度, 
sum(case when 季度=3 then 销售量 else 0 end) as 三季度, 
sum(case when 季度=4 then 销售量 else 0 end) as 四季度 
from sales group by 年;

创建高性能索引

B-Tree对索引列是顺序组织存储的,所以很适合查找()数据

范围
例如找出所以以h到k开头的名字

在三列上建了一个B-Tree索引,根据最后一列的条件查询会用到索引吗

不会,只有全值匹配、精确匹配第一列,范围匹配第二列、精确匹配一二列,范围匹配第三列

哈希索引只有()才有效

精确匹配索引所有的列查询
哈希索引在mysql中只有memory引擎支持

索引的效果:对于非常小的表(),中大型表(),特大型表()

对于非常小的表,简单的全表扫描更高效
中大型表,非常高效
建立和使用索引的代价将随之增长,建议分区

对于某些存储很长字符串的列可以使用()索引

前缀索引
前缀索引也是B树索引

为什么在多个列上建立独立的单列索引大部分情况不能提高查询性能

最好的情况也只能是一星索引,查询永远只能发挥出一个列的效率

如何选择索引列的顺序,怎么选择第一列

当不需要考虑排序或分组时,将选择性最高的列放在前面,相同值越多,选择性越低

建索引的列的数据应该尽量()

不为空

schema与数据类型优化

计数表与汇总表可以优化查询,但是()

会增加维护成本

整型与字符型,哪个代价更低

整型

字符串存储时间类型好还是timestamp等内建数据类型存储时间类型好

内建类型

什么时候应该避免列为空

计划在列上建立索引,避免设计成可为空的列

datetime 和 timestamp 精确到()

timestamp 与 datetime优势比较

timestamp只使用datetime一半的存储空间,具有特殊的自动更新能力,,建表时,可以设置insert或update自动更新
但是timestamp允许的时间范围要小

int 与 bigint 占多少位,换算成多少字节

32位与64位,
占4字节与8字节

int 有()属性,表示不允许(),可提升一倍上限

unsigned,负值

为什么浮点运算比decimal快

CPU直接支持原生浮点运算,decimal的高精度运算是mysql自身实现的

为什么说float精度丢失

sout(1.2-1)结果不是0.2,而是0.19999996

decimal开销比较大,可以用()代替

bigint,,例如存储1.5元,可以乘以10等于15角,而1.55,可以乘以100,等于155厘

哪些情况使用varchar,哪些情况使用char

使用varchar:字符串列长度比平均长度大很多,列的更新很少
char适合所有的字符串接近同一个长度,或者很短的字符串

为什么列的更新少使用varchar较好

varchar是变长,只分配必要的空间,比char更节省空间,当行变长时,如果没有足够的空间,会导致分裂页,所以需要更新少才用varchar

timestamp只能表示到()年,datetime可以表示到()

2038
9999

如果要存储比秒更小粒度的时间怎么做

使用bigint,转换一下
也可以使用double存储秒之后的小数部分

统计24小时内发送的消息数

每小时统计一次当前小时的消息数,将过去23小时消息数与现在未完成的消息数相加既得

当有多个事务并发更新计数器会串行执行,怎么解决效率问题

可以添加一百行数据,每次随机选择一个槽进行更新,要获得统计结果只需要聚合查询一下即可
sum(cnt),将所有槽相加

mysql优化

外键会增加成本,可以()

放弃外键,使用冗余字段

怎么理解汇总表,缓存表提升查询性能,举例

统计24小时的登录用户数量:每小时统计一次,将前23小时加起来,再加上未结束的这个小时的数量;
多个事务更新计数器:可以增加多行作为计数器,最终统计计数器之和

缓存可以减少数据库的访问,提升数据库性能,举例哪些缓存

mybatis的一级缓存,二级缓存
Redis缓存
本地缓存

你可能感兴趣的:(技术)