函数函数的参数和返回值是可以定义的类型的//命名函数functionfun(x:number,y:number):number{returnx+y;}//匿名函数letlo=function(msg:string):void{//viod表示没有返回值console.log('匿名函数')}可选参数&默认参数可选参数?关键字functionadd(x:number,y:number,flag?:boolean):number{if(flag){returnx+y;}else{returnx-y;}}console.log(add(5,3,true));//8console.log(add(5,3));//2默认参数functionadd(x:number,y:number=3,flag?:boolean):number{if(flag){returnx+y;}else{returnx-y;}}console.log(add(5,3));//2console.log(add(5));//2剩余参数在Python、Java里叫不定长参数functionadd(x:number,...args:number[]):number{console.log(x,args)//5[4,3,2,1]args.forEach(num=>{x+=num;});returnx;}console.log(add(5,4,3,2,1));//15函数重载Java的重载,是函数名一样,参数列表不一样,而TypeScript有点不一样函数参数定义有类型断言,声明重载,规范传入参数的类型//重载函数声明。相对于规范传入参数的类型functionadd(x:string,y:string):string;functionadd(x:number,y:number):number;functionadd(x:number|string,y:number|string):number|string{if(typeofx==="number"&&typeofy==="number"){returnx+y;//数字类型相加}if(typeofx==="string"&&typeofy==="string"){returnx+'+'+y;//字符串拼接}}console.log(add('**','**'))//**+**console.log(add(3,5))//8
说明对于传统的JavaScript程序我们会使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员使用这些语法就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。从ECMAScript2015,也就是ES6开始,JavaScript程序员将能够使用基于类的面向对象的方式。使用TypeScript,我们允许开发者现在就使用这些特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。类的结构类的主要元素:属性、构造器、方法classCat{//属性name:stringage:number//构造函数constructor(name:string,age:number){this.name=name;this.age=age;}//普通方法eat():void{console.log(`${this.name}吃玉米`)}}letcat:Cat=newCat('O_O',12);cat.eat()继承&多态关键字extendsclassAnimal{//属性name:stringage:numberconstructor(name:string,age:number){this.name=name;this.age=age;}run(distance:number):void{console.log(`Animalrun${distance}m`)}}classCatextendsAnimal{//继承type:string//构造函数constructor(name:string,age:number){super(name,age)//调用父类构造函数}//普通方法eat():void{console.log(`${this.name}吃玉米`)}//重写父类方法run(distance:number):void{console.log(`${this.name}run${distance}m`)}}letcat:Cat=newCat('O_O',12);cat.run(15)权限修饰符默认公共public,实例对象,类内部,子类都可以访问私有private,只有类内部可以访问保护protected,类内部、子类之类可以访问classAnimal{//属性protectedname:string//当前类内部子类publicage:number//公共constructor(name:string,age:number){this.name=name;this.age=age;}run(distance:number):void{console.log(`Animalrun${distance}m`)}}classCatextendsAnimal{//继承privatetype:string//当前类内部//构造函数constructor(name:string,age:number){super(name,age)//调用父类构造函数}//普通方法eat():void{console.log(`${this.name}吃玉米`)}//重写父类方法run(distance:number):void{console.log(`${this.name}run${distance}m`)}}letcat:Cat=newCat('O_O',12);cat.run(15)console.log(cat.age)//12readonly修饰符readonly修饰属性为可读,只能在定义时或者构造函数初始化classPerson{readonlyname:string='abc'constructor(name:string){this.name=name}}letjohn=newPerson('John')参数属性TypeScript不一样的地方,作用简化代码不止能写readonly,其他权限public,private,protected//与上一个代码效果一样classPerson{constructor(readonlyname:string){}}letjohn=newPerson('John')静态属性不用实例化就能获取的属性classDog{staticname_:string='O_O'}console.log(Dog.name_)//'O_O'抽象类抽象类和抽象方法用abstract关键字抽象类作为其它类继承使用。抽象类不能被实例化。不同于接口,抽象类的普通方法可以有方法体,但抽象方法不能有方法体抽象方法必须在抽象类里abstractclassAnimal{abstracteat():void;//抽象方法没有实现细节run(x:number){//继承后普通方法可以不用实现console.log(`xxrun${x}m`)}}classDogextendsAnimal{eat(){//必须实现全部抽象方法的方法体console.log(`dogeatxx`)}}letdog:Dog=newDog()dog.run(15)//xxrun15m
接口说明接口是对象的状态(属性)和行为(方法)的抽象(描述),简单理解就是规范结构,接口不能初始化属性TypeScript的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口结构结构关键字interfaceinterfaceIPersion{id:numbername:stringage:string}constperson:IPersion={id:1,name:'o_o',age:14};可选属性interfaceIPersion{id:numbername:stringage?:string//该属性可以不存在}constperson:IPersion={id:1,name:'o_o'};只读属性关键字readonly该值只能在初始化时被改变,除此之外不能被改变interfaceIPersion{id:numberreadonlyname:string//只读age?:number}letperson:IPersion={id:1,name:'o_o'};person.id=2;//person.name='*_*';//报错不呢被改变readonlyvsconstreadonly修饰属性const修饰变量接口规范函数类型看着可能有点奇怪,这里明确一个概念,函数的必要元素:参数列表、返回值。(匿名没有函数名,所以函数名非必要)//定义函数类型interfaceIFunType{//参数列表:返回值(s1:string,s2:string):string}//变量类型为函数letfun:IFunType=function(s1:string,s2:string):string{return`${s1}_${s2}`}console.log(fun('kk','xx'));类接口作用于Java、C#里面一样,类需要实现接口interfaceIAnimal{type:stringeat():void}classCatimplementsIAnimal{type:string='猫'eat(){console.log(`${this.type}吃${this.type}粮`)}}letcat:Cat=newCat();cat.eat()接口继承接口接口间叫继承interfaceIAnimal{//动物接口type:stringeat():void}interfaceIFelidaeextendsIAnimal{//猫科接口继承动物接口leg_num:number}类实现多个接口interfaceIAnimal{//动物接口type:stringeat():void}interfaceIPerson{name:stringage:number}classPersonimplementsIAnimal,IPerson{type:string='person'name:stringage:numbereat(){console.log(`${this.type}吃玉米`)}}letperson:Person=newPerson();person.eat()//person吃玉米
说明TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型布尔值letmsg:boolean=true;数字TypeScript里的所有数字都是浮点数。这些浮点数的类型是number。letn1:number=12;//十进制letn2:number=0b1010;//二进制letn3:number=0o12;//八进制letn4:number=0xa;//十六进制字符串单引号双引号都可以lets:string='xxx';undefined和null它们的本身的类型用处不是很大letund:undefined=undefined;letnu:null=null;数组TypeScript像JavaScript一样可以操作数组元素。有两种方式可以定义数组。第一种,let变量名:类型[]=[];letarr:number[]=[1,2,3,4];第二种泛型定义,Array<类型>letarr:Array<number>=[1,2,3,4];元组元组类型允许表示一个已知元素数量和类型的数组lettu:[number,string,boolean]=[1,'咪咪',true];枚举enum类型是对JavaScript标准数据类型的一个补充enumColor{Red,//0Green=10,Blue//11}letcolor:Color=Color.Blue;console.log(Color)//0any当不知定义什么类型比较合适时使用leta:any;a=12;a='12';letb:any=12;b='a';console.log(a,b)//'12''a'object所有对象的基类leto:object={'a':12};letoo:object=newString('oo');联合类型表示取值为多种类型中的一种functionfun(s:number|string):number|string{returns}类型断言当使用联合类型时,编译器可能不知道,现在我们处理的时候什么类型,而出现报错,因此就需要告诉编译现在处理的是什么。/*两种定义方式:第一种:(<类型>变量)第二种:(变量as类型)*/functionfun(s:number|string):number{//返回参数的长度if((<string>s).length){return(<string>s).length;}else{return(sasnumber).toString().length;}}类型推断当不定义类型是,TypeScript会自动推断类型,如果只是定义,不初始化变量,则类型为anyletn=123;//推断类型为number//n='a';//报错letm;//推断类型为anym=12;m='a';
生成package.json文件终端运行npminit-ypackage.json文件{"name":"code","version":"1.0.0","description":"","main":"index.js","scripts":{"test":"echo\"Error:notestspecified\"&&exit1"},"keywords":[],"author":"","license":"ISC",}生成tsconfig.json文件终端运行,生成ts配置文件tsc--init创建html文件新建目录public创建html文件index.html<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document</title></head><body></body></html>创建ts文件新建目录src创建ts文件main.ts(()=>{alert('打包成功啦~~~~')})()创建webpack.config.js文件新建目录./build创建Js文件webpack.config.jsconst{CleanWebpackPlugin}=require('clean-webpack-plugin')constHtmlWebpackPlugin=require('html-webpack-plugin')constpath=require('path')constisProd=process.env.NODE_ENV==='production'//是否生产环境functionresolve(dir){returnpath.resolve(__dirname,'..',dir)}module.exports={mode:isProd?'production':'development',//生产环境还是开发环境entry:{app:'./src/main.ts'//程序主目录},output:{//打包输出配置项path:resolve('dist'),//打包结果在dist目录下filename:'[name].[contenthash:8].js'//打包后的js文件名格式},module:{rules:[{//对src目录下的ts文件编译操作test:/\.tsx?$/,use:'ts-loader',include:[resolve('src')]}]},plugins:[newCleanWebpackPlugin({//把以前打包的js清除}),newHtmlWebpackPlugin({template:'./public/index.html'//当前目录的html打包})],resolve:{extensions:['.ts','.tsx','.js']//引入这类文件可以不写扩展名},devtool:isProd?'cheap-module-source-map':'eval-cheap-module-source-map',//提示错误信息devServer:{host:'localhost',//主机名port:8081,open:true//自动打开浏览器},}下载依赖npminstall-Dtypescriptnpminstall-Dwebpackwebpack-cliwebpack-dev-server//打包html和清除之前打包的jsnpminstall-Dhtml-webpack-pluginclean-webpack-plugin//对ts文件进行编译处理npminstall-Dts-loader#涉及跨平台的命令npminstall-Dcross-env配置打包命名//开发环境打包"dev":"cross-envNODE_ENV=developmentwebpack-dev-server--configbuild/webpack.config.js",//生产环境打包"build":"cross-envNODE_ENV=productionwebpack--configbuild/webpack.config.js"运行打包//开发环境打包测试npmrundev//生产环境打包npmrunbuild开发环境打包生产环境打包
什么是TypeScriptTypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScriptTypeScript的作者是安德斯·海尔斯伯格,C#的首席架构师。它是开源和跨平台的编程语言。TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。TypeScript是JavaScript的一个超集,主要提供了类型系统和对ES6+的支持,它由Microsoft开发,代码开源于GitHub上TypeScript三大特点始于JavaScript,归于JavaScriptTypeScript可以编译出纯净、简洁的JavaScript代码,并且可以运行在任何浏览器上、Node.js环境中和任何支持ECMAScript3(或更高版本)的JavaScript引擎中。强大的类型系统类型系统允许JavaScript开发者在开发JavaScript应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构。先进的JavaScriptTypeScript提供最新的和不断发展的JavaScript特性,包括那些来自2015年的ECMAScript和未来的提案中的特性,比如异步功能和Decorators,以帮助建立健壮的组件TypeScript快速上手安装TypeScript首选安装好node环境下载地址:https://nodejs.org/zh-cn/命令行运行如下命令,全局安装TypeScriptnpminstall-gtypescript安装完成后,在控制台运行如下命令,检查安装是否成功tsc-VTypeScript程序src/main.ts(()=>{functionhello(name:string){return"你好啊!"+name;}console.log(hello('NoNo'))})()手动编译手动编译,因为是.ts文件,文件不能被浏览器运行,需要编译成js文件在终端中打开,输入tscmain.ts,自动生成编译好的mian.js文件vscode自动编译命令行生成配置文件tsconfig.jsontsc--init修改tsconfig.json配置"outDir":"./src",//指定编译输出目录"strict":false,//关闭严格模式依次点击启动监视任务,当文件保存了改变,就会自动编译终端->运行任务->所有任务->监视tsconfig.json类型注解TypeScript的带来的高级功能//基本类型的注解functionhello(name:string){return"你好啊!"+name;}接口实现复杂注解使用接口,实现复杂注解interfacePerson{firstName:string,lastName:string}functionhello(name:Person){return"你好啊!"+name.firstName+'·'+name.lastName;}letuser={firstName:'No',lastName:'No'}console.log(hello(user))类TypeScript支持JavaScript的新特性,比如支持基于类的面向对象编程。classPerson{firstName:string//属性lastName:string//构造函数constructor(firstName:string,lastName:string){this.firstName=firstNamethis.lastName=lastName}}functionhello(name:Person){return"你好啊!"+name.firstName+'·'+name.lastName;}letuser=newPerson('No','No')console.log(hello(user))
—-思路来自志远大佬什么是TLS说道ja3指纹,必然是要知道TLS的安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。—-来自百度百科什么是ja3官方链接:https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967大概意思就是:JA3和JA3S作为一种方法来识别客户端和服务器之间的TLS协议,可以帮助对特定客户端与其服务器之间的某些指纹信息,来识别恶意通信。ja3测试使用网站:https://ja3er.com/jsonrequests、httpx、aiohttp的结果,这里就不做其他IP、UA等测试了,结果都是以知的一样查看TLS/SSL指纹信息过滤条件tls.handshake.version==0x00303使用的软件:鲨鱼魔改OpenSSL并编译把编译好的libcrypto-1_1-x64.dll、libssl-1_1-x64.dll使用curl和python测试curl测试Python测试
说明本文仅供学习交流,严禁用于非法用途,文章如有不当可联系博主删除网址:aHR0cHM6Ly9tLmN0eXVuLmNuL3dhcC9tYWluL2F1dGgvbG9naW4/cmVkaXJlY3Q9JTJGbXk=加密参数:password,comParam_seqCode,comParam_signature开始断点追栈找password加密处这里有个小技巧,先尝试看函数名,成的话就可以不用一层一层追了第一个login打断点,就发现了加密处//分析发现//加密函数是Object(l["c"])(a.value,Object(l["f"])(Object(l["g"])(s.value)))//其中Object(l["c"])为加密函数,它有两个参数第一个参数是密码a.value第二个参数是对邮箱进行了处理Object(l["f"])(Object(l["g"])(s.value))主要对邮箱的处理函数是Object(l["f"]),作用是保证第二个参数是'24位'不够'补0'多了'截取'分析加密逻辑复制整个js文件,发现是webpak打包的js,那就简单了//整个加密的核心是这行p.a.TripleDES.encrypt(e,d,l);//找到p变量是怎么来的,d=t("80e3"),l=t.n(d),s=t("3452")//s来自t("3452"),p=t.n(s)//p变量来自s,f=t("c466"),h=null,m=function(e){returnfunction(n){returnObject.prototype.toString.call(n)==="[object".concat(e,"]")}扒代码发现这是webpack打包过的,我们构造一个简单的加载器varfff;!function(e){varn={}functionf(t){if(n[t])returnn[t].exports;console.log('-->',t);varr=n[t]={i:t,l:!1,exports:{}};returne[t].call(r.exports,r,r.exports,f),r.l=!0,r.exports}fff=f;}({//函数})填入3452对象运行代码执行fff('3452'),发现报错,这是缺少代码在网站Js代码的加载器打断点,找到缺少的函数,然后复制粘贴代码补充完整,得到加密对象改写加密逻辑然后测试运行继续找comParam_seqCode、comParam_signature看着主要的逻辑//h.getTimestampOffset()的逻辑f是h.getTimestampOffset=function(){returnlocalStorage.getItem("timestampOffset")||f}//其他关键的点`u['k']`、`o()`向上翻,找到u['k']、o()定义点扒取代码,跟上面一样,这里的u['k']可以简单一点直接复制函数就行测试结果一样
说明Redis为订阅&发布模型如下,共有6个命令。相关命令subscribe订阅subscribechannel[channels]127.0.0.1:6379>subscribenews.itnews.sportReadingmessages...(pressCtrl-Ctoquit)1)"subscribe"2)"news.it"3)(integer)11)"subscribe"2)"news.sport"3)(integer)2#订阅后客户端挂起等待接受消息psubscribe模式订阅psubscribe与subscribe的差别只有一个地方,其他都一样subscribe:只能订阅指定名称的频道psubscribe:可以使用通配符指定频道#客户端1使用模式订阅127.0.0.1:6379>psubscribenews.*Readingmessages...(pressCtrl-Ctoquit)1)"psubscribe"2)"news.*"3)(integer)1#接收到的消息1)"pmessage"2)"news.*"3)"news.a"4)"testa"1)"pmessage"2)"news.*"3)"news.b"4)"testb"#客户端2发送消息127.0.0.1:6379>publishnews.atesta(integer)1127.0.0.1:6379>publishnews.btestb(integer)1publish发送消息publishchannelmessage发送消息到指定的频道127.0.0.1:6379>publishnews.ittest(integer)1#发送成功127.0.0.1:6379>publishxxtest(integer)0#发送失败pubsub查看订阅状态pubsub<子命令>pubsubchannels:默认查看所有使用subscribe命令订阅的频道,可以使用匹配127.0.0.1:6379>pubsubchannels1)"news.sport"2)"news.it"127.0.0.1:6379>pubsubchannelsnews.*1)"news.sport"2)"news.it"pubsubnumsubchannel[channel]:查看指定频道订阅的数量127.0.0.1:6379>pubsubnumsubnews.itnews.sport1)"news.it"2)(integer)13)"news.sport"4)(integer)1pubsubnumpat:返回使用psubscribe命令客户端的总数量#客户端1订阅127.0.0.1:6379>psubscribenews.goodReadingmessages...(pressCtrl-Ctoquit)1)"psubscribe"2)"news.good"3)(integer)1#客户端2查看psubscribe订阅的数量127.0.0.1:6379>pubsubnumpat(integer)1127.0.0.1:6379>unsubscribe退订指定的频道unsubscribechannel[channels]127.0.0.1:6379>unsubscribenews.it#退订news.it频道1)"unsubscribe"#退订2)"news.it"#退订的频道名称3)(integer)0#订阅的数量punsubscribe退订使用模式订阅频道punsubscribechannel[channels]的差别只有一个地方,其他都一样unsubscribe:只能退订指定名称的频道punsubscribe:可以使用通配符退订频道127.0.0.1:6379>punsubscribenews.*1)"punsubscribe"#退订2)"news.*"#退订的频道名称3)(integer)0#订阅的数量
tip:以下配置文件为windows上的,Linux版本可以参考配置文件的单位大小写不敏感INCLUDES包含可以把多个conf文件,组合成一个##################################INCLUDES###################################...#include.\path\to\local.conf#includec:\path\to\other.confNETWORK网络##################################NETWORK#####################################...#绑定IP,可以绑定多个#如需远程访问,可以填本机IPbind127.0.0.1#默认protected-modeyes#保护模式(默认)port6379#端口(默认)GENERAL通用#windows不支持daemonizeno#以守护进程(后台)运行,默认no,需要改为yessupervisedno#守护进程管理默认no,不用管#windows不支持pidfile/var/run/redis.pid#以守护进程运行,需要指定pid文件loglevelnotice#日志级别,默认notice,支持debug、verbose、notice、warninglogfile""#日志输入文件,默认控制台输出databases16#默认16个数据库always-show-logoyes#启动是的logo显示SNAPSHOTTING快照持久化的配置主要在此部分################################SNAPSHOTTING#################################执行持久化保存的时间save9001#900秒改动1次,执行1次持久化操作save30010#300改动10次,执行1次持久化操作save6010000stop-writes-on-bgsave-erroryes#持久化出错,是否继续工作,默认yesrdbcompressionyes#是否压缩rdb文件,默认yes,需要消耗CPU的性能rdbchecksumyes#保存rdb文件时校验,默认yesdbfilenamedump.rdb#rdb文件的路径,默认当前路径REPLICATION主从复制Redis启动默认为主机#在配置主从复制中只需修改从机的配置文件#replicaof<masterip><masterport>#主机ipportSECURITY安全密码配置文件##################################SECURITY####################################requirepassfoobared#密码,默认没有命令行127.0.0.1:6379>configgetrequirepass#获取密码1)"requirepass"2)""127.0.0.1:6379>configsetrequirepass123456#设置密码OK127.0.0.1:6379>configsetrequirepass(error)NOAUTHAuthenticationrequired.#设置后需要验证密码127.0.0.1:6379>auth123456#验证密码OK127.0.0.1:6379>configgetrequirepass1)"requirepass"2)"123456"CLIENTS客户端客户端的限制###################################CLIENTS#####################################maxclients10000#客户端最大连接数量MEMORYMANAGEMENT内存设置##############################MEMORYMANAGEMENT#################################maxmemory<bytes>#最大的内存容量#maxmemory-policynoeviction#内存达到上限的处理策略#volatile-lru->使用带有过期时间的Key使用lru算法进行删除。#allkeys-lru->使用lru算法进行删除Key#volatile-random->随机删除一些有过期时间的Key#allkeys-random->随机删除一些Key#volatile-ttl->删除最近要过期的Key#noeviction->不要删除任何东西,只在写操作时返回一个错误。【默认】APPENDONLYMODE(AOF持久化)主要功能是记录改变Key的操作##############################APPENDONLYMODE###############################appendonlyno#默认no不开启,大部分情况rdb完全够用appendfilename"appendonly.aof"#aof持久化文件路径appendfsynceverysec#执行aof持久化同步操作的模式,有三种#always->每次修改值,都会执行fsync(),消耗资源#everysec->每秒执行一次fsync(),可能丢失这一秒的数据(默认)#no->不执行fsync(),让操作系统在需要的时候刷新数据,速度快
什么是持久化Redis是内存数据库,数据保存在内存中,当关机时,内存中的数据也就丢失了。持久化就是把内存中的数据写入到磁盘上,保存内存在断电情况下,数据也不会丢失。RDB(RedisDataBase)RDB文件是在硬盘上的二进制文件;RDB文件是Redis在内存存储的数据在某一时刻的快照;RDB文件可在Redis启动的时候自动载入;RDB文件是Redis节点复制时的媒介;相关配置#RDB自动持久化规则#当900秒内有至少有1个键被改动时,自动进行数据集保存操作save9001#当300秒内有至少有10个键被改动时,自动进行数据集保存操作save30010#当60秒内有至少有10000个键被改动时,自动进行数据集保存操作save6010000#RDB持久化文件名dbfilenamedump.rdb#数据持久化文件存储目录dir./#bgsave发生错误时是否停止写入,通常为yesstop-writes-on-bgsave-erroryes#rdb文件是否使用压缩格式rdbcompressionyes#是否对rdb文件进行校验和检验,通常为yesrdbchecksumyes持久化过程Redis会单独创建一个子线程来进行持久化操作,会先将内存中的数据写入到一个文件中,待持久化结束后,再替换掉上次持久化的文件,整个过程中,主进程不进行IO操作,保证了性能。缺点:最后一次持久化的数据可能丢失(子进程还没有写入完成就被某种原因关闭)触发RDB持久化机制的方式手动触发save、bgsavesave命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求。bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即Redis主进程)则继续处理请求。127.0.0.1:6379>saveOK127.0.0.1:6379>bgsaveBackgroundsavingstarted自动触发规则,在配置文件配置save9001save30010save6010000AOF(AppendOnlyFile)快照功能(RDB)并不是非常耐久(durable):如果Redis因为某些原因而造成故障停机,那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从1.1版本开始,Redis增加了一种完全耐久的持久化方式:AOF持久化。需要在配置文件开启,重启Redis服务生效。appendonlyyes相关配置#开启AOF持久化方式appendonlyyes#AOF持久化文件名appendfilenameappendonly-<port>.aof#appendfsyncalways#每次修改值,都会同步数据到磁盘,消耗资源appendfsynceverysec#每秒把缓冲区的数据同步到磁盘#appendfsyncno#不执行数据同步,让操作系统在需要的时候刷新数据#数据持久化文件存储目录dir./#是否在执行重写时不同步数据到AOF文件#这里的yes,就是执行重写时不同步数据到AOF文件no-appendfsync-on-rewriteyes#触发AOF文件执行重写的最小尺寸auto-aof-rewrite-min-size64mb#触发AOF文件执行重写的增长率auto-aof-rewrite-percentage100执行流程windows下需要使用redis-server.exeredis.windows.conf启动,配置文件才生效127.0.0.1:6379>setk1v1OK127.0.0.1:6379>setk2v2OK127.0.0.1:6379>setk3v3OK127.0.0.1:6379>setk4v4OK127.0.0.1:6379>getk2"v2"127.0.0.1:6379>setk5v5OK可以看到修改值的操作都被记录下来了AOF文件修复Redis自带有redis-check-aof程序,用来修复AOF文件,修复后的文件能打开,但会出现数据丢失的情况,至少比不能打开好。删除RDB文件(里面有完整数据),破坏AOF文件(删除某些文件),模拟数据被破坏的场景。启动Redis服务,启动失败使用aof修复工具,修复成功再次启动Redis服务,启动成功查看所有的Key,发现只有两个,其他的已经丢失,至少能打开了扩展对比-RDBAOF启动优先级低高体积小大恢复速度快慢数据安全性丢数据根据策略决定RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令恢复原始的数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写(缩减某些格式),使得AOF文件的体积不至于过大。只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.同时开启两种持久化方式在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。性能建议:因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save9001这条规则。如果启用AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOFrewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOFrewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。如果不启用AOF,仅靠Master-SlaveReplication(主从复制)实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。
乐观锁乐观锁并不是Redis的功能,而是Redis的watch关键字能实现乐观锁。开启事务前,设置对数据的监听(watch),exec时,如果发生数据发生过修改,作用于改数据的事务会自动取消(discard),事务exec后,无论成败,监听会被移除添加监听:watchkey[keys]取消监听(会取消所有的):unwatch乐观锁实例开启监听新开启一个客户端,修改k值127.0.0.1:6379>setkbbOK继续exec执行事务(exec之前k值已经被改变),事务执行不会成功应用场景多个客户端可能同时操作同一组数据,并且该数据一旦被操作修改后,便不适合在原基础上继续操作,如四个业务员对一个物品进行入库操作,一名业务员操作完成之后,其他的业务员将不可以再原先基础的数量上进行入库操作。