Redis

  1. MySQL的执行过程
mysql的简单执行过程.png
1. 索引是在 执行引擎 中发挥的作用
2. MySQL查询缓存(内存中)的效率是最高的,所以Redis应运而生!
mysql的完整执行过程.png

Redis、Memcached、MySQL

mysql与memcached与redis.png
  1. 内存管理机制
    1. Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存切割成特定长度的块,存储相应长度的key-value数据记录,以完全解决内存碎片化问题;使用空闲列表(free-list)判断存储状态,比如现有内存块为88 bytes、112 bytes、144 bytes,当有一条100 bytes的数据时,会选择112 bytes的块存储,所以会有12 bytes的碎片;
    2. Redis使用现场申请内存的方式来存储数据,并且很少使用 free-list 等方式来优化内存分配,会在一定程度上存在内存碎片;
    3. 主要在于CPU的内存是连续的,所以容易出现内存碎片;
    4. Memcached和Redis的机制,类似JVM对象的分类:空闲列表、直接内存分配(指针碰撞)
  2. 数据持久化
    1. Memcached不支持内存数据的持久化操作,所有的数据以 in-memory 的形式存储;
    2. redis支持两种数据持久化方案,RDB、AOF
    3. RDB:全量数据备份,备份的是数据
    4. AOF:增量支持化备份,备份的是指令,如set key value
  3. 数据过期机制
    1. Memcached在删除失效主键时也是采用消极方法,内部不会监视主键是否失效,而是在通过 Get 访问主键时才会检查是否已经失效;
    2. Redis采用定时、定期等多种方式检测失效主键,减少内存泄漏;
  4. 数据类型:Memcached支持单一数据类型,而Redis支持多达5种数据类型;
  5. Redis与MySQL相比
    1. 优点:没有Scheme(创建表、字段类型)约束,数据结构变更相对容易,抗压能力强,性能极高,可以达到每秒10万人次访问;
    2. 缺点:没有索引和外键,缺少int/date等基本数据类型,多条件查询集合内联和连接间接实现,开发效率低,可维护性不佳;
    3. 二者通常搭配使用,Redis也可以作为mybatis的二级缓存,而一级缓存则是SqlSession、进程缓存,单次链接有效;

