zoukankan      html  css  js  c++  java
  • 非关系数据库之redis入门到实战(5)Redis企业运用

    第一章 NoSQL 入门概述

    1.1 入门概述

    1.1.1 单机 MySQL 的美好年代

      在90年代,一个网站的访问量一般都不大,用单个数据库完全可以轻松应付。
      在那个时候,更多的都是静态网页,动态交互类型的网站不多。


      上述架构下,我们来看看数据存储的瓶颈是什么?
      1、数据量的总大小:一个机器放不下时
      2、数据的索引(B+ Tree):一个机器的内存放不下时
      3、访问量(读写混合):一个实例不能承受
      如果满足了上述 1 or 3 个,进化……

    1.1.2 Memcached(缓存) + MySQL + 垂直拆分

      后来,随着访问量的上升,几乎大部分使用 MySQL 架构的网站在数据库上都开始出现了性能问题,web 程序不再仅仅专注在功能上,同时也在追求性能。程序员们开始大量的使用缓存技术来缓解数据库的压力,优化数据库的结构和索引。开始比较流行的是通过文件缓存来缓解数据库压力,但是当访问量继续增大的时候,多台 web 机器通过文件缓存不能共享,大量的小文件缓存也带了了比较高的 IO 压力。在这个时候,Memcached 就自然的成为一个非常时尚的技术产品。
      


      Memcached 作为一个独立的分布式的缓存服务器,为多个 web 服务器提供了一个共享的高性能缓存服务,在 Memcached 服务器上,又发展了根据 hash 算法来进行多台 Memcached 缓存服务的扩展,然后又出现了一致性 hash 来解决增加或减少缓存服务器导致重新 hash 带来的大量缓存失效的弊端。

    1.1.3 MySQL 主从复制--读写分离

      由于数据库的写入压力增加,Memcached 只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来达到读写分离,以提高读写性能和读库的可扩展性。Mysql 的 master-slave 模式成为这个时候的网站标配了。
      

    1.1.4 分表分库 + 水平拆分 + MySQL 集群

      在 Memcached 的高速缓存,MySQL 的主从复制,读写分离的基础之上,这时 MySQL 主库的写压力开始出现瓶颈,而数据量的持续猛增,由于 MyISAM 使用表锁,在高并发下会出现严重的锁问题,大量的高并发 MySQL 应用开始使用 InnoDB 引擎代替 MyISAM。
      同时,开始流行使用分表分库来缓解写压力和数据增长的扩展问题。这个时候,分表分库成了一个热门技术,是面试的热门问题也是业界讨论的热门技术问题。也就在这个时候,MySQL 推出了还不太稳定的表分区,这也给技术实力一般的公司带来了希望。虽然 MySQL 推出了 MySQL Cluster 集群,但性能也不能很好满足互联网的要求,只是在高可靠性上提供了非常大的保证。
      

    1.1.5 MySQL 的扩展性瓶颈

      MySQL 数据库也经常存储一些大文本字段,导致数据库表非常的大,在做数据库恢复的时候就导致非常的慢,不容易快速恢复数据库。比如 1000 万 4KB 大小的文本就接近 40GB 的大小,如果能把这些数据从 MySQL 省去,MySQL 将变得非常的小。关系数据库很强大,但是它并不能很好的应付所有的应用场景。MySQL 的扩展性差(需要复杂的技术来实现),大数据下 IO 压力大,表结构更改困难,正是当前使用 MySQL 的开发人员面临的问题。

    1.1.6 今天是什么样子?

    1.1.7 为什么用 NoSQL?

      为什么使用 NoSQL ?
      今天我们可以通过第三方平台(如:Google、Facebook等)可以很容易的访问和抓取数据。用户的个人信息、社交网络、地理位置、用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那 SQL 数据库已经不适合这些应用了,NoSQL 数据库的发展也却能很好的处理这些大的数据。
      

    1.2 NoSQL 是什么?

      NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是 SQL”,泛指非关系型的数据库。随着互联网 web2.0 网站的兴起,传统的关系数据库在应付 web2.0 网站,特别是超大规模和高并发的 SNS 类型的 web2.0 纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL 数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题,包括超大规模数据的存储。
      例如谷歌或 Facebook 每天为他们的用户收集万亿比特的数据。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展

    1.3 NoSQL 能干啥?

    KV -- 键值对
    Cache -- 缓存
    Persistence -- 持久化

    1.4 NoSQL 有哪些?

    Redis
    Memcache
    MongoDB

    1.5 NoSQL 怎么玩?

    Hello World
    类比
    递推
    各大网站、博客、论坛、微信公众号

    1.2 4V + 3高

    4V:
      海量(大量) Volume
      多样 Variety
      实时(高速) Velocity
      低价值密度 Value

    参考链接:https://www.cnblogs.com/chenmingjun/p/10335265.html#_label0_1

    3高:
      高并发
      高可扩
      高性能

    1.3 云计算--互联网云架构

    1.3.1 当前的互联网江湖

    云计算大数据工程师 = 云大。

    人算不如天算,天算就是云计算。

    架构即技术,技术即人生。

    云计算 和 大数据 是不分家的。就相当于语文和历史的关系。

    买书容易看书难,搬家还麻烦!

    天上飞的理论是相通的,落地的产品各不相同。


    我们身处“争雄图霸”的 IT 乱世

    滴滴 + 快的 合并
    美团 + 大众点评 合并
    携程 + 去哪网儿 合并
    海底捞

    支付宝会记录你的消费轨迹 => 蚂蚁金服 -> 花呗+借呗+芝麻信用 => 广告的分类推送 + 精确营销


    百度 -- 熊厂、狼厂、蓝厂
      百度的 Logo 是一个蓝色的熊爪子,百度CEO李彦宏给百度员工的一封公开信:《鼓励狼性淘汰小资》。
    腾讯 -- 鹅厂
      腾讯的 Logo 是一只企鹅,企鹅也是鹅。
    迅雷 -- 鸟厂
    360 -- 绿厂、数字公司
    阿里巴巴 -- 猫厂、东厂、西厂
      阿里巴巴是因为旗下天猫的 Logo。
    新浪 -- 渣浪
      起因是 up 主使用外链投稿曾多次被新浪审核但又无故删除,使得UP主们抓狂,从此就有了“战渣浪”的定义。
    网易 -- 猪厂
      网易 CEO 丁磊在之前养过一段时间的猪。
    搜狐 -- 狐厂
      搜狐的吉祥物是一只红色大尾巴的小狐狸。
    京东 -- 狗厂
    小米 -- 粮厂、杂粮、粗粮
    盛大 -- 无花名
      据说在盛大工作过1-2年叫盛斗士;3-5年叫必盛客;6-9年叫斗战盛佛;10年以上叫齐天大盛。

    1.3.2 传统行业 Java 工程师

    01 图


    02 图

    03 图

    问题:
      用户端:公司发展,用户过万,在线用户上千,应用服务器和数据库都成为瓶颈。
      开发组:团队变大,所有开发人员集中针对同一个系统,冲突严重。

    业务模式决定架构:
      • 面向互联网海量用户提供服务 
      • 面向商家提供网络销售平台 
      • 商品种类繁多,需及时更新数据 
      • 全天24小时运营,快速处理客户订单 
      • 交易量大且不断增长,促销活动期间业务量突增 
      • 支持多种营销模式,逻辑复杂 
      • 业务模式包括自营B2C、商家B2C、广告、代发物流等
      • 经营实物商品和数字商品,交付方式不同 
      • 支持多种安全的登录方式和支付方式

    1.3.1 大型互联网架构架构

    1、当当网


    当当网技术架构

    2、美团网

    3、宜人贷

    4、淘宝网

    1.3.2 云大具体应用

    1.3.3 需要掌握的技术


    把传统的 JavaEE + 云平台整合构建?

    1.3.4 软件架构设计

      • 子系统的设计以及接口的定义
      • 基于 Linux 下的服务器集群配置
      • 负载均衡与性能提升

    阿里巴巴大规模互联网实践的顶层架构图

    企业级互联网架构平台的事实标准

    企业信息系统演进的历程

    芒果TV合作案例

    1.4 当下的 NoSQL 经典应用

    阿里巴巴中文站架构发展历程

    阿里巴巴中文站第五代网站架构

    阿里巴巴中文站第五代网站架构的使命

    和我们相关的,多数据源多数据类型的存储问题

    为什么要去IOE?

      2008年,王坚加盟阿里巴巴成为集团首席架构师,即现在的首席技术官。这位前微软亚洲研究院常务副院长被马云定位为:将帮助阿里巴巴集团建立世界级的技术团队,并负责集团技术架构以及基础技术平台搭建。
      在加入阿里后,带着技术基因和学者风范的王坚就在阿里巴巴集团提出了被称为 “去IOE”(在 IT 建设过程中,去除 IBM 小型机、Oracle 数据库及 EMC 存储设备)的想法,并开始把云计算的本质,植入阿里 IT 基因。
      王坚这样概括 “去IOE” 运动和阿里云之间的关系:“去IOE” 彻底改变了阿里集团 IT 架构的基础,是阿里拥抱云计算,产出计算服务的基础。“去IOE” 的本质是分布化,让随处可以买到的 Commodity PC 架构成为可能,使云计算能够落地的首要条件。

    UDSL 是什么?

    1.5 在分布式数据库中 CAP 原理(CAP + BASE)

      最多只能同时较好的满足两个。
      CAP 理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求。
      因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三大类:
      • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
      • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
      • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

    CAP 的 3 进 2

      CAP 理论就是说在分布式存储系统中,最多只能实现上面的两点。
      而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡,没有 NoSQL 系统能同时保证这三点。
      C:强一致性 A:高可用性 P:分布式容忍性
      CA -- 传统 Oracle 数据库
      AP -- 大多数网站架构的选择
      CP -- Redis、Mongodb
      注意:分布式架构的时候必须做出取舍。
      一致性和可用性之间取一个平衡。多余大多数 web 应用,其实并不需要强一致性。
      因此牺牲 C 换取 P,这是目前分布式数据库产品的方向。
    一致性与可用性的决择
      对于 web2.0 网站来说,关系数据库的很多主要特性却往往无用武之地
    数据库事务一致性需求 
      很多web实时系统并不要求严格的数据库事务,对读一致性的要求很低,有些场合对写一致性要求并不高。允许实现最终一致性。
    数据库的写实时性和读实时性需求
      对关系数据库来说,插入一条数据之后立刻查询,是肯定可以读出来这条数据的,但是对于很多web应用来说,并不要求这么高的实时性,比方说发一条消息之 后,过几秒乃至十几秒之后,我的订阅者才看到这条动态是完全可以接受的。
    对复杂的SQL查询,特别是多表关联查询的需求 
      任何大数据量的 web 系统,都非常忌讳多个大表的关联查询,以及复杂的数据分析类型的报表查询,特别是 SNS 类型的网站,从需求以及产品设计角度,就避免了这种情况的产生。往往更多的只是单表的主键查询,以及单表的简单条件分页查询,SQL 的功能被极大的弱化了。

    BASE 是什么?

      BASE 就是为了解决关系数据库强一致性引起的问题而引起的可用性降低而提出的解决方案。
      BASE 其实是下面三个术语的缩写:
      基本可用(Basically Available)
      软状态(Soft state)
      最终一致(Eventually consistent)
      它的思想是通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上改观。为什么这么说呢?缘由就在于大型系统往往由于地域分布和极高性能的要求,不可能采用分布式事务来完成这些指标,要想获得这些指标,我们必须采用另外一种方式来完成,这里 BASE 就是解决这个问题的办法。

    分布式系统

      分布式系统(distributed system)由多台计算机和通信的软件组件通过计算机网络连接(本地网络或广域网)组成。分布式系统是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。分布式系统可以应用在在不同的平台上如:Pc、工作站、局域网和广域网上等。
      简单来讲:
      1、分布式:不同的多台服务器上面部署不同的服务模块(工程),他们之间通过 RPC/RMI 之间通信和调用,对外提供服务和组内协作。
      2、集群:不同的多台服务器上面部署相同的服务模块,通过分布式调度软件进行统一的调度,对外提供服务和访问。

    第二章 Redis 入门介绍

    2.1 入门概述

    2.2 Redis 的安装


    查看端口命令复习

    2.3 Redis 启动后杂项基础知识讲解

    第三章 Redis 数据类型

    第四章 解析 Redis 配置文件 redis.conf

    4.1 Units 单位


    1、配置大小单位,开头定义了一些基本的度量单位,只支持 bytes(字节),不支持 bit(位数)。
    2、对大小写不敏感。 

    4.2 INCLUDES 包含


    指定包含其它的配置文件,可以在同一主机上多个 Redis 实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件。
    include /path/to/local.conf 

    4.3 GENERAL 通用

    daemonize
    pidfile
    port


    bind
    timeout

    loglevel
    logfile
    databases

    4.4 SNAPSHOTTING 快照

    sava

    RDB是整个内存的压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件。
    默认
    save 900 1
    save 300 10
    save 60 10000

    15分钟内改了1次,就保存一次快照
    或5分钟内改了10次,就保存一次快照
    或1分钟内改了1万次,就保存一次快照

    注意:实际开发中 30 分钟保存一次快照。

    如果想禁用RDB持久化的策略,只要不设置任何save指令,或者给save传入一个空字符串参数也可以。
    sava ""

    stop-writes-on-bgsave-error 默认配置是yes,表示在使用bgsave命令备份的时候出现错误则停止外界写入操作,
    如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制。
    dbfilename
    dir dump.rdb存储的目录,即启动redis时的目录

    4.5 SECURITY 安全


    具体操作如下:

    4.6 LIMITS 限制

    maxclients

      设置 redis 同时可以与多少个客户端进行连接。默认情况下为 10000 个客户端。当你无法设置进程文件句柄限制时,redis 会设置为当前的文件句柄限制值减去 32,因为 redis 会为自身内部处理逻辑留一些句柄出来。如果达到了此限制,redis 则会拒绝新的连接请求,并且向这些连接请求方发出 “max number of clients reached” 以作回应。
    maxmemory
      设置 redis 可以使用的内存量。一旦到达内存使用上限,redis 将会试图移除内部数据,移除规则可以通过 maxmemory-policy 来指定。如果 redis 无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么 redis 则会针对那些需要申请内存的指令返回错误信息,比如 SET、LPUSH 等。
      但是对于无内存申请的指令,仍然会正常响应,比如 GET 等。如果你的 redis 是主 redis(说明你的 redis 有从 redis),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素。
    maxmemory-policy
      •(1)volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键
      •(2)allkeys-lru:使用 LRU 算法移除 key
      •(3)volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键
      •(4)allkeys-random:移除随机的 key
      •(5)volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key
      •(6)noeviction:不进行移除。针对写操作,只是返回错误信息,实际开发中不使用这个配置。

    4.7 APPEND ONLY MODE 追加

    appendonly
    appendfilename
    appendfsync
      always:同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好。
      everysec:出厂默认推荐,异步操作,每秒记录,如果一秒内宕机,有数据丢失,实际开发中推荐
      no

    4.8 常见配置 redis.conf 介绍

    参数说明:

    redis.conf 配置项说明如下:
    1. Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程
      daemonize no
    2. 当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定
      pidfile /var/run/redis.pid
    3. 指定 Redis 监听端口,默认端口为 6379,作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口,因为 6379 在手机按键上是 MERZ 对应的号码,而 MERZ 取自意大利歌女 Alessia Merz 的名字
      port 6379
    4. 绑定的主机地址
      bind 127.0.0.1
    5.当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
      timeout 300
    6. 指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 verbose
      loglevel verbose
    7. 日志记录方式,默认为标准输出,如果配置 Redis 为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null
      logfile stdout
    8. 设置数据库的数量,默认数据库为 0,可以使用 SELECT <dbid> 命令在连接上指定数据库 id
      databases 16
    9. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
      save <seconds> <changes>
      Redis 默认配置文件中提供了三个条件:
      save 900 1
      save 300 10
      save 60 10000
      分别表示 900 秒(15分钟)内有 1 个更改,300 秒(5分钟)内有 10 个更改以及 60 秒内有 10000 个更改。
    10. 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大
      rdbcompression yes
    11. 指定本地数据库文件名,默认值为 dump.rdb
      dbfilename dump.rdb
    12. 指定本地数据库存放目录
      dir ./
    13. 设置当本机为 slave 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步
      slaveof <masterip> <masterport>
    14. 当 master 服务设置了密码保护时,slave 服务连接 master 的密码
      masterauth <master-password>
    15. 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭
      requirepass foobared
    16. 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息
      maxclients 128
    17. 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 key 存放内存,value 会存放在 swap 区
      maxmemory <bytes>
    18. 指定是否在每次更新操作后进行日志记录,Redis 在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis 本身同步数据文件是按上面 save 条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为 no
      appendonly no
    19. 指定更新日志文件名,默认为 appendonly.aof
       appendfilename appendonly.aof
    20. 指定更新日志条件,共有3个可选值: 
      no:表示等操作系统进行数据缓存同步到磁盘(快) 
      always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) 
      everysec:表示每秒同步一次(折衷,默认值)
      appendfsync everysec
    21. 指定是否启用虚拟内存机制,默认值为 no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章会仔细分析 Redis 的 VM 机制)
      vm-enabled no
    22. 虚拟内存文件路径,默认值为 /tmp/redis.swap,不可多个 Redis 实例共享
      vm-swap-file /tmp/redis.swap
    23. 将所有大于 vm-max-memory 的数据存入虚拟内存,无论 vm-max-memory 设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是 keys),也就是说,当 vm-max-memory 设置为0的时候,其实是所有 value 都存在于磁盘。默认值为 0
      vm-max-memory 0
    24. Redis swap 文件分成了很多的 page,一个对象可以保存在多个 page 上面,但一个 page 上不能被多个对象共享,vm-page-size 是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page 大小最好设置为 32 或者 64bytes;如果存储很大大对象,则可以使用更大的 page,如果不确定,就使用默认值
      vm-page-size 32
    25. 设置 swap 文件中的 page 数量,由于页表(一种表示页面空闲或使用的 bitmap)是在放在内存中的,,在磁盘上每 8 个 pages 将消耗 1byte 的内存。
      vm-pages 134217728
    26. 设置访问 swap 文件的线程数,最好不要超过机器的核数,如果设置为 0,那么所有对 swap 文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为 4
      vm-max-threads 4
    27. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
      glueoutputbuf yes
    28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
      hash-max-zipmap-entries 64
      hash-max-zipmap-value 512
    29. 指定是否激活重置哈希,默认为开启(后面在介绍 Redis 的哈希算法时具体介绍)
      activerehashing yes
    30. 指定包含其它的配置文件,可以在同一主机上多个 Redis 实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
      include /path/to/local.conf
     

    第五章 Redis 的持久化


    RBD小结

    AOF小结

    第六章 Redis 的事务

    Redis 事务常用命令:


    Redis 的事务脑图
     

    第七章 Redis 的复制(Master/Slave)

    第八章 Redis 的 Java 客户端 Jedis


    测试连通性
    package com.atguigu.test;

    import redis.clients.jedis.Jedis;

    public class HelloRedis {

        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.25.102", 6379);

            String result = jedis.ping();

            System.out.println("**********" + result);

            // Connection
        }
    }

    五大数据类型 + 1个 key

    package com.atguigu.test;

    import java.util.Set;

    import redis.clients.jedis.Jedis;

    public class APIRedis {

        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.22.167", 6379);

            Set<String> set = jedis.keys("*");
            System.out.println(set.size());

            jedis.set("k3", "v3");

            System.out.println(jedis.get("k3"));
        }
    }

    事务提交

    package com.atguigu.test;

    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.Transaction;

    public class TXRedis {

        public boolean transMethod() throws InterruptedException {
            Jedis jedis = new Jedis("192.168.25.102", 6379);

            int balance;// 可用余额
            int debt;// 欠额
            int amtToSubtract = 10;// 实刷额度

            jedis.watch("balance");
            Thread.sleep(8000); // 停止程序一小段时间,外面先修改balance数据后看效果
            balance = Integer.parseInt(jedis.get("balance"));

            if (balance < amtToSubtract) {
                jedis.unwatch();
                System.out.println("The Data has been updated before you commit or balance < amtToSubtract");
                return false;
            } else {
                System.out.println("***********transaction");

                Transaction transaction = jedis.multi();
                transaction.decrBy("balance", amtToSubtract);
                transaction.incrBy("debt", amtToSubtract);
                transaction.exec();

                balance = Integer.parseInt(jedis.get("balance"));
                debt = Integer.parseInt(jedis.get("debt"));

                System.out.println("*******" + balance);
                System.out.println("*******" + debt);
                return true;
            }
        }

        /**
         * 伪代码如下: 通俗点讲,watch 命令就是标记一个键,如果标记了一个键:
         * 
         * 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中重新再尝试一次。
         * 首先标记了键balance,然后检查余额是否足够,不足就取消标记,并不做扣减; 足够的话,就启动事务进行更新操作,
         * 如果在此期间键balance被其它人修改, 那在提交事务(执行exec)时就会报错, 程序中通常可以捕获这类错误再重新执行一次,直到成功。
         * 
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
            TXRedis test = new TXRedis();
            boolean retValue = test.transMethod();
            System.out.println("main retValue-------: " + retValue);
        }
    }

    主从复制

    package com.atguigu.test;

    import redis.clients.jedis.Jedis;

    public class ReplicationRedis {

        public static void main(String[] args) {
            Jedis jedisM = new Jedis("192.168.25.102", 6379);
            Jedis jedisS = new Jedis("192.168.25.102", 6380);

            jedisS.slaveof("192.168.25.102", 6379);

            jedisM.set("k3", "v3");

            String result = jedisS.get("k3");

            System.out.println("############# result: " + result);
        }
    }

    JedisPoolUtils.java

    package com.atguigu.test;

    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;

    public class JedisPoolUtils {
        private static volatile JedisPool jedisPool = null;

        private JedisPoolUtils() {
        }

        // DCL(double check lock)
        public static JedisPool getInstance() {
            if (null == jedisPool) {
                synchronized (JedisPoolUtils.class) {
                    if (null == jedisPool) {
                        JedisPoolConfig poolConfig = new JedisPoolConfig();
                        poolConfig.setMaxActive(100);
                        poolConfig.setMaxIdle(32);
                        poolConfig.setMaxWait(100 * 1000);
                        poolConfig.setTestOnBorrow(true);

                        jedisPool = new JedisPool(poolConfig, "192.168.25.102"6379);
                    }
                }
            }
            return jedisPool;
        }

        public static void close(JedisPool jedisPool, Jedis jedis) {
            if (null != jedis) {
                jedisPool.returnResourceObject(jedis);
            }
        }
    }

    JedisPoolRedis.java

    package com.atguigu.test;

    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;

    public class JedisPoolRedis {

        public static void main(String[] args) {
            JedisPool jedisPool = JedisPoolUtils.getInstance();
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                jedis.set("k5", "0508good");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JedisPoolUtils.close(jedisPool, jedis);
            }
        }
    }

    配置总结

    JedisPool 的配置参数大部分是由 JedisPoolConfig 的对应项来赋值的。

    maxActive:控制一个 pool 可分配多少个 jedis 实例,通过 pool.getResource() 来获取;如果赋值为 -1,则表示不限制;如果  pool 已经分配了 maxActive 个 jedis 实例,则此时 pool 的状态为 exhausted。

    maxIdle:控制一个 pool 最多有多少个状态为 idle(空闲) 的 jedis 实例。

    whenExhaustedAction:表示当 pool 中的 jedis 实例都被 allocated 完时,pool 要采取的操作,默认有三种:
       WHEN_EXHAUSTED_FAIL  --> 表示无 jedis 实例时,直接抛出 NoSuchElementException
       WHEN_EXHAUSTED_BLOCK --> 则表示阻塞住,或者达到 maxWait 时抛出 JedisConnectionException
       WHEN_EXHAUSTED_GROW --> 则表示新建一个 jedis 实例,也就说设置的 maxActive 无用

    maxWait:表示当 borrow 一个 jedis 实例时,最大的等待时间,如果超过等待时间,则直接抛 JedisConnectionException;

    testOnBorrow:在 borrow 一个 jedis 实例时,是否提前进行 validate 操作;如果为 true,则得到的 jedis 实例均是可用的;

    testOnReturn:在 return 给 pool 时,是否提前进行 validate 操作;

    testWhileIdle:如果为 true,表示有一个 idle object evitor 线程对 idle object 进行扫描,如果 validate 失败,此 object 会被从 pool 中 drop 掉;这一项只有在 timeBetweenEvictionRunsMillis 大于0时才有意义;

    timeBetweenEvictionRunsMillis:表示 idle object evitor 两次扫描之间要 sleep 的毫秒数;

    numTestsPerEvictionRun:表示 idle object evitor 每次扫描的最多的对象数;

    minEvictableIdleTimeMillis:表示一个对象至少停留在 idle 状态的最短时间,然后才能被 idle object evitor 扫描并驱逐;这一项只有在 timeBetweenEvictionRunsMillis 大于0时才有意义;

    softMinEvictableIdleTimeMillis:在 minEvictableIdleTimeMillis 基础上,加入了至少 minIdle 个对象已经在 pool 里面了。如果为 -1,evicted 不会根据 idle time 驱逐任何对象。如果 minEvictableIdleTimeMillis > 0,则此项设置无意义,且只有在 timeBetweenEvictionRunsMillis > 0 时才有意义;

    lifo:borrowObject 返回对象时,是采用 DEFAULT_LIFO(last in first out,即类似 cache 的最频繁使用队列),如果为 False,则表示 FIFO 队列;

    =================================================
    其中 JedisPoolConfig 对一些参数的默认设置如下:

    testWhileIdle=true
    minEvictableIdleTimeMills=60000
    timeBetweenEvictionRunsMillis=30000
    numTestsPerEvictionRun=-1
     
  • 相关阅读:
    todo---HttpClient,httpUrlConnection
    todo---callback
    todo-braintree-java
    todo--com.paypal.sdk
    todo--OkHttp基本使用
    @RequestBody 和@ResponseBody 注解详解
    常用Jar包下载
    SpringMVC 使用JSR-303进行校验 @Valid
    SpringMVC 之 表单标签
    SpringMVC 自定义类型转换器
  • 原文地址:https://www.cnblogs.com/huanghanyu/p/13808816.html
Copyright © 2011-2022 走看看