redis aof文件浅析
redis中的数据持久化有两种方式:RDB持久化和AOF持久化。RDB相对于AOF来说是一个粗粒度的备份,即RDB可能是一天备份一次,而AOF可能是每秒一次甚至每次写操作进行一次备份。
redis中的aof(append only file)文件的作用就相当于mysql中的binlog,用于记录写操作日志。当redis服务器异常宕机,可以使用该文件对数据进行恢复。进行数据恢复时,redis服务器端会创建一个 fake client(假冒客户端),即通过fake client来模拟redis客户端,该fake client读取aof文件并执行其中的redis command,通过这种方式来模拟redis客户端对redis服务器的操作(增删改),从而达到恢复数据的目的。
另外redis是k-v数据库,数据存储在内存,当然也会定时持久化。在redis服务关闭时,内存中原先的数据就会丢失,所以在redis每次重新启动时,需要将持久化的数据恢复到内存中。持久化的数据就是rdb和aof文件,具体采用的方式是根据redis的配置文件而定。
c++中const变量真的不可以修改吗?
在学c++的时候,看到的大部分的书籍对const关键字的介绍都是:const关键字修饰的变量的值是不可被修改的。但是事实真是如此吗?今天做了一个小的实验,发现const变量是可以被修改的。c++代码如下:
1 #include <iostream>
2 using namespace std;
3
4 int main()
5 {
6 const int a = 3;
7 int* p = const_cast<int*>(&a);
8 *p = 4;
9 cout << "value of p: " << *p << endl;
10 cout << "value of a: " << a << endl;
11 cout << "address of p: " << p << endl;
12 cout << "address of a: " << &a << endl;
13
14 return 0;
15 }
上面代码第7行将a的地址赋值给指针p,然后第8行修改p所指向的地址中的值,运行结果如下:
如上所示结果,指针p所指向的地址和变量a的地址相同,但是p所指地址中的值已经发生改变。但是为何变量a对应的地址中的值已经由3变为4,但是a的值确实3呢?
暂时把这个问题搁浅。再来看一下如下的c++代码:
1 #include <iostream>
2 using namespace std;
3 const int a = 3;
4 int main()
5 {
6 //const int a = 3;
7 int* p = const_cast<int*>(&a);
8 *p = 4;
9 cout << "value of p: " << *p << endl;
10 cout << "value of a: " << a << endl;
11 cout << "address of p: " << p << endl;
12 cout << "address of a: " << &a << endl;
13
14 return 0;
15 }
如上代码g++编译通过,在运行时报错如下:
由此可见,在c++中全局const变量和局部const变量的编译器处理的方法是不一样的。查阅资料得知,全局const变量是不分配内存地址的,它编译器放置在符号表中作为编译期常量,全局const变量放在只读数据段中,受到只读数据段的权限保护,当你修改一个只读数据段中的内容时,会得到一个运行时错误。而局部const变量是放在堆栈之中,因为在内存中有地址,通过修改地址中的值可以达到修改const所指内存中值的目的。
使用crontab自动化拷贝工作
一、 背景
由于开发机上缺少相应的环境,平时我们(特别是像我之类的新人)为了调试方便,往往会直接在测试机上进行开发工作,然后再拷贝到开发机上,提交到svn。然而这样会存在很多问题,有时候我们明明在测试机上测试正常,提测之后却出了问题,仔细检查可能会发现我们拷贝了文件到svn。避免这样的问题,一是在开发过程中,记录自己修改创建的文件,但这样比较繁琐;二是在开发机上开发,自测时使用测试机进行测试。毫无疑问第二种方法要好一些,但是这样往往会增加很多繁琐的拷贝工作,本文就是为了减少这样的工作而撰写。
我们首先要在测试机上建立免密码登陆开发机的信任关系。A为测试机(我的测试机是aaa@bbb.com); B为开发机(我的开发机是ccc@ddd.com);
在A上的命令:
# ssh-keygen -t rsa (机器A上操作,连续三次回车,即在本地生成了公钥和私钥,不设置密码)
# ssh ccc@ddd.com (机器A上操作,ssh登陆机器B时需要输入密码)
# mkdir .ssh;chmod 700 .ssh (机器B上操作,如果.ssh目录存在,则不需要mkdir, 注:必须将.ssh的权限设为700)
# scp ~/.ssh/id_rsa.pub ccc@ddd.com:/home/users/qinlei01/.ssh/id_rsa.pub(机器A上操作,需要输入密码)
登陆机器B,在B上的命令:
# touch ~/.ssh/authorized_keys (如果已经存在这个文件, 跳过这条)
# chmod 600 ~/.ssh/authorized_keys (# 注意: 必须将~/.ssh/authorized_keys的权限改为600, 该文件用于保存ssh客户端生成的公钥,可以修改服务器的ssh服务端配置文件/etc/ssh/sshd_config来指定其他文件名)
# cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys (将id_rsa.pub的内容追加到 authorized_keys 中, 注意不要用 > ,否则会清空原有的内容,使其他人无法使用原有的密钥登录)
回到A机器测试是否能免密码登陆机器B:
# ssh ccc@ddd.com(不需要密码, 登录成功)
=====================================================================
附上A与B互相免密码登陆的方法:
机器A:192.168.0.106
机器B:192.168.0.107
1、在机器A上执行如下操作
ssh-keygen -d
然后一路回车
cd ~/.ssh
cp id_dsa.pub authorized_keys2
chmod 600 authorized_keys2
2、在B上做如上同样的操作
3、建立A到B的信任关系(即A免密码登陆B)
在A中执行(注意需要把文件改名)
scp id_dsa.pub usernameB@192.168.0.107:.ssh/a.pub
在B中的~/.ssh下执行
cat a.pub >> authorized_keys2
此时从A执行ssh到B就不用输入密码了
4、建立B到A的信任关系(即B免密码登陆A)
在B执行
scp id_dsa.pub usernameA@192.168.0.106:.ssh/a.pub
在A执行
cat a.pub >> authorized_keys2
在配置过程需要输入多次密码
此时,无论从A到B,还是从B到A,都不需要输入密码了
如果出现问题,可以删除~/.ssh所有文件(连文件夹),然后重新配置
====================================================================
三、建立拷贝文件脚本
1、在机器A某路径建立拷贝文件脚本(如/home/wiki/xx.sh),在该xx.sh中加入自己需要从开发机上拷贝的文件。例如如下简单的xx.sh(要将xx.sh设定为可执行,可简单chmod 777 xx.sh)
scp qinlei01@db-gouyi.db01:/home/users/qinlei01/xxx /home/wiki/xxx
四、 crontab添加任务
1、 在A机器输入如下命令
# crontab –e (打开crontab的任务列表文件)
2、 输入如上命令后,vi会打开一个文件(不用管他),在其中输入如下命令(如果你想每一分钟执行xx.sh的话)
*/1 * * * * /home/wiki/xx.sh
五、附录
1、crontab使用:http://vbird.dic.ksu.edu.tw/linux_basic/0430cron.php#crontab
六、完结
到现在你的xx.sh脚本将会每隔一分钟执行拷贝任务了,这样你就不需要去关心从开发机拷贝代码到测试机的繁琐事务了。如果想即时将开发机上的代码拷贝到测试机上,只需手动执行xx.sh脚本即可。
xx.sh可以根据你的需要编写一些高级的逻辑,来判断文件自上次拷贝之后是否进行了修改,如果没有修改就跳过拷贝逻辑,来处理复杂的拷贝情况,这里给出 一个实现方案:在开发机上建立一个文件yy.txt,并在开发机上运行一个脚本定时计算需要拷贝的文件的md5值,与之前保存在yy.txt中相应文件的 md5值做比较,如果md5值改变,则写入另外一个文件zz.txt,然后测试机每隔固定时间从开发机拷贝这个zz.txt文件,这样测试机就知道哪些文 件发生了改变,只需要拷贝zz.txt中的文件即可,注意zz.txt中的相应文件的md5值需要覆盖yy.txt中相应文件的md5值,便于下一次比 较。当然如果觉得拷贝zz.txt也不够快速,可以考虑使用k-v数据库来存储。
希望这篇简单的文档能对你有所帮助。
如下的html文件,user.html.需要到
https://github.com/douglascrockford/JSON-js/blob/master/json.js 下载json.js
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2 <html>
3 <head>
4 <title> New Document </title>
5 <meta name="Generator" content="EditPlus">
6 <meta name="Author" content="">
7 <meta name="Keywords" content="">
8 <meta name="Description" content="">
9 </head>
10
11 <body>
12 <script>
13 var my_json='{"FBI":{"length":2,"context":[{"name":"rose","age":25},{"name":"qinlei","age":30}]}}'
14 var my_object=eval('('+my_json+')');
15 document.writeln(my_object.FBI.length);
16 for(i=0;i<my_object.FBI.length;++i)
17 {
18
19 document.writeln(my_object.FBI.context[i].name);
20 document.writeln(my_object.FBI.context[i].age);
21
22 }
23
24 </script>
25 <script src="json.js"></script>
26 <script>
27 var my_object={
28 FBI:[
29 {name:"rose",age:"25"},
30 {name:"jack",age:"25"}
31 ],
32 NBA:[
33 {name:"tom",sex:"man"},
34 {name:"jack",sex:"women"}
35 ]
36 };
37 document.writeln(my_object.toJSONString());
38 </script>
39 </body>
40 </html>
41
1.创建版本库
git init-db,使用该命令会在当前目录下创建版本库。生成一个.git的子目录,该子目录中会有三个需要重点注意的东东,一个HEAD文件,一个objects子目录,一个refs子目录
2.git add,使用该命令将相应文件加入到版本库文件索引中,或者使用git update-index
3.git status,查看版本库状态
4.git commit -m,提交文件到版本库中
5.git diff,查看当前的工作目录和版本库中的差别
6.git branch创建版本分支
7.git checkout ,转到相应的版本分支
8.git branch -D,删除版本分支
为了说明c++的声明顺序所导致的作用域问题,考虑如下代码
1 #include<iostream>
2 //#include <map>
3 //#include <string>
4 using namespace std;
5
6 int a;
7 void first()
8 {
9 a = 1;
10 }
11
12 void second()
13 {
14 int a = 7;
15 first();
16 cout << "second:" << a << endl;
17 }
18
19
20 int main()
21 {
22 a = 2;
23 int num;
24 cin >> num;
25 if (num > 0)
26 {
27 second();
28 }
29 else
30 {
31 first();
32 }
33 cout << a << endl;
34 return 0;
35 }
36
猜想一下上面的代码输出的结果是什么?main函数中输出的结果是1。不论你输入的num值是正数还是负数结果都是1。为什么会这样呢?这是因为c++采用的是静态作用域规则。第9行代码是关键所在。对于c++这种静态语言而言,第9行代码实际修改的是全局变量a的值。所以该程序的最终结果会是1。那么动态作用域规则的语言会输出什么样的结果呢?那就要根据所输入的num来决定了。
c++声明变量的作用就是引进名字符号,表明该变量的作用域,而定义则是给变量分配内存,并且绑定值的过程。对于调用子函数的过程,为了找到子函数中的变量的声明作用域,编译器采用了静态链接的方法。对于程序的执行流程,编译器会维护一个栈,栈中会存储与相应调用函数对应的帧。编译器通过栈和帧数据结构来维护程序执行所调用的函数层次流程。要找到一个子函数中的变量声明域实际上就是在栈中相应帧中寻找该变量的声明。寻找起点是当前活动帧,而当前活动帧又通过静态链接(相当于指针)与它的父帧相关联。但是考虑上面的程序,当输入num大于0时,应该是先调用second,然后调用first,而second中对变量a重新进行了声明,如果栈中维护的层次是函数调用的层次,则此时first中修改的变量a应该是second中声明的变量a才是,那么结果输出应该是2,但是事实并非如此。所以我认为栈中的静态链接所链接的不是函数调用的层次,而是声明的层次。考虑上面的程序,全局变量a和函数first的声明是在同一层次的,则如果要寻找a中变量的声明,应该首先查找a所在的那个模块所对应的帧(姑且认为是全局帧吧,看成有一个全局范围的函数与之对应),则这时找到的a的声明应该就是全局变量a。所以如果按照这种分析的话,那么程序的结果就是1了。
以上只是我的猜想,由于最近要忙于考试,没有时间查阅更多资料,且编译原理那块已经几乎忘得差不多了。如有错误请各位指正。
刚查了下我的邮箱,第一封求职邮件是2月24日发给opera的。直到今天已经不知不觉过去了三个多月,发送的简历数多达200多封,细数面试的次数也应该有几十次,几乎各种类型的企业都有面过。虽然最终还是拿到了比较满意的offer,但是这个过程是充满教训的。
首先,我对自己极度不自信,所以才会采取海投简历的方法,以期望以大的面试基数,来获得面试成功的次数。由于我每天都要投不少简历,所以需要在各大求职网上转悠,浪费了不少原本可以用于打牢专业基础的时间。虽然获得了不少面试机会,也增加了不少面试经验,但是每次面试之后,都没有对自己存在的本质问题进行思考总结,所以每次进步都很缓慢。
当然这个过程也不是一点收获也没有,比如我获取了大量hr们的联系方式,并且掌握了一套获取求职信息的方法。甚至于,我会主动打电话到一些公司的人力部亲自问他们是否缺人,而实践证明这一招还是很不错的方法。很多机会都不是等来的,而是自己争取到的。
关于面试我想主要要做好以下几方面:
1.简历。一定要做好自己的简历,即使花上几天也是值得的,面试官的第一印象就是从简历开始的吧,而且面试中的很多东西都是跟简历相关的。起初我的简历做得比较花哨,但是内容不充实,重点不突出,没有什么亮点,后来再两个师哥的指点下,做了修改,确实好了很多。当然现在的简历还是有很大问题的。一份好的简历都要反复修改,并且要经常更新。对于自己应聘的不同岗位,应该要做一份针对性的简历,当然不是完全重做,可以在一开始就做一份尽量翔实的简历(包括英文简历),包含自己的信息,获奖情况,项目情况,担任班干部情况(这个外企好像会看重一些,有时会问到一些过去的经历,特别是对待困难的态度,感觉外企更注重人的综合素质),然后在此基础上针对不同企业的不同职位增删一些内容。这点其实我并没有做到,这也是做得不到位的地方。
2.面试知识准备
1.专业基础知识,主要是语言,数据结构,算法,网络,linux,数据库......网上都有很多介绍。其中linux和数据库要是熟练掌握是有加分的,由于我对linux及linux下编程不太熟悉,面试中有被鄙视的感觉,然后就加强了这方面的准备。其实linux和数据库方面问的都比较浅,就是看看平时有没有了解过。
2.围绕自己简历内容做准备。这个其实是非常重要的,往往决定性因素可能就在这上面。比如做过数据库方面的项目的话,可以看看数据库的范式,数据优化什么的,我就看了一下范式,然后还真派上用场了。针对每个做过的项目可能涉及的知识点复习一下,比如多线程,socket通信,windows通信机制等,根据项目不同做相应准备。另外对个别自己感觉比较重要的且可能被问到的项目(比如近期做过的项目,跟应聘职位相关性很大的项目,跟应聘公司某 些产品相关的项目)重点做准备。这些项目尽量深入进去,如果实在不太记得自己当时是怎么做的,就自己重新思考一下现在该如何去做,并且上网查一些相关资料。
3.面试经验
4.提问
这个我觉得也是一个可以加分的项。比如深入了解一下应聘公司的某个产品,问问面试官其中的技术实现,然后谈谈自己的想法。不过要注意度,尽量让对方说得爽,同时自己也要有点见解。
教训:
1.海投简历,这是个下招
2.没花太多时间充实自己的基础知识,而是将时间浪费在了海投简历以及面试各种公司上
3.面试过程中,用词不当,我经常爱用“也许”“可能”“我不太记得了”之类的词,感觉这样给人的感觉就是基础很不牢靠。
4.每次面试的时候没有向面试官询问自己面试中存在的不足
5.电话面试太多,其实电话面试有很多劣势。你看不到面试官的表情变化,没法察言观色。建议现场面试,即使跑到北京去也是可以的。
6.简历做得不够好。
7.项目准备不充分,经常被问住,应该多花时间准备项目。
8.电话面试忘记微笑
9.还是基础不够扎实,平时编程也不够,看书也不够全面,不够深入。
10.对自己的定位不明,还不知道自己以后要深入的方向,这是最大的悲哀。这方面zyf童鞋,是我的榜样啊。
待续。。
-----------------------我是低调的分界线------------------
-----------------------投简历参考(补充)----------------
目前我曾经联系过的一部分还需要实习生的公司。具体职位可以使用提供的邮箱百度搜索一下。
**********省略给别人的公司及相关信息
**********
公司还有很多,这个自己平时慢慢找吧,关键是主动获取信息。如果你主动点每天都能投出去10多封邮件的,招人的公司还是非常多的。但是不建议海投简历,还是先打好基础,这样效率会高一些。海投的话也有效,毕竟虽然面试成功几率小,但是面试次数多,所以最终也还是会有比较好的结果的。但是这样很辛苦,还是以打牢基础为主。
另外附上几个我常用的招聘信息网站:
1.Hiall
2.应届生求职网
3.北邮人论坛(这个还是不错的,上面有不少招聘信息),用游客身份登录,然后点“信息社会”->兼职实习信息
4.水木清华bbs(基本上跟上面差不多)
5.鲤鱼网 实习
6.大街网
平时多关注这些网站上的招聘信息。另外平时要多关注一些IT类的新闻。比如说上面的汤森路透可能大家很少听过,那是因为你平时关注这方面的信息比较少,你上网查查这个公司就知道了,还是很牛逼的。如果喜欢互联网行业的话,可以关注一些互联网的最新动态,还有一些创业公司,有些创业公司发展还是很不错的,这里提供几个:五分钟公司,创新工场的行云、友盟、豌豆夹、魔图精灵,人民搜索,另外还可以关注一下团购网站,例如高朋,美团。视频行业:优酷、土豆、百度奇艺,另外腾讯最近投了5亿元发展自己的视频业务,估计最近会有大动作,可以关注一下腾讯视频的实习招聘,如果官网或者招聘网没有公布招聘信息,你可以自己主动打电话过去询问,应该会很缺人。
寻找实习招聘信息:
1.关注上面推荐的招聘信息网站
2.直接打电话到自己想实习的公司。具体方法:进官网,点击诚聘英才(也可能是其他),一般在网站最下方,具体自己去实践吧。然后找到公司的联系方式,注意一般是公司的总机号码,你是得不到分机号码的,你拨总机之后,会有提示,比如按0查询分机号,这个时候会有人接电话,你告诉他你要找实习,让他帮忙转接到人力资源部,然后你再询问人力那边是否有实习生招聘计划就行。
3.牡丹园bbs,计算机学院。会有人发布内推消息
----------------我还是低调的分界线------------------
最后,自己终于能基本上在经济上能独立了。并给自己说一句迟到的“生日快乐”,跟母亲说一句”感谢您“。生日那天恰逢百度笔试,也正好是母亲节,都没过生日。
1.先从redis最初版本开始逐步研究
2.由于最近准备期末考试,所以每天晚上22:00-24:00学习2个小时
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4 #define max(a,b) ((a)>(b))?(a):(b)
5 using namespace std;
6 //写出来之后,尝试把每一个for循环用for_each来替换。或者将公用的for流程用函数替代
7 struct PrintResult
8 {
9 void operator()(int i)
10 {
11 cout << i << " ";
12 }
13 }printResult;
14
15 struct PrintVecResult
16 {
17 void operator()(vector<int> vec)
18 {
19 for_each(vec.begin(), vec.end(), printResult);
20 cout << endl;
21 }
22 }printVecResult;
23
24 int knapsack(vector<int> &vecWeight, vector<int> &vecValue, int capacity)
25 {
26 int num = vecWeight.size();
27 vector<vector<int> > f(num, vector<int>(capacity, 0));
28 vector<int> result(num, 0);
29
30 int j = 0;
31 int i = 0;
32 for (i = 1; i <= num; ++i)
33 {
34 for (j = 1; j <= capacity; ++j)
35 {
36 if (j >= vecWeight[i])
37 {
38 f[i][j] = max(f[i-1][j], f[i-1][j-vecWeight[i]] + vecValue[i]);
39 }
40 else
41 {
42 f[i][j] = f[i-1][j];
43 }
44 }
45 }
46 //打印f数组表
47 for_each(f.begin(), f.end(), printVecResult);
48
49 //打印背包所能容纳的最大价值
50 cout << f[num][capacity] << endl;
51
52 //打印产生最大价值的背包中物品的编号
53
54 for (j = capacity, i = num; i >= 1; --i)
55 {
56 //result[i] = f[i][j] > f[i-1][j] ? 1 : 0;
57 if (f[i][j] > f[i-1][j])
58 {
59 result[i] = 1;
60 j = j - vecWeight[i];
61 }
62 else
63 {
64 result[i] = 0;
65 }
66 }
67
68 for (i = 1; i <= num; ++i)
69 {
70 if (1 == result[i])
71 {
72 cout << i << " ";
73 }
74 }
75 return f[num][capacity] ;
76 }
77
78
79 int main()
80 {
81 int num = 0;
82 int capacity = 0;
83 cin >> num;
84 cin >> capacity;
85
86 vector<int> weight;
87 vector<int> value;
88 weight.push_back(0);
89 value.push_back(0);
90
91 for (int i = 1; i <= num; ++i)
92 {
93 int tempWeight = 0;
94 int tempValue = 0;
95 cin >> tempWeight >> tempValue;
96 weight.push_back(tempWeight);
97 value.push_back(tempValue);
98 }
99
100 knapsack(weight, value, capacity);
101
102 return 0;
103 }
计数排序实现:以学生年龄对学生信息进行排序。
1 #include <iostream>
2 #include <vector>
3 #include <string>
4 #include <algorithm>
5 using namespace std;
6
7 typedef struct StudentInfo
8 {
9 unsigned int age;
10 string name;
11 string major;
12 }StudentInfo;
13
14 struct StudentCount
15 {
16 void operator()(StudentInfo stuInfo)
17 {
18 cout << stuInfo.name << " " << stuInfo.age << stuInfo.major << endl;
19 }
20 }student;
21
22 void Countsort(vector<StudentInfo> &vec, unsigned int nMaxAge)
23 {
24 unsigned int* nCount = (unsigned int*)malloc(sizeof(unsigned int)*nMaxAge);
25 //vector<unsigned int> nCount(nMaxAge, 0);
26 memset(nCount, 0, sizeof(unsigned int)*nMaxAge); //assign 0 to the array nCount
27 vector<StudentInfo>::const_iterator iter = vec.begin();
28
29 //count the number of each age
30 for (; iter != vec.end(); ++iter) //用for_each试一下
31 {
32 ++nCount[iter->age];
33 }
34
35 //确定有多少个数据在当前元素的前面
36 for (int i = 1; i < nMaxAge; ++i)
37 {
38 nCount[i] += nCount[i-1];
39 }
40
41 vector<StudentInfo> StudentSorted;
42 StudentSorted.reserve(vec.size()); //使用reserve预分配空间,这些空间是否有初始值呢?
43 StudentSorted.resize(vec.size());
44 for (int i = 0; i < vec.size(); ++ i)
45 {
46 int index = --nCount[vec[i].age];
47 StudentSorted[index] = vec[i];
48 }
49
50 //将StudentSorted数据复制到vec中。
51 vec.swap(StudentSorted);
52 //for_each(vec.begin(), vec.end(), student);
53 free(nCount);
54 nCount = NULL;
55 }
56 int main()
57 {
58 vector<StudentInfo> vec;
59 //初始化vec中的数据
60 for (int i = 0; i < 20; ++i)
61 {
62 StudentInfo stuInfo = {i%10, "qinlei", "CS"};
63 vec.push_back(stuInfo);
64 }
65
66 //开始排序
67 Countsort(vec, 20);
68 //打印排序后的结果
69 for_each(vec.begin(), vec.end(), student);
70 return 0;
71 }