Redis

  1. centOS-7安装redis-4.0.6
    yum install wget //安装wget命令
    wget http://download.redis.io/releases/redis-4.0.6.tar.gz //下载redis-4.0.5
    yum install gcc //安装gcc环境
    mv redis-4.0.6.tar.gz /usr/local/
    tar -zxvf redis-4.0.6.tar.gz
    cd redis-4.0.6
    make MALLOC=libc // 编译
    cd src && make install //安装
    ./redis-server // 启动redis,测试
  2. 三种启动redis的方式
    1. 直接启动:执行 redis-server 命令,进程级别,Ctrl+C关闭进程,redis也随之关闭
    2. 指定配置文件,后台启动:修改redis.conf中的 daemonize 为 yes
      redis-server redis.conf //指定配置文件的方式启动
      ps -ef | grep redis // 查看redis的进程信息
      kill -9 8341 // 根据进程号杀死redis
    3. 开机启动redis
      1. 启动脚本在 redis 的utils目录下:redis_init_script
      2. Linux配置开机自启动:/etc/init.d
        mkdir /etc/redis
        cp redis.conf /etc/redis/6379.conf // 拷贝 redis.conf 到 /etc/redis/6379.conf
        cp utils/redis_init_script /etc/init.d/redisd // 通常以 d 结尾的表示后台自启动服务
      3. 修改启动脚本的级别
          #!/bin/sh
          #
          # chkconfig: 2345 90 10
      
      1. 执行脚本:chkconfig redisd on
      2. 重启系统,redis服务已经启动:ps -ef | grep redis
      3. 启动/关闭redis服务:service redisd start/stop
  3. SSH连接,则需要安装SSH服务,SSH存在的意义就是模拟操作Linux
    1. 检查是否安装了:yum list installed | grep openssh-server
    2. centOS-7系统上默认已经安装了openssh-server,对应的安装命令:yum install openssh-server
    3. 配置:vi /etc/ssh/sshd_config
      Port 22
      ListenAddress 0.0.0.0
      ListenAddress ::
      PermiRootLogin yes
      PasswordAuthentication yes
    4. 开启sshd服务:service sshd start
    5. 查看IP地址:ip addr
    6. window系统上可以使用Xshell工具连接SSH:ssh root@192.168.103.230 --> 回车键 --> 输入root的密码
    7. 让sshd开机自启动:systemctl enable sshd.service
  4. Redis支持的5种数据结构:string、list、set、sortset、hash
  5. string:字符串数据,最常用的一种数据类型,普通的key-value存储都可以归为此类;
    1. set、get
    2. mget:批量获取多个key的值
    3. incr、incrby:incr对key的值做++,incrby加指定的值,incrby key offset
    4. desr、desrby:desr对key的值做--,desrby减指定的值
    5. setnx:set if not exist,只有key不存在时才会设置,如果key已存在,则返回0
    6. setex:设置key的值为string类型,并指定有效期,单位是秒,setex key value seconds
      getrange:获取value的子串,getrange key startIndex endIndex
      mset:批量设置多个key的值,返回0 表示没有任何值被设置;
      msetnx:同mset,不存在则设置,不会覆盖原有的key-value
      getset:设置key的值,并返回旧值
      append:给value追加字符串,并返回新字符串的长度
    7. redis string的存储格式(C语言)
      struct key {
      int len; //buf种存储的字符串长度
      int free; //buf种空闲空间的长度
      char buf[]; //buf用于存储的字符串内容
      }
      type 数据类型 int/string
  6. hash:一个string类型的field和value之间的映射表
    1. redis的Hash数据类型的key(hash表名)对应的value,内部存储结构是一个HashMap,Map<String, Map<String, String>> --> Map<Key, Map<field, value>
    2. Hash特别适合存储对象:相对于将对象的每个属性存储为string类型,将整个对象存储在Hash类型中会占用更少的内存;
    3. 存储的成员较少时,数据存储为zipmap,当成员数量增大时会自动转为真正的HashMap,此时的encoding为ht
      hset key field value
      hset student name Jack
      hset student age 30
      hset student sex 男
      hmset key field1 value1 field2 value2 field3 value3
      hget key field
      hset student name // Jack
      hmget key field1 field2 field3
      hgetall key
      hgetall student
      hdel key field // 删除某个field
      hlen key //field的数量
  7. list:有序、可重复,以栈的结构存储,先进后出
    lpush/rpush key value1 value2 value3:向头/尾部添加元素
    lrange key [startIndex] [endIndex]:获取一段value
    lrange key 0 -1 //获取所有的value
    lpop/rpop key:从头/尾部删除一个元素,并返回被删除的元素
    llen key:元素个数
    lindex key [index]:根据索引index获取对应的元素
    lrem key [count] value:根据指定的元素名称,移除 count 个此元素
  8. Set:不允许重复,无序,HashMap结构存储,Map<String, Map<String, null> --> Map<Key, Map<value, null>
    sadd key value1 value2 value3:添加成员
    smembers key:获取所有成员
    sdiff key1 key2:求两个集合的差集,key1中有,但key2中没有
    suion key1 key2:求并集
    sinter key1 key2:求交集
  9. sortSet:在set的基础上增加控制顺序的score,应用场景如排行榜
    zadd key score1 value1 score2 value2 score3 value3
    zadd myzset 1 AA 3 BB 2 CC
    zrange:根据索引范围获取value
    zrange myzset 0 -1 //获取所有的value
    zrange myzset 0 -1 withscores //获取所有的value及其score
    zrem key value1 value2 value3:删除元素
    zrangebyscore //根据score的范围获取value
    zrangebyscore myzset 1 6
    zrangebyscore myzset 1 6 withscores
    zrank key value:查看value的排名,从 0 开始,score最小的value,排名为 0
    zcard key:元素个数
    1. sortset也是HashMap结构存储的,另外还加了一层跳跃表
    2. 跳跃表相当于双向链表,在其基础上添加了前往比当前元素大的跳转连接,分为若干层,性能与树结构差不多
  10. 发布订阅:
    publisher -> channel -> subscriber1、subscriber2、subscriber3
    1. 消息发布者把消息发送到管道,由管道负责把消息准确发送给每个订阅者;
    2. 类似信息管道,用来进行系统之间的消息解耦,降低失败的风险;
      PUBLISH channel message // 向channel中发送message
      SUBSCRIBE channel // 订阅channel
      UNSUBSCRIBE channel1 channe2 // 取消订阅,如果不指定,则取消所有订阅
    3. 与MQ相比,redis的发布订阅功能比较薄弱、无后台功能,但比较轻量级;而MQ可以持久化消息,但消息的可靠性较差;

事务

传统关系型数据库的事务

  1. 目的:
    1. 为数据库操作序列(一组SQL命令)提供一个从失败中恢复到正常的方法,即使数据库处在异常状态下,仍能保持一致性;
    2. 当并发访问数据库时,可以做到隔离,彼此的操作互不干扰;
  2. 四大特性:ACID
    1. 原子性(Atomicity):事务作为一个整体被执行,要么全部被执行,要么都不执行;
    2. 一致性(Consistency):保证数据库从一个一致状态转变为另一个一致状态,一致状态指数据库中的数据应满足完整性约束;
    3. 隔离性(Isolation):多个事务并发执行时,相互不应该干扰;
    4. 持久性(Durability):已提交的事务对数据库的修改应该永久保存在数据库;
  3. 事务隔离机制:read uncommintted、read committed、repeatable read、serializable
    select @@tx_isolation; // 查看当前会话的事务隔离级别
    select @@global.tx_isolation; // 查看系统隔离级别
    1. 在旧版本的MySQL系统中,tx_isolation是transaction_isolation的别名,新版已经弃用了!
      select @@transaction_isolation;
      select @@global.transaction_isolation;
    2. 设置隔离级别
      set session transaction isolation level read committed; //设置当前会话的隔离级别
      set global transaction isolation level read committed; //设置系统的隔离级别
  4. innodb:MySQL的默认引擎
    1. innodb实现repeatable read的方式:MVCC,Multiversion Concurrency Control 多版本并发控制
innodb的MVCC原理.png
2. DATA_TRX_ID:一条记录的一个事务ID,每处理一个事务,ID值自动加1
3. DATA_ROLL_PTR:存储UNDO LOG的指针,undo log用于记录事务执行命令的记录;
4. DELETE_BIT:标识这条记录是否被删除,不是真正的删除,只有事务被commit之后才会真删除;

Redis事务

    multi  // 开启事务
    ······  // 命令入队
    EXEC  // 执行事务
    DISCARD  // 取消事务,清空命令队列
    WATCH  // 监视key
redis事务流程.png

Redis与ACID

  1. 原子性:单个Redis命令的执行是原子性的,但Redis事务上没有任何机制维持原子性,所以Redis事务并不是原子性;

  2. 一致性:比如A向B转账100,当B账户加了100时,A账户一定减了100,反之亦然,绝不会出现中间状态!

    1. 入队错误:在命令入队过程中,如果客户端向服务器发送了错误的命令,如参数出错,那么服务器将向客户端返回一个出错信息,并将客户端的事务状态设置为 REDIS_DIRTY_EXEC
    2. 执行错误:如果命令在事务执行过程中发生错误,如对一个不同类型的key执行了错误的操作,那么Redis只会将错误包含在事务的结果中,不会引起事务中断或整个失败,不会影响已执行命令的结果,也不会影响后面要执行的事务命令,所以它对一致性也没有影响;
  3. 隔离性:WATCH命令用于在事务开始之前监视任何数量的key,事务执行过程中,如果任意一个被监视的key被其他客户端更改了,那么整个事务不再执行,直接返回失败,所以对隔离性没有影响;

  4. 持久性:事务不过是用队列包裹的一组Redis命令,并没有提供任何额外的持久性功能,所以事务的持久性由Redis所使用的持久化模式决定;

  5. redis在分布式集群环境下的session共享

    1. SpringBoot的依赖:spring-session-data-redis
    2. @EnableRedisHttpSession:开启Redis缓存Session,属性maxInactiveIntervalInSeconds 指定缓存时间
      request.getSession().setAttribute(key, value):设置session
      request.getSession().getAttribute(key):获取session
      request.getSession().getId():获取session ID,首次建立会话并设置Session时,会自动生成Session ID
    3. Spring Session会保存两个Session ID,以防止其中一个过期
  6. MySQL数据库表的设计

    1. 更小的通常更好,控制字节长度
    2. 使用合适的数据类型,如tinyint只占8个位,char存储定长数据时比varchar更节省空间,因为varchar需要额外存储数据长度,如32位的UUID可以用char(32)
    3. 尽量避免NULL,而选择 NOT NULL、DEFAULT ''
      NULL的列会让索引统计和值比较更复杂,可为NULL的列会占用更多的磁盘空间,MySQL处理起来也更复杂
  7. MySQL索引设计

    1. 选择唯一性索引:索引的值是唯一的,可以更快速的查询,保证物理上的唯一
    2. 为经常需要排序、分组和联合操作的字段建立索引,排序会浪费很多时间
    3. 常作为查询条件的字段建立索引
    4. 数据少的地方不必建立索引
  8. SQL语句的优化:explain查看执行计划

    1. 能用 between 就不用 in
    2. 能用 distinct 就不用 group by
    3. 避免数据强转
  9. 缓存带来的回报

    1. 高速读写
      CPU L1/L2/L3 Catch、Linux page Cache加速硬盘读写、浏览器缓存、Ehcache缓存数据库结果
    2. 降低后端负载
      后端服务器通过前端缓存降低负载,通过Redis降低MySQL负载
  10. 缓存带来的代价

    1. 数据不一致性:缓存层和数据层有时间窗口不一致,和更新策略有关;
    2. 代码维护成本增加:原本只需要读写数据库就能实现的功能,在加入了缓存之后就要去维护缓存的数据;
    3. 堆内缓存可能带来内存溢出的风险,影响用户进程,如ehCache、loadingCache
      本机内存分类:堆、JVM栈、方法区、本地方法栈、程序计数器
    4. 堆内缓存与远程服务器(如Redis)缓存
      堆内缓存一般性能更好,远程缓存则需要套接字传输;
      用户级别的数据尽量采用远程缓存;
      大数据量尽量采用远程缓存,服务节点化原则;

缓存雪崩

  1. 缓存集中在一段时间内失败,发生大量缓存穿透,所有查询都落在数据库上,造成缓存雪崩;
  2. 由于原有缓存失效,在新缓存未到期间,所有原本应该访问缓存的请求都去查询数据库了,从而对数据库CPU和内存造成巨大压力,严重的会导致数据库宕机;

解决方案

  1. 加锁排队:multex互斥锁解决
    Redis的 SETNX 去 set 一个 mutex key,当操作返回成功时,再进行load db的操作,并回设缓存;否则就重试整个get缓存的方法;
  2. 数据预热
    系统上线后,将相关的缓存数据直接加载到缓存系统,从而避免在用户请求的时候,先查数据库、再将数据设置到缓存;
    可以通过缓存reload机制,预先去更新缓存,在即将发生大并发访问前,手动触发加载,缓存不同的key
  3. 双层缓存策略
    C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期
  4. 定时更新缓存策略
    时效性要求不高的缓存,容器启动初始化加载,采用定时任务更新缓存
  5. 设置不同的过期时间,让缓存失效时间点尽量均匀

缓存穿透

  1. 用户查询数据,如果在数据库中没有,缓存中自然也不会有,这就导致每次用户查询时,在缓存中查不到,就要去数据库中再查一遍,然后返回空!也就是进行两次无用的查询;
  2. 每次用户请求,都会绕过缓存,直接查询数据库,这就是缓存穿透;

解决方案

  1. 缓存空值
    如果一个查询返回的数据为空,不管数据是否存在,我们都把这个空结果进行缓存,但它的过期时间会很短,最长不超过5min
    第二次查询缓存时,就可以获取到一个值了,而不会继续查询数据库;
  2. 采用布隆过滤器 Bloom Filter
    将所有可能存在的数据 哈希 到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免对底层存储系统造成压力;
    布隆过滤器占用内存很小,bit存储,性能特别高
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容