Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

[Oracle] enq: TX - row lock contention 优化案例

$
0
0

根据开发反馈,最近每天早上7:30应用会报警,应用的日志显示数据库连接池满了,新的连接被拒绝。

首先,我做了ASH报告(报告区间:7:25 ~ 7:35),从ASH的等待事件发现enq: TX - row lock contention居然高达76.54%,如下所示:

Top User Events

    Event Event Class % Event Avg Active Sessions
    enq: TX - row lock contention Application 76.54 0.81
    CPU + Wait for CPU CPU 12.76 0.14
    db file sequential read User I/O 7.40 0.08
    enq: TX - row lock contention等待事件是一种行的等待事件,也就是说同一时刻有多个session请求修改同一行。

    下一步就是找这个等待事件主要由哪些SQL引起的:

    Top SQL with Top Events

      SQL ID Planhash Sampled # of Executions % Activity Event % Event Top Row Source % RwSrc SQL Text
      4rm17788qwxuy 1272661853 54 69.45 enq: TX - row lock contention 69.45 UPDATE 69.45 update shift_case set expertId...
      1cqbcdr0ufyk6 1272661853 10 5.20 enq: TX - row lock contention 5.20 UPDATE 5.20 update shift_case set daySecti...
      1anu5c146v8d7 1272661853 4 1.89 enq: TX - row lock contention 1.89 UPDATE 1.89 update shift_case set daySecti...
      gbw4zk8jv0n0u 2588599834 10 1.57 CPU + Wait for CPU 0.79 TABLE ACCESS - BY GLOBAL INDEX ROWID 0.47 select sc.scId, sc.estId, ct.c...
      dvmk92c1umc97 905317021 9 1.42 CPU + Wait for CPU 1.42 CONNECT BY - NO FILTERING WITH START-WITH 0.63 select h.hospitaluuid id, h.pl...
      从上表可以得出,SQL_ID=4rm17788qwxuy的SQL语句是罪魁祸首,改SQL语句如下:

      4rm17788qwxuy update shift_case set expertId = :1 , shiftDate = :2 , daySection = :3 , rcLimit = :4 , orderingCount = :5 , shareRccount = :6 , clinicTypeUuid = :7 , fee = :8 , isTimeDivision = :9 , state = :10 , isopen=:11 , stateTime = :12 , updateTime = sysdate where scId =:13
      scid是shift_case的主键,也就是说同一时刻有非常多的session在请求更新同一行。

      好了,既然已经定位到问题就好办了,马上把应用开发人员找来一问,真相大白:原来该应用需要从外部系统获取数据,为了让内部的数据库和外部的尽量保持一致,每次查询外部系统时,会在数据库里执行update语句。

      解决办法也简单:由于每次的Update都会把前一次的update覆盖(等于前面的update做的都是无用功),所以根本没必要每次查询都update,只要最后一次查询做update就可以了。

      作者:u010415792 发表于2014-6-4 9:28:56 原文链接
      阅读:0 评论:0 查看评论

      一次JVM内存调优的分享

      $
      0
      0

      这篇文章是关于最近一次性能调优的经历。跟往常一样,开始的时候总会有一些模糊的征兆。这次的现象看起来是”应用程序运行缓慢,但是我们无法获取到对应的源代码。我们该如何来解决这个问题“。

      仔细观察下这个应用会发现它运行着一些批量任务。分析下性能相关的指标会发现它在运行某个特定任务的时候花费的时间太长了。进一步分析我得出了一个可量化的优化目标。我需要将这个任务预分配内存所占用的时间减少两分钟。

      这个出问题的应用程序只是个非常无辜的小jar包而已。幸运的是,我对它进行了压测。

      打开GC日志开关(-XX:+PrintGCTimeStamps -Xloggc:/path-to/gc.log -XX:+PrintGCDetails)后运行这个应用程序,并分析日志,很快你就会发现第一个要优化的目标。GC暂停时间加起来花了有3分半钟,这说明这块是一个优化的机会。

      像这种情况一般有好几种解决办法 ,下面几种是比较简单和直接的:

      • 修改堆的大小
      • 调整GC算法
      • 调整内存堆各区域的大小比例。

      我选择了修改堆大小的方式。这并不是我运气好而蒙对的,是因为我最近学习了一些关于存活数据集大小和推荐的堆大小关系的东西。从GC日志中我注意到存活数据集大概是240M。因此根据我最近所了解到的知识来看,应用的最佳堆大小大概是在720M到960M之间。

      不过我发现现在配置的堆大小只有300M。调整了下参数后我重新运行了下该测试用例 ,结果如下:

      堆大小 总的GC暂停时间吞吐量
      300m 207.48s92.25%
      384m54.13s97.97%
      720m20.52s99.11%
      1,440m*11.37s*99.55%

      *号表示没有出现Full GC。

      如果你光看这个结果的话可能会认为结论是” 越大越好“。如果你只看这个毫秒值的话,你这么想也是对的。但如果成功的指标中有一项是和钱有关的话,就不那么容易了。成百上千台机器上进行大规模部署的话,你这么搞 ,到时光电费单就能吓你一跳。

      除此之外,这篇文章也是性能调优课程上的一个用例。如果你有一个可测量的目标并且你可以去测量它的结果而不是去猜测,那么你就已经成功了。如果我没有一个明确的目标或者没法进行压测的话,可能我现在还在东调整下西调整下那么瞎捣鼓。

      原创文章转载请注明出处: 一次JVM内存调优的分享

      英文原文链接

      LINUX 命令大汇总

      $
      0
      0


      df 命令;
      df 是来自于coreutils 软件包,系统安装时,就自带的;我们通过这个命令可以查看磁盘的使用情况以及文件系统被挂载的位置;
      举例:
      [root@localhost beinan]# df -lh
      Filesystem容量 已用 可用 已用% 挂载点
      /dev/hda8**G 6.0G 4.4G 58% /
      /dev/shm ***M 0 ***M 0% /dev/shm
      /dev/sda1**G 22G 35G 39% /mnt/sda1
      我们从中可以看到,系统安装在/dev/hda8 ;还有一个56G的磁盘分区/dev/sda1挂载在 /mnt/sda1中;
      其它的参数请参考 man df
      系统
      # uname -a               # 查看内核/操作系统/CPU信息
      # head -n 1 /etc/issue   # 查看操作系统版本
      # cat /proc/cpuinfo      # 查看CPU信息
      # hostname               # 查看计算机名
      # lspci -tv              # 列出所有PCI设备
      # lsusb -tv              # 列出所有USB设备
      # lsmod                  # 列出加载的内核模块
      # env                    # 查看环境变量
      资源
      # free -m                # 查看内存使用量和交换区使用量
      # df -h                  # 查看各分区使用情况
      # du -sh         # 查看指定目录的大小
      # grep MemTotal /proc/meminfo   # 查看内存总量
      # grep MemFree /proc/meminfo    # 查看空闲内存量
      # uptime                 # 查看系统运行时间、用户数、负载
      # cat /proc/loadavg      # 查看系统负载
      磁盘和分区
      # mount | column -t      # 查看挂接的分区状态
      # fdisk -l               # 查看所有分区
      # swapon -s              # 查看所有交换分区
      # hdparm -i /dev/hda     # 查看磁盘参数(仅适用于IDE设备)
      # dmesg | grep IDE       # 查看启动时IDE设备检测状况
      网络
      # ifconfig               # 查看所有网络接口的属性
      # iptables -L            # 查看防火墙设置
      # route -n               # 查看路由表
      # netstat -lntp          # 查看所有监听端口
      # netstat -antp          # 查看所有已经建立的连接
      # netstat -s             # 查看网络统计信息
      进程
      # ps -ef                 # 查看所有进程
      # top                    # 实时显示进程状态
      用户
      # w                      # 查看活动用户
      # id             # 查看指定用户信息
      # last                   # 查看用户登录日志
      # cut -d: -f1 /etc/passwd   # 查看系统所有用户
      # cut -d: -f1 /etc/group    # 查看系统所有组
      # crontab -l             # 查看当前用户的计划任务
      服务
      # chkconfig --list       # 列出所有系统服务
      # chkconfig --list | grep on    # 列出所有启动的系统服务
      程序
      # rpm -qa                # 查看所有安装的软件包
      这是一个linux常见命令的列表。
      那些有• 标记的条目,你可以直接拷贝到终端上而不需要任何修改,因此你最好开一个终端边读边
      剪切&拷贝

      所有的命令已在Fedora和Ubuntu下做了测试
      命令
      描述

      apropos whatis
      显示和word相关的命令。 参见
      线程安全

      man
      -t man | ps2pdf - > man.pdf
      生成一个PDF格式的帮助文件

      which command
      显示命令的完整路径名

      time command
      计算命令运行的时间

      time cat
      开始计时. Ctrl-d停止。参见
      sw

      nice
      info
      运行一个低优先级命令(这里是info)

      renice 19 -p $$
      使脚本运行于低优先级。用于非交互任务。
      目录操作

      cd -
      回到前一目录

      cd
      回到用户目录

      (cd dir && command)
      进入目录dir,执行命令command然后回到当前目录

      pushd .
      将当前目录压入栈,以后你可以使用popd回到此目录
      文件搜索

      alias
      l='ls -l --color=auto'
      单字符文件列表命令

      ls -lrt
      按日期显示文件. 参见
      newest

      ls /usr/bin | pr -T9 -W$COLUMNS
      在当前终端宽度上打印9列输出

      find -name '*.[ch]' | xargs grep -E 'expr'
      在当前目录及其子目录下所有.c和.h文件中寻找'expr'. 参见
      findrepo

      find -type f -print0 | xargs -r0 grep -F 'example'
      在当前目录及其子目录中的常规文件中查找字符串'example'

      find -maxdepth 1 -type f | xargs grep -F 'example'
      在当前目录下查找字符串'example'

      find -maxdepth 1 -type d | while
      read
      dir; do echo $dir; echo cmd2; done
      对每一个找到的文件执行多个命令(使用while循环)

      find -type f ! -perm -444
      寻找所有不可读的文件(对网站有用)

      find -type d ! -perm -111
      寻找不可访问的目录(对网站有用)

      locate -r 'file[^/]*\.txt'
      使用locate 查找所有符合*file*.txt的文件

      look reference
      在(有序)字典中快速查找

      grep
      --color
      reference /usr/share/dict/words
      使字典中匹配的正则表达式高亮
      归档 and compression

      gpg -c file
      文件加密

      gpg file.gpg
      文件解密

      tar -c dir/ | bzip2 > dir.tar.bz2
      将目录dir/压缩打包

      bzip2 -dc dir.tar.bz2 | tar -x
      展开压缩包 (对tar.gz文件使用gzip而不是bzip2)

      tar -c dir/ | gzip | gpg -c | ssh user@remote 'dd of=dir.tar.gz.gpg'
      目录dir/压缩打包并放到远程机器上

      find dir/ -name '*.txt' | tar -c --files-from=- | bzip2 > dir_txt.tar.bz2
      将目录dir/及其子目录下所有.txt文件打包

      find dir/ -name '*.txt' | xargs cp -a --target-directory=dir_txt/ --parents
      将目录dir/及其子目录下所有.txt按照目录结构拷贝到dir_txt/

      ( tar -c /dir/to/copy ) | ( cd /where/to/ && tar -x -p )
      拷贝目录copy/到目录/where/to/并保持文件属性

      ( cd /dir/to/copy && tar -c . ) | ( cd /where/to/ && tar -x -p )
      拷贝目录copy/下的所有文件到目录/where/to/并保持文件属性

      ( tar -c /dir/to/copy ) | ssh -C user@remote 'cd /where/to/ && tar -x -p'
      拷贝目录copy/到远程目录/where/to/并保持文件属性

      dd bs=1M if=/dev/sda | gzip | ssh user@remote 'dd of=sda.gz'
      将整个硬盘备份到远程机器上
      rsync (使用 --dry-run选项进行测试)

      rsync -P rsync://rsync.server.com/path/to/file file
      只获取diffs.当下载有问题时可以作多次

      rsync --bwlimit=1000 fromfile tofile
      有速度限制的本地拷贝,对I/O有利

      rsync -az -e ssh --delete ~/public_html/ remote.com:'~/public_html'
      镜像网站(使用压缩和加密)

      rsync -auz -e ssh remote:/dir/ . && rsync -auz -e ssh . remote:/dir/
      同步当前目录和远程目录
      ssh (安全 Shell)

      ssh $USER@$HOST command
      在$Host主机上以$User用户运行命令(默认命令为Shell)

      ssh -f -Y $USER@$HOSTNAME xeyes
      在名为$HOSTNAME的主机上以$USER用户运行GUI命令

      scp -p -r $USER@$HOST: file dir/
      拷贝到$HOST主机$USER'用户的目录下

      ssh -g -L 8080:localhost:80 root@$HOST
      由本地主机的8080端口转发到$HOST主机的80端口

      ssh -R 1434:imap:143 root@$HOST
      由主机的1434端口转发到imap的143端口
      wget (多用途下载工具)

      (cd cmdline && wget -nd -pHEKk http://www.pixelbeat.org/cmdline.html)
      在当前目录中下载指定网页及其相关的文件使其可完全浏览

      wget -c http://www.example.com/large.file
      继续上次未完的下载

      wget -r -nd -np -l1 -A '*.jpg' http://www.example.com/
      批量下载文件到当前目录中

      wget ftp://remote/file[1-9].iso/
      下载FTP站上的整个目录

      wget -q -O- http://www.pixelbeat.org/timeline.html | grep 'a href' | head
      直接处理输出

      echo 'wget url' | at 01:00
      在下午一点钟下载指定文件到当前目录

      wget --limit-rate=20k url
      限制下载速度(这里限制到20
      KB/s
      )

      wget -nv --spider --force-html -i bookmarks.html
      检查文件中的链接是否存在

      wget --mirror http://www.example.com/
      更新网站的本地拷贝(可以方便地用于cron)
      网络(ifconfig, route, mii-tool, nslookup 命令皆已过时)

      ethtool eth0
      显示网卡eth0的状态

      ethtool --change eth0 autoneg off speed 100 duplex full
      手动设制网卡速度

      iwconfig eth1
      显示无线网卡eth1的状态

      iwconfig eth1 rate 1Mb/s fixed
      手动设制无线网卡速度

      iwlist scan
      显示无线网络列表

      ip link show
      显示interface列表

      ip link set dev eth0 name wan
      重命名eth0为wan

      ip link set dev eth0 up
      启动interface eth0(或关闭)

      ip addr show
      显示网卡的IP地址

      ip addr add 1.2.3.4/24 brd + dev eth0
      添加ip和掩码(255.255.255.0)

      ip route show
      显示路由列表

      ip route add default via 1.2.3.254
      设置默认网关1.2.3.254

      tc qdisc add dev lo root handle 1:0 netem delay 20msec
      增加20ms传输时间到loopback设备(调试用)

      tc qdisc del dev lo root
      移除上面添加的传输时间

      host pixelbeat.org
      查寻主机的DNS IP地址

      hostname -i
      查寻本地主机的IP地址(同等于host `hostname`)

      whois pixelbeat.org
      查寻某主机或莫IP地址的whois信息

      netstat -tupl
      列出系统中的internet服务

      netstat -tup
      列出活跃的连接
      windows networking (samba提供所有windows相关的网络支持)

      smbtree
      寻找一个windows主机. 参见findsmb

      nmblookup -A 1.2.3.4
      寻找一个指定ip的windows (netbios)名

      smbclient -L windows_box
      显示在windows主机或samba服务器上的所有共享

      mount -t smbfs -o fmask=666,guest //windows_box/share /mnt/share
      挂载一个windows共享

      echo 'message' | smbclient -M windows_box
      发送一个弹出信息到windows主机(XP sp2默认关闭此功能)
      文本操作 (sed使用标准输入和标准输出,如果想要编辑文件,则需添加newfile)

      sed 's/string1/string2/g'
      使用string2替换string1

      sed 's/\(.*\)1/\12/g'
      将任何以1结尾的字符串替换为以2结尾的字符串

      sed '/ *#/d; /^ *$/d'
      删除注释和空白行

      sed ':a; /\\$/N; s/\\\n//; ta'
      连接结尾有\的行和其下一行

      sed 's/[ \t]*$//'
      删除每行后的空白

      sed 's/\([\\`\\"$\\\\]\)/\\\1/g'
      将所有转义字符之前加上\

      seq 10 | sed "s/^/      /; s/ *\(.\{7,\}\)/\1/"
      向右排N(任意数)列

      sed -n '1000p;1000q'
      输出第一千行

      sed -n '10,20p;20q'
      输出第10-20行

      sed -n 's/.*\(.*\).*/\1/ip;T;q'
      输出HTML文件的字段中的 内容

      sort -t. -k1,1n -k2,2n -k3,3n -k4,4n
      排序IPV4地址

      echo 'Test' | tr '[:lower:]' '[:upper:]'
      转换成大写

      tr -dc '[:print:]'
      过滤掉不能打印的字符

      history | wc -l
      计算指定单词出现的次数
      集合操作 (如果是英文文本的话
      export LANG=C
      可以提高速度)

      sort file1 file2 | uniq
      两个未排序文件的并集

      sort file1 file2 | uniq -d
      两个未排序文件的交集

      sort file1 file1 file2 | uniq -u
      两个未排序文件的差 集

      sort file1 file2 | uniq -u
      两个未排序文件的对称差集

      join -a1 -a2 file1 file2
      两个有序文件的并集

      join file1 file2
      两个有序文件的交集

      join -v2 file1 file2
      两个有序文件的差集

      join -v1 -v2 file1 file2
      两个有序文件的对称差集
      数学

      echo '(1 + sqrt(5))/2' | bc -l
      方便的计算器(计算 φ)

      echo 'pad=20; min=64; (100*10^6)/((pad+min)*8)' | bc
      更复杂地计算,这里计算了最大的FastE包率

      echo 'pad=20; min=64; print (100E6)/((pad+min)*8)' | python
      Python处理数值的科学表示法

      echo 'pad=20; plot [64:1518] (100*10**6)/((pad+x)*8)' | gnuplot -persist
      显示FastE包率相对于包大小的图形

      echo 'obase=16; ibase=10; 64206' | bc
      进制转换(十进制到十六进制)

      echo $((0x2dec))
      进制转换(十六进制到十进制)((shell数学扩展))

      units -t '100m/
      9.69s
      ' 'miles/hour'
      单位转换(公尺到英尺)

      units -t '500GB' 'GiB'
      单位转换(SI 到IEC 前缀)

      units -t '1 googol'
      定义查找

      seq 100 | (tr '\n' +; echo 0) | bc
      加N(任意数)列. 参见
      add
      and
      funcpy
      日历

      cal -3
      显示一日历

      cal 9 1752
      显示指定月,年的日历

      date -d fri
      这个星期五是几号. 参见
      day

      date --date='25 Dec' +%A
      今年的圣诞节是星期几

      date --date '1970-01-01 UTC 2147483647 seconds'
      将一相对于1970-01-01 00:00的秒数转换成时间

      TZ=':America/Los_Angeles' date
      显示当前的美国西岸时间(使用tzselect寻找时区)

      echo "mail -s 'get the train' P@draigBrady.com
      在指定的时间发送邮件

      echo "DISPLAY=$DISPLAY xmessage cooker" | at "NOW + 30 minutes"
      在给定的时间弹出对话框
      locales

      printf "%'d\n" 1234
      根据locale输出正确的数字分隔

      BLOCK_SIZE=\'1 ls -l
      用ls命令作类适于locale()文件分组

      echo "I live in `locale territory`"
      从locale数据库中展开信息

      LANG=en_IE.utf8 locale int_prefix
      查找指定地区的locale信息。参见
      ccodes

      locale | cut -d= -f1 | xargs locale -kc | less
      显示在locale数据库中的所有字段
      recode (iconv, dos2unix, unix2dos 已经过时了)

      recode -l | less
      显示所有有效的字符集及其别名

      recode windows-1252.. file_to_change.txt
      转换Windows下的ansi文件到当前的字符集(自动进行回车换行符的转换)

      recode utf-8/CRLF.. file_to_change.txt
      转换Windows下的ansi文件到当前的字符集

      recode iso-8859-15..utf8 file_to_change.txt
      转换Latin9(西欧)字符集文件到utf8

      recode ../b64  file.b64
      Base64编码

      recode /qp..  file.qp
      Quoted-printable格式解码

      recode ..HTML  file.html
      将文本文件转换成HTML

      recode -lf windows-1252 | grep euro

      字符表
      中查找欧元符号

      echo -n 0x80 | recode latin-9/x1..dump
      显示字符在latin-9中的字符映射

      echo -n 0x20AC | recode ucs-2/x2..latin-9/x
      显示latin-9编码

      echo -n 0x20AC | recode ucs-2/x2..utf-8/x
      显示utf-8编码
      光盘

      gzip  cdrom.iso.gz
      保存光盘拷贝

      mkisofs -V LABEL -r dir | gzip > cdrom.iso.gz
      建立目录dir的光盘镜像

      mount -o loop cdrom.iso /mnt/dir
      将光盘镜像挂载到 /mnt/dir (只读)

      cdrecord -v dev=/dev/cdrom blank=fast
      清空一张CDRW

      gzip -dc cdrom.iso.gz | cdrecord -v dev=/dev/cdrom -
      烧录光盘镜像 (使用 dev=ATAPI -scanbus 来确认该使用的 dev)

      cdparanoia -B
      在当前目录下将光盘音轨转录成wav文件

      cdrecord -v dev=/dev/cdrom -audio *.wav
      将当前目录下的wav文件烧成音乐光盘 (参见cdrdao)

      oggenc --tracknum='track' track.cdda.wav -o 'track.ogg'
      将wav文件转换成ogg格式
      磁盘空间 (参见
      FSlint
      )

      ls -lSr
      按文件大小降序显示文件

      du -s * | sort -k1,1rn | head
      显示当前目录下占用空间最大的一批文件. 参见
      dutop

      df -h
      显示空余的磁盘空间

      df -i
      显示空余的inode

      fdisk -l
      显示磁盘分区大小和类型(在root下执行)

      rpm
      -q -a --qf '%10{SIZE}\t%{NAME}\n' | sort -k1,1n
      显示所有在rpm发布版上安装的

      ,并以包字节大小为序

      dpkg
      -query -W -f='${Installed-Size;10}\t${Package}\n' | sort -k1,1n
      显示所有在deb发布版上安装的

      ,并以KB包大小为序

      dd bs=1 seek=2TB if=/dev/null of=ext3.test
      建立一个大的测试文件(不占用空间). 参见
      truncate
      监视/调试

      tail -f /var/log/messages
      监视Messages
      日志文件

      strace -c ls >/dev/null
      总结/剖析命令进行的系统调用

      strace -f -e open ls >/dev/null
      显示命令进行的系统调用

      ltrace -f -e getenv ls >/dev/null
      显示命令调用的库函数

      lsof -p $$
      显示当前进程打开的文件

      lsof ~
      显示打开用户目录的进程

      tcpdump not port 22
      显示除了ssh外的网络交通. 参见
      tcpdump_not_me

      ps -e -o pid,args --forest
      以树状结构显示进程

      ps -e -o pcpu,cpu,nice,state,cputime,args --sort pcpu | sed '/^ 0.0 /d'
      以CPU占用率为序显示进程

      ps -e -orss=,args= | sort -b -k1,1n | pr -TW$COLUMNS
      以内存使用量为序显示进程. 参见
      ps_mem.py

      ps -C firefox-bin -L -o pid,tid,pcpu,state
      显示指定进程的所有线程信息

      ps -p 1,2
      显示指定进程ID的进程信息

      last reboot
      显示系统重启记录

      free -m
      显示(剩余的)内存总量(-m以MB为单位显示)

      watch -n.1 'cat /proc/interrupts'
      监测文件/proc/interrupts的变化
      系统信息 (参见
      sysinfo
      )

      uname -a
      查看内核/操作系统/CPU信息

      head -n1 /etc/issue
      查看操作系统版本

      cat /proc/partitions
      显示所有在系统中注册的分区

      grep MemTotal /proc/meminfo
      显示系统可见的内存总量

      grep "model name" /proc/cpuinfo
      显示CPU信息

      lspci -tv
      显示PCI信息

      lsusb -tv
      显示USB信息

      mount | column -t
      显示所有挂载的文件系统并对齐输出
      #
      dmidecode -q | less
      显示SMBIOS/DMI 信息
      #
      smartctl -A /dev/sda | grep Power_On_Hours
      系统开机的总体时间
      #
      hdparm -i /dev/sda
      显示关于磁盘sda的信息
      #
      hdparm -tT /dev/sda
      检测磁盘sda的读取速度
      #
      badblocks -s /dev/sda
      检测磁盘sda上所有的坏扇区
      交互 (参见
      linux keyboard shortcut database
      )

      readline
      Line editor used by bash, python, bc, gnuplot, ...

      screen
      多窗口的虚拟终端, ...

      mc
      强大的文件管理器,可以浏览rpm, tar, ftp, ssh, ...

      gnuplot
      交互式并可进行脚本编程的画图工具

      links
      网页浏览器
      miscellaneous

      alias
      hd='od -Ax -tx1z -v'
      方便的十六进制输出。 (用法举例: • hd /proc/self/cmdline | less)

      alias
      realpath='readlink -f'
      显示符号链接指向的真实路径((用法举例: • realpath ~/../$USER)

      set | grep $USER
      在当前
      环境
      中查找

      touch -c -t 0304050607 file
      改变文件的时间标签 (YYMMDDhhmm)

      python -m SimpleHTTPServer
      Serve current directory tree at http://$HOSTNAME:8000/

      本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/32889/showart_1667272.html

      已有 0人发表留言,猛击->> 这里<<-参与讨论


      ITeye推荐



      时间管理-李开复

      $
      0
      0

      人的一生两个最大的财富是:你的才华和你的时间。

      才华越来越多,但是时间越来越少,我们的一生可以说是用时间来换取才华。

      如果一天天过去了,我们的时间少了,而才华没有增加,那就是虚度了时光。

      所以,我们必须节省时间,有效率地使用时间。

       

      如何有效率地利用时间呢?我有下面几个建议:

       

      一. 做你真正感兴趣、与自己人生目标一致的事情。 

       

      我发现我的“生产力”和我的“兴趣”有着直接的关系,而且这种关系还不是单纯的线性关系。

      如果面对我没有兴趣的事情,我可能会花掉40%的时间,但只能产生20%的效果;

      如果遇到我感兴趣的事情,我可能会花100%的时间而得到200%的效果。

      要在工作上奋发图强,身体健康固然重要,

      但是真正能改变你的状态的关键是心理而不是生理上的问题。

      真正地投入到你的工作中,你需要的是一种态度、一种渴望、一种意志。

       

      二.知道你的时间是如何花掉的。

       

      挑一个星期,每天记录下每30分钟做的事情,然后做一个分类

      (例如:读书、准备GRE、和朋友聊天、社团活动等)和统计,看看自己什么方面花了太多的时间。

      凡事想要进步,必须先理解现状。

      每天结束后,把一整天做的事记下来,每15分钟为一个单位

      (例如:1:00—1:15等车,1:15—1:45搭车,1:45—2:45与朋友喝茶……)。

      在一周结束后,分析一下,这周你的时间如何可以更有效率地安排?

      有没有活动占太大的比例?有没有方法可以增加效率?

       

      三.使用时间碎片和“死时间”。 

       

      如果你做了上面的时间统计,你一定发现每天有很多时间流失掉了,

      例如等车、排队、走路、搭车等,可以用来背单字、打电话、温习功课等。

      现在随时随地都能上网,所以没有任何借口再发呆一次。

      我前一阵和同事一起出差,他们都很惊讶为什么我和他们整天在一起,

      但是我的电子邮件都可以及时回答?

      后来,他们发现,当他们在飞机上和汽车上聊天、读杂志和发呆的时候,

      我就把电子邮件全回了。

      重点是,无论自己忙还是不忙,你要把那些可以利用时间碎片做的事先准备好,

      到你有空闲的时候有计划地拿出来做。

       

      四.要事为先

       

      每天一大早挑出最重要的三件事,当天一定要能够做完。 

       

      在工作和生活中每天都有干不完的事,唯一能够做的就是分清轻重缓急。

      要理解急事不等于重要的事情。

      每天除了办又急又重要的事情外,一定要注意不要成为急事的奴隶。

      有些急但是不重要的事情,你要学会放掉,要能对人说 no! 而且每天这三件事里最好有一件重要但是不急的,这样才能确保你没有成为急事的奴隶。 

       

      五.要有纪律。 

       

      有的年轻人会说自己“没有时间学习”,

      其实,换个说法就是“学习没有被排上优先级次序”。

      曾经有一个教学生做时间管理的老师,他上课时带来两个大玻璃缸和一堆大小不一的石头。

      他做了一个实验,在其中一个玻璃缸中先把小石、砂倒进去,最后大石头就放不下了。

      而另一个玻璃缸中先放大石头,其它小石和砂却可以慢慢渗入。

      他以此为比喻说:

      “时间管理就是要找到自己的优先级,

      若颠倒顺序,一堆琐事占满了时间,重要的事情就没有空位了。” 

       

      六.运用80%-20原则。 

       

      人如果利用最高效的时间,只要20%的投入就能产生80%的效率。

      相对来说,如果使用最低效的时间,80%的时间投入只能产生20效率。

      一天头脑最清楚的时候,应该放在最需要专心的工作上。

      与朋友、家人在一起的时间,相对来说,不需要头脑那么清楚。

      所以,我们要把握一天中20%的最高效时间(有些人是早晨,也有些人是下午和晚上;

      除了时间之外,还要看你的心态,血糖的高低,休息是否足够等综合考量),专门用于最困难的科目和最需要思考的学习上。许多同学喜欢熬夜,但是晚睡会伤身,所以还是尽量早睡早起。

       

      七.平衡工作和家庭。 

       

      我对于家庭的时间分配是用下列的原则:?

      划清界限、言出必行——对家人做出承诺后,而且一定要做到,

      但是希望其他时间得到谅解。制定较低的期望值以免造成失望。

      忙中偷闲——不要一投入工作就忽视了家人,有时10分钟的体贴比10小时的陪伴还更受用。

      闲中偷忙——学会怎么利用时间碎片。

      例如:家人没起床的时候,你就可以利用这段空闲时间去做你需要的工作。

       

      注重有质量的时间(quality time)——

      时间不是每一分钟都是一样的,有时需要全神贯注,有时坐在旁边上网就可以了。

      要记得家人平时为你牺牲很多,度假、周末是你补偿的机会 。

      作者:zhaoyazhi2129 发表于2014-6-3 20:17:31 原文链接
      阅读:72 评论:0 查看评论

      美国一名程序员对Swift语言的看法

      $
      0
      0

      英文原文: Swift

      近 20 年来,苹果一直把 Objective-C 作为主要的编程语言,而在今天举行的全球开发者大会上,苹果推出了一个全新的编程语言 Swift。据悉,苹果此次发布的 Swift 兼容 Objective-C 语言,从其开发者指南中,我们看到 Swift 从 Python 和 JavaScript 中吸取了许多东西,从而让 Swift 更易读,或者说更为“口语化”。

      然而,对这类语法更为简洁的语言,许多人会质疑其运行效率。对此苹果指出,Swift 在许多方面性能都比 Objective-C 好。

      美国一名程序员看完了 Swift 的指南后,写出了自己对这个新编程语言的看法。以下为译文:

      Swift 保障体系

      • Swift 可供所有人使用;
      • Swift 语言继承了C语言以及 Objective-C 的特性,且克服了C语言的兼容性问题;Swift 还采用安全编程模式,且加入多种新功能,使编程更简单、灵活;
      • 和许多其他的编程语言一样,Swift 可被 LLVM 编译为本地代码,且它的运行速度特别快,可供很多原生设备使用。

      Swift 功能特性

      • Swift 中并没有加入宏系统;其协议和扩展都源自 Objective-C;
      • Swift 采用 var 声明变量和 let 声明常量;结合 if 和 let,可方便处理可空变量;
      • 单继承类显示重写和内容,外加多种接口;
      • Lambdas 表达式看起来和类 Ruby 块形式十分相似;
      • Swift 没有明确的指标,依赖像 C# 语言一样的二分法价值和引用类型;
      • Swift 是一个没有 globs 的基本模块系统,以进口或重命名分类,通过属性,支持重新导出功能;
      • Swift 支持本地类型推断、整理数值类型,具有更好的文字处理功能,但不支持隐式强制转换,因此所有的代码需要转化为显式类型;
      • 脚本语言支持词典文字,简单的脚本可做大规模的工作,可在多核计算机、集群、网格、云和超级计算机上运行。

      Swift 的独特性

      • Swift 支持默认检查算法功能;
      • Swift 使用[]操作符声明数组和字典,数组还有独特的延伸语义功能;但不清楚是否允许程序员自己执行迭代器协议;
      • Swift 看起来并不像是个表达式语言;
      • Swift 使用 class 来创建类,类可包含字段和方法;
      • Swift 采用 Objective-C 的命名参数和动态对象模型,提供了对 Cocoa 框架和 mix-and-match 的互操作性。基于这些基础,Swift 引入了很多新功能,如结合面向过程和面向对象。
      • 除了代数类型、选择类型和偶然会发生的“”runtime error 外,Swift 没设其他处理错误的讨论区。

      本文链接

      新闻外衣的推荐引擎:今日头条的价值在哪里?

      $
      0
      0

        资讯类APP“今日头条” 今日确认获得1亿美元的C轮融资,估值超过5亿美元。据了解,此轮融资由红杉资本和新浪微博领投。此前“今日头条”已经进行过两轮融资,2012年7月获得SIG海纳亚洲等数百万美元A轮投资,2013年9月获得DST等数千万美元B轮投资。自2012年8月份上线以来,今日头条已经累计用户超过1.2亿。

        一支没有任何新闻门户基因的创业团队,却做出了一款用户规模近1亿的热门新闻客户端产品,这听上去有些不可以思议,但它的确发生了。

        在2013年的新闻客户端大战中,张一鸣和他的今日头条成为了最大的黑马。

        2012年8月,今日头条上线,用“生不逢时”形容它当时的境遇再合适不过了。彼时四大门户均已推出了自己的新闻客户端产品,其中搜狐、网易新闻客户端的用户规模更是已经接近4000万。虽然几大门户的新闻客户端之间还在贴身肉搏,但是对于刚刚踏上战场的今日头条来说,这场战争似乎已经结束了。对手们都是装备精良、割据一方的正规军,而自己则是势单力薄的游击队,胜负看上去实在没什么悬念。

        不过,剧情却没有像大家预想的那样发展。在这场本该属于门户豪强的游戏里,今日头条不仅顽强活了下来,还生生从对手们那里抢下了一块不小的地盘。截至2014年2月中旬,今日头条的用户规模已经超过9000万,日活跃用户1000万,并且保持每月1000万以上的新增用户,看来过亿只是时间问题。

        它究竟是怎么做到的?

        像调教电台一样,调教你的新闻客户端

      今日头条CEO张一鸣是一位典型的连续创业者

        用豆瓣或者虾米电台听歌,正在成为越来越多年轻人的习惯。

        对于喜欢民谣的文艺青年来说,广播里冷不丁冒出一首凤凰传奇的歌简直是件难以忍受的事,而在豆瓣或虾米电台这样的产品上, 用户可以依据个人偏好,通过点赞、跳过或者垃圾箱等几个简单的按钮,逐步调教出属于自己的个性化电台,这样既满足了发现新歌的需求,同时也能最大程度确保播放的都是符合自己口味的歌曲。

        不过,你知道新闻客户端其实也能这么玩吗?

        这就是今日头条这款产品最大的特色,用户可以像调教电台一样,调教自己的新闻客户端。

        一叠厚厚的报纸,令你感兴趣的新闻加起来可能只有一个版;在门户上泡了半个小时,真正用来阅读新闻的时间可能只有5分钟,剩下的25分钟全部花在了浏览标题上面;而RSS工具始终是极少数精英人群的专利。这就是传统新闻内容分发方式的真实写照。

        多年来,受众对这种模式已经麻木了,很少有人再去想,为什么不能存在这样一家媒体,上面发布的每一篇新闻都是自己关心的。而这正是今日头条想要做到的事情。

         一旦绑定你的社交媒体账号,今日头条的推荐引擎就能迅速根据你账号的标签、好友、转发等信息分析出你大致的兴趣爱好,从而向你推荐相应的内容,而且,随着算法的不断进化以及用户使用时长的增加,这种推送也会变得愈发精准,目前今日头条9000万的用户中有1/3都绑定了自己的社交媒体账号。

        凭借这一招鲜的推荐绝活,今日头条这家一年前还名不见经传的创业公司,已经成为了引领行业发展方向的标杆,并且受到资本的追捧。

        今年1月中旬,搜狐在发布搜狐新闻客户端4.0版本的同时,宣布了“个性化、视频化、本地化和社交化”的产品发展规划。其中的“个性化”正是要以今日头条为学习模板。搜狐产品副总裁方刚在搜狐新闻客户端合作伙伴年会上明确表示,下一步会根据用户的阅读行为和喜好向其推荐新闻,最终实现“千人千面”的目标,即每个用户看到的新闻都不尽相同。值得一提的是,方刚特意在演讲PPT中提到了今日头条,并且坦言正是因为今日头条这匹黑马的杀出,令搜狐新闻客户端2013年未能完成预定的目标。

        穿着新闻外衣的推荐引擎

        虽然有着一个新闻性十足的名字“今日头条”,而且看上去与同类产品大同小异,但在骨子里,今日头条则完全是另外一个物种。与其将它称为新闻客户端,其实叫它推荐引擎更为准确,因为从出发点上,今日头条就与门户网站的新闻客户端截然不同。

        传统门户想要做的是一款提供新闻资讯的移动App,完成从PC向移动的过渡,而张一鸣想做的却是一款基于移动互联网的推荐引擎,新闻资讯仅仅是它所承载的内容而已。

        而之所以将推荐内容选定在新闻资讯,实际上是一个排除法后的产物。

        今日头条CEO张一鸣告诉《商业价值》,他们先将能够被推荐的信息按照题材和类别全部罗列了出来,接着进行筛选。

        “我们首先排除了小说,比如一年你就看10本书,你可能就用我10次。后来觉得也不该推荐游戏,因为它也是一次投入很久,用户选择的频次也少,依此类推,还有许多东西都是不这么优先的。”张一鸣说。

        除了排除这些不适合推荐的内容,还有一类则是操作起来难度较大的。“比如全购物领域就不太好做,比如你家里灯泡坏了就想换灯泡,窗帘坏了想换窗帘,但是我怎么知道它们什么时候坏呢?因为没办法拿到这些信息,它们坏了也不会通知我。”

        经过层层筛选,张一鸣最终选择了用户使用频次高、覆盖广、适宜推荐的新闻资讯作为推荐的内容。

        用推荐引擎分发新闻,是对传统新闻门户工作方式的一次巨大颠覆。

        传统的新闻门户都有着庞大的编辑团队,编辑从每天抓取到的海量新闻中,按照一定的价值判断标准,选择出一些所谓重要的、用户感兴趣的新闻推荐到首页,或者排在靠前的位置。

        这种模式固然可行,但并不完美。人工推荐模式的背后所追求的是信息覆盖的广度,只有大家都感兴趣的新闻才能为网站带来足够多的流量,所以这就意味着一些小众的的长尾信息需求无法得到满足。

        举个简单的例子,如果一位用户喜欢一支乏人问津的NBA弱旅,那他就很难在门户首页上看到这支球队的消息。因为放在首页的永远是那些战绩最好、最炙手可热的球星和球队的消息。

         想要解决长尾的信息需求,推荐引擎就成为了最好的选择。但对于门户网站来说,多年来的工作习惯早已养成,而且传统的人工编辑模式依旧运转良好,因此也没有足够的动力和危机感去推动这样的改革。况且庞大的编辑团队也是转型的负担,一旦全部改用机器推荐了,那么这些编辑怎么办?此外,一些政策层面的因素也制约着传统门户的手脚。

        不过,这些“拖后腿”的因素在张一鸣这里并不是问题,没有任何门户工作经验反而是他能够做成今日头条的原因。 张一鸣是一位典型的连续创业者,他曾是饭否的技术合伙人,负责饭否的搜索、热词挖掘。离开饭否后,他创办了房产搜索网站九九房,在公司走上正轨后,对技术更感兴趣的他将公司交给了他人打理,自己则开始了新一次创业——字节跳动科技,并将公司的目标锁定在了推荐引擎上面。

        2012年3月,新公司推出了成立后的第一款产品——搞笑图;5月,第二款产品内涵段子上线;8月,今日头条上线。这也是今日头条至今为止三款最重要的产品。所有产品后台的技术架构基本是一样的,都是利用推荐引擎分发信息,但产品难度逐次增加。对于张一鸣而言,无论是搞笑图还是内涵段子,其实都是为了今日头条练手和铺垫。

        事实最终证明,用推荐引擎分发新闻,是一条切实可行的道路。

        虽然其他新闻客户端产品都有不少的编辑原创内容,比如网易的“每日轻松一刻”就颇受用户欢迎,但是张一鸣却不为所动,坚持将机器推荐进行到底,最大程度上排除人工的因素。

        当然,今日头条也并非完全没有编辑团队,但他们所承担的主要工作是审查内容,规避政策和法律层面的风险。

        正因为是与传统新闻客户端完全不同的物种,张一鸣也并没有把其他的新闻客户端视为自己的对手。在被问及谁才是今日头条最有威胁的对手时,张一鸣给出的答案十分简单,“(我们的对手)不是我们的同行。”

        那究竟真正对手是谁,张一鸣并不愿多说,不过在他看来,至少微信的朋友圈都是一个比新闻客户端更具威胁的产品。

        虽然张一鸣没有透露自己的“假想敌”,但是不妨推测一下,今日头条的对手一定是与它类似、帮助用户发现新内容的产品,环顾眼下国内的热门应用,豌豆荚与今日头条似乎有不少共通的地方。

        需要注意的一点是,张一鸣从来没有把推荐引擎的内容只局限在新闻资讯上面。“我们做今日头条的时候就还犹豫要不要做一个更综合的东西,比如叫今日发现。后来觉得没有必要了,就在上面扩充就好了。”据他介绍,今日头条下一步会向用户推荐更多新闻之外的内容,比如会根据用户所处的不同使用场景向其推荐更多维度的信息。比如一位用户要去丽江旅游,今日头条会贴心地为他推荐丽江旅游攻略,或者当地的美食信息等。

        总而言之,如果你现在还只是将今日头条当做是一款新闻客户端,那就大错特错了。

        从新闻客户端到信息分发商

        虽然有着一款颇具竞争力的产品,但是今日头条的崛起背后也有许多其他的因素。

        事实上,对于普通用户来说,并不知道推荐引擎或者个性化新闻究竟意味着什么,促使他们选择今日头条的原因仅仅是因为它的名字。在许多对手眼中,今日头条的崛起在很大程度上也是占了名字的便宜。

        相对于传统新闻门户清一色的“网站名+新闻客户端”的命名方式,今日头条的名字从营销角度讲更容易抓住受众的眼球,尤其是对互联网公司并不十分了解的小白用户,今日头条这个直白的名字能够瞬间抓住他们的眼球。

        为了给产品起名,张一鸣和团队着实花了一番功夫。他们将历史上成功和失败公司的名字先列出来进行参考,然后又找来各大应用市场的排行榜,研究前100名产品的命名规律,尤其是其中的新产品。

        “我们是比较重视方法论的团队,我们把名字分为3类,一类是像百度这种意义很完整、很综合的名字,一类是像小米这种拟物的、具象的名字,最后还有一类就是大白话,我们对这三种做了分析,最后发现排行榜上靠前的都是大白话类型的,比如铃声大全、高清影视之类的,所以我们知道一定不能起一个很装的名字。”

        最终,“今日头条”这个名字从近百个候选方案中脱颖而出。也正是由于这个新闻性十足的名字,许多用户选择了今日头条,而在使用过程中,随着推荐新闻的愈发精准,不少用户渐渐喜欢上了这款产品。

        当然,今日头条的崛起并不能简单归结于某个因素。

        “做得差的公司有时候并不是做错了什么,他们也做对了很多事,而做得好的公司只是因为做到了更加极致,过了及格线而已。”张一鸣说,“我们在不少事情上做得还不错。比如我们的名字相对于错的来说是还不错,我们的技术稳定相对于差的公司做得还不错。推广虽然没有做到最好,但是关键时间点,该做的事情我们也都做了。”

        个性的产品,出色的运营等等多重因素的叠加最终成就了今天的今日头条。而在产品初步成功后,今日头条从去年11月开始,启动了自己的商业化。

        对于商业模式,张一鸣从创业初就已经想得十分清楚,就是坚定地选择了广告这一条路径,而诸如游戏联运、电商、增值业务、会员机制等业内的常见做法则并不在他的考虑范围之内,他解释说,因为并没有看到用户有这方面的需求。

        推荐引擎的信息分发模式对于广告主来说,有着很大的吸引力。启动商业化后,很快就有不少知名厂商找上门来寻求合作。包括东风雪铁龙、沃尔沃、乐视等公司都已经是今日头条的客户,眼下,公司的收入足以覆盖每月的运营成本。春节过后,今日头条的广告系统1.0版本也已上线,商业化又向前迈进了一步,据张一鸣介绍,公司今年的营收目标是达到2亿。

        对于传统门户来说,今日头条的闪电崛起又是一个外来势力搅局的经典商业案例,一个毫无媒体从业背景的创业者,只用了一年时间就摇生变,成为了传媒新贵,这给整个传媒产业带来了许多新的思考,尤其是这种利用推荐引擎分发新闻资讯的创新方式,究竟会对整个媒体行业,尤其是内容生产方式造成怎样的深远影响,这绝对是一件值得长期关注的事情。

      开发者常用的10大GUI测试框架

      $
      0
      0

      1.Abbot - Java GUI 测试框架

      Abbot是一个基于GUI的简单的Java测试框架,它能够帮助开发者测试Java用户界面。 它提供事件自动生成和验证Java GUI组件,使您能够轻松地启动,探索和控制应用程序。开发者可通过脚本和编译代码两种方式来使用Abbot框架,这就是为什么它被认为是在开发者的系统测试和QA的功能测试中都能用到的最完美的GUI测试工具。

      测试工具abbot

      2.EggPlant - GUI自动化测试工具

      EggPlant是一个QA的GUI自动化测试工具,它是为使专业商业软件的应用程序测试和手工测试更加简单方便而特别设计的。 因为它不与底层代码进行交互,并且使用图像匹配技术与被测试的所寻找的对象级别应用程序完全不同,所以EggPlant是用于对那些出现问题的应用程序进行QA自动化测试和黑盒测试的最理想的工具,而这些出现问题的应用工具包括Flash, java, HTML, .Net, Silverlight等。

      测试工具EggPlant

      3.GUIdancer - Eclipse的GUI自动化测试工具

      GUIdancer是一个用于GUI自动测试的Eclipse测试工具,这个工具强大的行为库可以用于创建Java 和 HTML应用的功能测试。 它可以让那些以前没做过编程工作的测试人员编写模块化的灵活的测试 。 创建测试只需从GUIdancer库中所需的模块拖放即可完成。

      测试工具GUIdancer

      4.HP WinRunner

      HP WinRunner 是一个自动化的GUI功能测试工具,它支持用户以测试脚本的形式记录和回退UI的交互。由于它能获取、验证和自动回退用户的交互,因此你可以识别缺陷和确定业务流程是否按照所设计的那样工作。

      测试工具HP WinRunner

      5.IcuTest - GUI单元测试工具

      IcuTest是用于WPF应用程序上的GUI单元测试工具。它不仅仅是另外一个能记录和回退的自动化系统,它还可以直接与你的测试框架相结合。开发者可以通过测试代码同时进行调试来测试应用,在没有打开整个应用和个人GUI组件的情况下这种测试也是很容易的。

      测试工具IcuTest

      6.iMacros

      iMacros是用于网络测试 、自动化网络和数据提取的独特工具,它不但能记录你的工作,还能回放你那些重复的工作。它能够填充表格并自动化的下载和上传文本、图片、文件和网页,也可以轻松的将你的数据通过CSV或XML文件格式或数据库或任何其他来源导入/导出到web应用程序。

      测试工具iMacros

      7.FitNesse

      FitNesse是一个完整独立的Wiki站点——包含一个网络服务器和自动化测试工具。它完美的应用于协作验收测试,而提供详细描述系统功能的可读信息的单元测试则不是它的菜。

      测试工具FitNesse

      8.Maveryx - 开源的自动化测试框架

      Maveryx是一个用于Java 应用程序功能、回归、GUI和数据驱动测试的新型自动化工具。 它不需要任何用于构建和执行测试的GUI图形,而是使用GUI对象探测器来测试GUI对象并在运行的时候通过操作控制它来进行直接识别。

      测试工具Maveryx

      9.QAliber

      QAliber是一个用于 windows操作系统平台下桌面和网络应用的免费和开源的GUI自动化测试工具。它包含一套用于 .NET下自动化开发的工具和一个无需任何编码知识技能的GUI自动化编写工具。

      测试工具QAliber

      10.Selenium - 软件测试

      Selenium是一组跨越多种平台的web应用程序自动化测试工具 。通过使用Selenium,开发人员在不需要学习任何测试脚本语言的情况下,可以很容易地使用记录/回放测试工具来编写测试。Selenium 是真正的多平台、多浏览器测试工具,提供对一些流行的编程语言的支持,包括c#、Java、Groovy、Perl、PHP、Python、Ruby和各种流行的测试框架。

      测试工具Selenium

      本文翻译自dreamcss博客>>

      作者:wuyuanjingni 发表于2014-6-3 14:40:29 原文链接
      阅读:96 评论:0 查看评论

      Web前端--黑客技术揭秘(菜鸟知识)

      $
      0
      0

      一,Web安全的关键点

      1.同源策略是众多安全策略的一个,是Web层面上的策略,非常重要。

      2.同源策略规定:不同域的客户端脚本在没明确授权的情况下,不能读写对方的资源。

      3.同域要求两个站点同协议,同域名,同端口。

      4.当然,在同一个域内,客户端脚本可以任意读写同源内的资源,前提是这个资源本身是可读可写的。

      5.安全类似木桶原理,短的那块板决定了木桶实际能装多少水。一个Web服务器,如果其上的网站没做好权限分离,没控制好信任关系,则整体安全性就由安全性最差的那个网站决定。

      6.一个安全性非常好的网站有可能会因为建立了不可靠的信任关系,导致网站被黑。

      7.CSRF是跨站请求伪造。CSRF会借用目标用户的权限做一些借刀杀人的事(注意是“借用”,而不是“盗取”目标权限),然后去做坏事,“盗取”通常是XSS(跨站脚本攻击)最喜欢做的事。


      二,前端基础

      1.为了解决CSS兼容性而发展的CSS Reset技术,该技术会重置一些样式(这些样式在不同的浏览器中有不同的呈现),后续的CSS将在这个基础上重新开始定义自己的样式。

      2.为了解决JavaScript兼容性,诞生了许多优秀的JavaScript框架,如jQuery, YUI等等。

      3.URL的请求协议几乎都是HTTP,它是一种无状态的请求响应,即每次的请求响应之后,连接会立即断开或延时断开(保持一定的连接有效期),断开后,下一次请求再重新建立。

      4.HTTP是无状态的,那么每次在连接时,服务端如何知道你是上一次的那个?这里通过Cookies进行会话跟踪,第一次响应时设置的Cookies在随后的每次请求中都会发送出去。Cookies还可以包括登录认证后的身份信息。

      5.iframe标签还有一些有趣的安全话题,当网站页面使用iframe方式潜入一个页面时,我们约定网站页面是父页,而被嵌入的这个页面是子页。

      6.如果父页和子页之间是同域,那就很容易,父页可以通过调用子页的contentWindow来操作子页的DOM树,同理,子页可以调用父页的contentWindow来操作父页的DOM树。如果它们不同域,则必须遵守同源策略,但子页还是可以对父页的location值进行写操作,这样可以让父页重定向到其他网页,不过对location的操作仅仅只有写权限,而没有读权限,这样就不能获取父页location URL的内容,否则有可能会造成隐私数据泄漏,比如,有的网站将身份认证token存在于URL中。

      7.对跨站师来说,大多数情况下,有了XSS漏洞,就意味着可以注入任意的JavaScript,有了JavaScript,就意味着被攻击者的任何操作都可以模拟,任何隐私信息都可以获取到。可以说,JavaScript就是跨站之魂。

      8.从window.location或location处可以获取URL地址中的数据。

      9.异步和同步对应,异步可以理解为单独开启了一个线程,独立于浏览器主线程去做自己的事,这样浏览器就不会等待(阻塞),这个异步在后台悄悄进行,所以利用AJAX的攻击显得很诡异,无声无息。AJAX本身就是由JavaScript构成的,只是XML并不是必需的,XML在这里是想指数据传输格式是XML,比如,AJAX发出去的HTTP请求,响应回的数据是XML格式,然后JavaScript去解析这个XML DOM树得到相应节点的内容。其实响应回的数据格式还可以是JSON(已经是主流),文本,HTML等等。AJAX中特别提到XML是因为历史原因。

      10.AJAX的核心对象是XMLHttpRequest。

      11.AJAX是严格遵守同源策略的,既不能从另一个域读取数据,也不能发送数据到另一个域。不过有一种情况,可以发送数据到另一个域,W3C的新标准中,CORS开始推进浏览器支持这样的跨域方案,现在的浏览器都支持这个方案了,过程如下:

      www.foo.com(来源域)的AJAX向www.evil.com(目标域)发起了请求,浏览器会给自动带上Origin头,如下:

      Origin:  http://www.foo.com

      然后目标域要判断这个Origin值,如果是自己预期的,那么就返回。

      12.如果目标域不设置Access-Control-Allow-Origin:http://www.foo.com,那么隐私数据可以被偷到吗?答案是肯定的。

      13.对于GET方式,实际上就是一个URL。

      14.对于POST的请求,前面说的XMLHttpRequest对象就是一个非常方便的方式,可以模拟表单提交,它有异步与同步之分,差别在于XMLHttpRequest实例化的对象xhr的open方法的第三个参数,true表示异步,false表示同步,如果使用异步方式,就是AJAX。异步则表示请求发出去后,JavaScript可以去做其他事情,待响应回来后会自动触发xhr对象的onreadystatechange事件,可以监听这个事件以处理响应内容。同步则表示请求发出去后,JavaScript需要等待响应回来,这期间就进入阻塞阶段。

      15.Cookie是一个神奇的机制,同域内浏览器中发出的任何一个请求都会带上Cookie,无论请求什么资源,请求时,Cookie出现在请求头的Cookie字段中。

      16.Cookie经常被用来存储用户的会话信息,比如,用户登录认证后的Session,之后同域内发出d请求都会带上认证后的会话信息。

      17.HttpOnly是指仅在HTTP层面上传输的Cookie,当设置了HttpOnly标志后,客户端脚本就无法读写该Cookie,这样能有效地防御XSS攻击获取Cookie。

      18.Secure Cookie机制指的是设置了Secure标志的Cookie仅在HTTPS层面上安全传输,如果请求是HTTP的,就不会带上这个Cookie,这样能降低重要的Cookie被中间人截获的风险。

      19.本地Cookie与内存Cookie,它与过期时间(Cookie的expires字段)紧密相关。如果没设置过期时间,就是内存Cookie,这样的Cookie会随着浏览器的关闭而从内存中消失;如果设置过期时间是未来的某个时间点,那么这样的Cookie就会以文本形式保存在操作系统本地待过期时间到了才会消失。

      20.删除Cookie时,仅需设置过期值为过去的时间即可。Cookie无法跨浏览器存在。

      21.Flash是跨浏览器的通用解决方案,Flash Cookie的默认存储数据大小是100KB。

      22.如果在h1之前有大段非法字符,如何保证h1的代码顺利解析?在h1之前加上{}即可,如果是在IE下,加上}即可,这是浏览器解析差异导致的。

      {}h1{font-size:50px; color:red;}


      三,前端黑客之XSS

      1.XSS即跨站脚本,发生在目标网站中目标用户的浏览器层面上,当用户浏览器渲染整个HTML文档的过程中出现了不被预期的脚本指令并执行时,XSS就会发生。

      目标网站的目标用户:这里强调了场景

      浏览器:因为这类攻击是由浏览器来解析执行的。

      不被预期的:那么就很可能是攻击者在输入时提交了可控的脚本内容,然后在输出后被浏览器解析执行。

      2.跨站脚本的重点不在“跨站”上,而应该在“脚本”上,这是从字面上来分析的。因为这个“跨”实际上属于浏览器的特性,而不是缺陷,造成“跨”这样的假象是因为绝大多数XSS攻击都会采用嵌入一段远程或者说第三方域上的脚本资源。

      3.通俗地总结XSS为:想尽一切办法将你的脚本内容在目标网站中目标用户的浏览器上解析执行即可。

      4.XSS有三类:反射型XSS(也叫非持久性XSS),存储型XSS(也叫持久型XSS)和DOM XSS 。

      5.存储型XSS的攻击是最隐蔽的。


      四,前端黑客之CSRF

      1.在跨站的世界中,CSRF同样扮演着及其重要的角色。CSRF的全称是Cross Site Request Forgery,即跨站请求伪造。

      攻击的发生是由各种请求造成的,对于CSRF来说,它的请求有两个关键点,跨站点的请求与请求是伪造的。

      2.安全风险总是出现在正常的流程中,现在我们发出的是一个删除文章的GET请求,对于合法的跨域请求,浏览器会放行。

      3.HTML中能够设置src/href等链接地址的标签都可以发起一个GET请求。

      4.还有通过JavaScript动态地生成的标签对象或CSS对象发起的GET请求,而发出POST请求只能通过form提交方式。

      5.由于JSON格式的简洁与强大,网站开始逐渐使用JSON代替传统的XML进行数据传输。

      6.JSON数据如果以字典形式返回,直接在浏览器中显示会报错,原因是浏览器以为“{”开头的脚本应该是一段左右花括号包围住的代码块。所以,对这种JSON数据的处理,一般会这样:

      eval(“(”+JSON_DATA+")");  //前后加上圆括号

      7.对于使用列表形式返回的JSON数据,它是一个Array对象,以前可以通过劫持Array数据来进行JSON HiJacking攻击。


      五,前端黑客之界面操作劫持

      1.界面操作劫持攻击时一种基于视觉欺骗的Web会话劫持攻击,它通过在网页的可见输入控件上覆盖一个不可见的框(iframe),使得用户误以为在操作可见控件,而实际上用户的操作行为被其不可见的框所劫持,执行不可见框中的恶意代码,从而完成在用户不知情的情况下窃取敏感信息,篡改数据等攻击。

      2.界面操作劫持分为三种:点击劫持,拖放劫持和触屏劫持。

      3.在浏览器中,拖放操作是不受“同源策略”限制的,用户可以把一个域的内容拖放到另一个不同的域。因此,突破同源策略限制的拖放劫持可以演化出更广泛的攻击形式,突破很多种防御。

      4.控件位置之间的层次关系使用z-index,而且任何浏览器都支持:

              z-index:1,数值可以是负数,高数值的控件会处于低数值控件的前面,数值越高,控件越靠近用户。

       

      六,漏洞挖掘

      1.回到XSS漏洞挖掘上,上面说了攻击者可控的输入点有<path>,<query>,<fragment>三个,其实<fragment>里的值一般不会出现在服务端解析,除非Web 2.0网站。

      2.最普通的场景出现在<div id="body" >[输出]</div>位置,那么提交:

          id=1<script>alert(1)</script>就可以触发XSS了。

          可如果出现在下面这些标签中呢?

          <title></title>

          <textarea></textarea>

          <xmp></xmp>

          <iframe></iframe>

          <noscript></noscript>

          <noframes></noframes>

          <plaintext></plaintext>

          比如,代码<title><script>alert(1)</script></title>会弹出提示框吗?答案是:都不会!这些标签之间无法执行脚本。XSS漏洞挖掘机制必须具备这样的区分能力,比如,发现出现在<title></title>中,就将提交的payload变为:

          </title><script>alert(1)</script>

          除了这些,还有两类特殊的标签<script>和<style>,它们是不能嵌套标签的,而且payload构造情况会更灵活,除了闭合对应的标签外,还可以利用它们自身可执行脚本的性质来构造特殊的payload。

      3.HTML是一个很不严格的标记语言(它的反面代表是XML),属性值可以不用引号,或者使用单引号,双引号,反单引号(仅IE浏览器支持)进行引用。

      4.“探子”的目的有两个:目标参数值是否出现在响应上,如果不出现,就完全没必要进行后续的payload请求与分析,因为这些payload请求与分析可能会进行多次,浪费请求资源;目标参数值出现在HTML的哪个部分,从上面的分析我们已经知道,不同的HTML部分对待XSS的机制是不一样的,请求的payload当然也不一样。

      5.肉眼看到的一个文字或符号单元就是一个字符(包括乱码),一个字符可能对应1~n字节,1字节为8位,每一位要么为1,要么为0。

      6.一个字符对应1~n字节是由字符集与编码决定的,比如,ASCII字符集就是一个字符对应1字节,不过1字节只用了7位,最高位用于其他目的,所以ASCII字符集共有2的7次方(128)个字符,基本就是键盘上的英文字符(包括控制符)。

      7.<!-- [if IE] >所有的IE可识别<! [endif] -->

         <!-- [if IE 6] >仅IE6可识别<! [endif] -->

         <!-- [if lt IE 6] >IE6以及IE6以下版本可识别<! [endif] -->

         <!-- [if gte IE 6] >IE6以及IE6以上版本可识别<! [endif] -->
        这是IE所独有的,在其他浏览器看来与普通注释无异,但是在IE看来却是可根据条件执行的,这给我们绕过过滤器创造了可乘之机。

      8.目前在XSS中常用的伪协议有三个:javascript:,vbscript:(协议名也可以简写为vbs:)和data:

      9.同HTML标签和属性的特点相似,伪协议的协议名也是不区分大小写的,并且跟事件相仿,数据也可以做自动的HTMLDecode解码以及进制解码。

      10.@charset为规则;!important为声明。其中能被我们利用插入XSS脚本的地方只有CSS资源类属性值和@import规则,以及一个只能在IE浏览器下执行的属性值expression。

      11. var a = "123</script><script>alert(1);</script>";

         对HTML页面中的JavaScript代码来说,</script>闭合标签具有最高优先权,可以在任何位置中断JavaScript代码。所以,在实际的过滤器实现中,事实上还会区分引用变量中是否使用了</script>闭合标签,如果使用了,则要用反引线做转换“<\/script>”。另外,还要注意引用变量的数据走向,看能否有DOM XSS的可能性。

      12.根据需求的不同,JSON大体上有两种格式:没有callback函数名的裸Object形式和有callback函数名的参数调用Object的形式,如下:

        [{"a":"b"}]

        callback([{"a":"b:}])

        后者的存在主要是为了跨域数据传输的需要,而这个特性通常也成了攻击者跨域获取用户隐私数据的重要渠道。

       

      七,漏洞利用

      1.<script>标签请求内容可跨域,这是合法的功能,请求到是数据必须是合法的JavaScript语法格式。这种技术在之前有提过,包括请求回来的是JSON+CallBack函数这样的数据内容(这种跨域数据通信被称为JSONP)。

       

      八,HTML5安全

       

      九,Web蠕虫

      1.Web蠕虫主要包括:XSS蠕虫,CSRF蠕虫,Clickjacking蠕虫,这三类蠕虫都与具体的漏洞风险有关系,从名字上很好区分。为了更好地表述Web蠕虫思想,会顺带提及第四类:文本蠕虫。

      2.这些蠕虫除了利用的漏洞不一样,其本质是一样的,都是使参与进Web2.0交互的用户受到了欺骗,导致被动或主动(或介于两者之间)地传播了威胁。从XSS蠕虫到CSRF蠕虫,再从Clickjacking蠕虫到文本蠕虫,越往后,社工的成分越大。

      3.利用了大众的心理,在心理作用的驱使下去传播,我们称之为文本蠕虫。

      4.蠕虫具有的最主要的两个性质如下:传播性和病毒行为。

       

      十,关于防御

      作者:ljiechang 发表于2014-6-3 23:28:42 原文链接
      阅读:125 评论:0 查看评论

      Struts2获取request三种方法

      $
      0
      0
      Struts2获取request三种方法
       
      struts2里面有三种方法可以获取request,最好使用ServletRequestAware接口通过IOC机制注入Request对象。
      在Action中获取request方法一:
       
      在Action中的代码:
      Map request = (Map)ActionContext.getContext().get("request");
      List<Task> tasks = taskManager.findAll();
      request.put("tasks", tasks);
       
      在JSP页面中获取其中的值:
      <s:iterator id="task" value="#request.tasks">
             <tr class="table_header">
              <td><s:property value="#task.tname"/></td>
              <td><s:property value="#task.tuid"/></td>
              <td><s:property value="#task.tstartTime"/></td>
              <td><s:property value="#task.tendTime"/></td>
              <td><s:property value="#task.tstate"/></td>
              <td><input type="radio" id="choose" name="choose" onclick="getId(this.value)" value="<s:property value='#task.tid'/>"/></td> 
             </tr>
      </s:iterator>
      --------------------------------------------------------------------------------------------
      方法二:通过ServletActionContext类来获取,使用struts2经验如果处理get传参是中文,只能使用该方法进行处理乱码问题
       
      Action中代码:
      HttpServletRequest request = ServletActionContext.getRequest();
      request.setAttribute("username", "zhangsan");
       
      在jsp中获取其中的值
           <s:property value="#request.username">或者${requestScope.req}
      -------------------------------------------------------------------------------------------- 
      方法三:通过ServletRequestAware接口通过IOC机制注入Request对象
      Action中的代码:
      Action实现ServletRequestAware接口,实现接口中的方法
           private HttpServletRequest request;
           //实现接口中的方法
           public void setServletRequest(HttpServletRequest request){
            this.request = request;
           }
           //然后在execute()方法中就可以使用了
           public String execute(){
            request.setAttribute("username", "zhangsan");
            request.getSession().getServletContext().getApplication(); //得到Application
           }
           该方法必须要实现,而且该方法是自动被调用
           这个方法在被调用的过程中,会将创建好的request对象通过参数的方式传递给你,你可以用来赋给你本类中的变量,然后request就可以使用了
           注意:setServletRequest()方法一定会再execute()方法被调用前执行
       
      在jsp页面中获取其中的值
      <s:property value="#request.task.tname"/>
      /本篇文章来源于Java秀,原文出处:http://www.java.sh/article/jsp/1353.html


      已有 0人发表留言,猛击->> 这里<<-参与讨论


      ITeye推荐



      高效程序员的特征:聪明,懒惰

      $
      0
      0

      LazyCat这里我使用了 聪明懒惰程序员这几个词。我说的这几个词的意思是:

      • 程序员:有积极活力的,专注于用代码解决真实世界里的问题
        • 不是指那些梦想家,那些永远只想不做的人
      • 聪明:能够周全的思考问题(不是那些耍小聪明的人)
      • 懒惰:就像是程序中的 lazy-loading,是指延后写代码的时间(而不是无所事事的人)。

      正确的软件开发应该是懒惰式开发,也被称作忍耐式开发;这种开发方式的表现是,在真正动手写代码前,程序员要花大量的时间通盘考虑所有可能的解决方案和途径。这可以看作是 延缓写代码,在没有完全理解问题前绝不动手写代码。先把问题理解清楚,确保将要写的代码能真正的解决问题,这将会避免之后写出大量无用的代码。

      inspection这里说的 先把问题弄清楚,表现有:

      • 真正的理解需求,让产品部门(业务分析部门)弄清楚他们真正需求的是什么。
        • 这些部门通常不给足够的时间来整理需求
        • 他们经常不是请教问题领域专家,而是顺从领导的意见
        • 他们通常无法提供前后一致或完整的需求意见。
      • 清楚跟团队中的其它程序员或其他团队中的程序员需要那些交互,如何交互,这包括:
        • 使用白板交流
        • 画流程图(UML或Visio)

      你需要花大量的时间调研,来确保需求符合实情,来做工作让你和同事的交流有共同的语言语义。然而,程序员都喜欢立刻冲上去编程,喜欢在电脑前不停的敲代码。

      instructor-led-computer-training-sm在真正的软件开发中,只有5%的开发时间是有效率的(你可以参考《 程序员开发效率悖论》)。如果你发现一个程序员用100%的时间都在盯着屏幕,那么,你看到的这个程序员是最糟糕的程序员。

      如果一个程序员总是在电脑前编码,这绝对是一个不好的信号。

      高效的程序员总是不断检查他对需求的理解,确保他们的代码和需求是 同步的。高效的程序员是频繁的和产品经理/业务人员沟通交流,你可以经常看到他们使用白板与同事和架构师交流讨论。程序员的阅历和经验都是用来提高开发效率,这最优秀的程序员:

      • 他们思考代码的时间增加而写代码时间减少
      • 对问题的透彻理解使调试代码的速度更快
      • 深思熟虑后的代码速度更快
      • 代码长度更短

      程序员从心理上讲都是喜爱自己的代码的。

      Life-Experience烂程序员不喜欢去修改已经写成的烂代码。相比起优化自己的代码,他们更愿意简单的 增加更多的代码,以此来弥补之前的缺陷。更糟糕的是,他们喜欢把责任归咎于他人。最终,一堆不好用的代码上在来另外一堆不好用的代码,整个系统变得到处是bug,极不稳定。

      优秀的程序员经常也会写出烂代码,但他们能看到那些代码需要优化,哪些需要重写。优秀的程序员和不优秀的程序员的区别就在于对有问题的代码的态度,优秀的程序员的做法是:

      • 如果代码整体上好的,那就重构代码。
      • 如果代码整体上有问题,那就重新代码

      Untitled当代码中有需要优化或需要重写的地方时,时间拖的越久,你就越难回头解决这些问题。因为对这些代码依赖的程序会越来越多,越来越深,当你优化这些代码时,相关的依赖也需要进行相关修改。当积累的问题越来越多时,轻松的优化/重新这些代码已经变得不可能。而使用继续增加代码的方式来弥补之前代码问题,会让系统变得越来越不稳定。

      如果脑子里没想清楚,那就懒一些,把写代码的时间往后推。

      请阅读全文: 高效程序员的特征:聪明,懒惰

      本文由 外刊IT评论网( www.vaikan.com)原创发表
      文章地址: 高效程序员的特征:聪明,懒惰
      [英文原文: Productive Developers are Smart and Lazy ]

      你也许会喜欢这些文章:

      1. 管道工程序员
      2. 一个科技公司受人尊敬的品质
      3. 面向对象编程已死
      4. 大学里做不出好软件
      5. 所有的程序员都是自学成才




      Oracle数据库10个小问题

      $
      0
      0

      Oracle数据库10个小问题

       

      1、自增长字段

      很多人从别的数据转来使用Oracle时,会发现Oracle的数据类型里没有自增长字段类型,而像mysql,sqlserver,db2等主流数据库都有对应的自增长字段类型,Oracle的官方解决方案是采用sequence实现,sequence比较灵活,可以指定增长间隔等参数,虽然最终可以实现与其它数据库一样的效果,但是本来一个简单的数据类型可能解决,在Oracle却需要增加一个sequence,insert的时候还需要用sequence.nextval才行,这个地方感觉特不方便,为了兼容Oracle数据库很通用系统还不得不自己设计自增长字段解决方案,比如增加一张专用表来保存自增长字段的表和字段名,每次新增记录时都把这个记录值加1再取出使用。

      2、安装

      Oracle的安装是出名的复杂,特别是在非windows系统中,在网上可以找到遍地的Oracle安装文档,但依然有很多人花了大量时间才安装成功,如果要搭建rac系统,那就更复杂了。我们也看到Oracle公司也一直在降低安装的复杂度,11g的安装已经比8i,9i方便了一些。最让人费解的是Oracle非要搞一个java的安装界面,很多朋友都是因为java图形化界面的问题安装失败。我想Oracle是认为java的跨平台特性,选择了用java开发安装程序以统一界面,这样也不用开发多套软件,但是有没有发现我们在安装完后,服务器上使用Oracle数据库大部份管理都是命令行,几乎就没用到过图形化界面了。 Oracle安装其实可以做得更简单,操作系统变量及环境变量安装程序应该可以自动处理,软件包依赖检测可以人性化提示。在非windows系统上图形化是为了更方便安装,但是我感觉Oracle这图形化界面如果启动了我基本上也是点next,安装好了,库建好了再来修改相关参数。

      3、外连接

      外连接(left join ,right join,full join)在SQL标准语法中就有明确的定义,但是Oracle的语法确与标准完全不同,这个是历史原因了,没有左连接与右连接的概念,也不支持全外连接,Oracle语法如下所示:select * from t1,t2 where t1.id=t2.id(+)

      采用(+)来表示外连接,但是大部份人第一眼会认为这是一个右连接的语法,其实在Oracle中它相当于左连接,我直到现在还有时会搞错左右。好在Oracle9i中增加了标准外连接的语法支持。但是现在用Oracle数据库的人SQL大部份还是采用传统的语法,主要原因可能是传统语法已经习惯,传统语法更好用提示优化,SQL标准语法成熟度不够且还存在一些BUG等等。

      4、VARCHAR2类型

      看VARCHAR2这名字,还以为有新的VARCHAR类型,其实在Oracle中就这一种变长字符类型,为什么叫VARCHAR2我也不清楚,也许是历史原因吧,在Oracle也可以使用VARCHAR,但是VARCHAR只是VARCHAR2的一个同义词,而且在官方文档中还建议大家都用VARCHAR2,真是不明白了。

      VARCHAR2类型的最大长度到11g中还是4000字节,这个上限不知道为什么这么小,很多需求都会超过4000,只能使用lob来管理,但是Oracle的lob比较复杂,性能也不好,所以有些系统甚至在一个表建多个varcahr2(4000)的字段来表示一个字段的内容。而其它数据库varchar类型的最大长度都比4000大,有8K,32K,还有64K的。

      5、NUMBER类型

      在10g以前NUMBER类型是Oracle唯一数值类型,可以用来表示整数和小数,范围也足够我们使用,Oracle也支持INTEGER语法,但是INTEGER只是NUMBER的一个同义词,不明白Oracle为什么没有专用的整数类型。NUMBER类型很灵活,但是存储空间很大,性能也不好,如果用4字节可以表示的整数,用NUMBER平均需要6字节存储,如果采用number类型做指数或对数运算,与标准的浮点数性能可能会相差50倍。好在Oracle10g中增加了高效的浮点类型binary_float,binary_double,从而弥补了浮点数性能的问题。Oracle在pl/sql语法中有专用的整形pls_integer,但是pls_integer不能做为字段的类型。

      6、DATE类型

      Oracle主要有两种日期类型,date和timestamp,从词面看以为是date表示日期,timestamp可以存储时间,但是实际是date类型可以表示日期和时间,timestamp可以存储更高精度的时间,为什么date不叫datetime算了,Oracle没有只保存日期的类型,如果我们的数据仅要保存日期,那2-4个字节就可以搞定了,但Oracle的date类型需要7个字节存储。

      7、物化视图

      materialized view在Oracle官方文档中好像是翻译为实体化视图,物化视图是国内的通用叫法。一提起视图脑海里立即会认为这只是视图的一种类型,在数据库中也只是一些定义,但是实际上materialized view与普通视图关系不大,它更像是一个表,它是保存了实实在在的数据,并且可以与表一样定义存储参数,可以与表一样使用(select,insert,update,delete)。在其它数据库中也有和物化视图相似的解决方案,DB2叫物化查询表(materialized query table),sqlserver有索引视图,但是索引视图仅是起优化作用,与oracle的物化视图还不太一样。

      8、用户与SCHEMA

      schema的概念在很多数据库之间都没有统一的定位,有些数据库可以有多个database,每个database下面有多个schema,Oracle中的schema有点怪怪的,因为Oracle只有一个database的概念,所以schema就与其它数据库中的database有点类似,我们一般理解是schema(database)与用户没有直接关系,schema是逻辑概念,user是为了安全认证,只与权限有关,但Oracle用户与schema关系与其它数据库不一样,一个用户就对应一个schema,且不能更改,而且对schema的权限管理不方便,比如想让某个user可以访问另一个schema所有对像的权限就很麻烦。这种设计也是oracle特有的,估计Oracle也不会去改变。

      9、客户端

      装个客户端,给我个几百M大小的软件,有这么复杂吗,还有,为什么客户端安装好了还要做什么TNS配置,我直接提供数据库连接信息 (ip+port+dbname)不行吗。Oracle的TNS配置确实让开发人员头痛了很久,特别是那种要把C/S软件发布给客户端使用的场景,本来安装客户端就不方便,还要配置TNS,故障诊断时总是要检查一下TNS是否配置正确,不小心选错了TNS名连错了数据库导致误删除了数据的情况也时有发生。因为客户端软件的庞大,有些人还专门制做了只有几M或几十M的简易客户端安装软件,还有人开发了不需要客户端的插件,可以集成在软件里发布,可见Oracle的客户端安装配置不是一般人能接受的。好在现在很多应用都是B/S系统了,Oracle SQL DEVELOPER也不需要配置客户端,用jdbc连接Oracle仿佛世界清静了许多。

      10、管理及开发工具

      大部份数据库系统都会有一个比较官方全面的数据库管理工具,尤其是微软的产品,但是Oracle发展到现在自身的管理工具还是没有定型,8i用企业管理器,9i用JAVA重写了,10g用WEB重写了,现在11g官方推荐用Oracle SQL DEVELOPER做开发,用B/S架构的OEM做管理与监控。对于Oracle官方的开发管理工具,一直认为太不专业了,远没有其它第三方面工具好用,plsql developer和toad应该是Oracle管理及开发人员用得最多的工具,其次就是sqlplus了。Oracle SQL DEVELOPER采用JAVA开发,很强大,可以用JDBC连接管理任何数据库,但是还是不专业,使用起来不方便。10g的OEM有一些公司开始用了,它的监控功能应该还是不错的,但是不能做开发管理,这个是产品定位的问题了。期待有一天Oracle能有一个像SQL SERVER那个强大的开发及管理工具。

       

      以上说的是个人认为Oracle一些不爽的地方,从9i到11g也可以看出Oracle在增强功能以外,系统易用性,数据类型性能方面一直在改进,但是还有很多提高的空间。这些并不影响Oracle成为当前最先进的关系型数据库系统,它的并发处理机制,锁管理,数据字典,性能监控与统计,提供多种优化方法,在线管理,RAC高可用性架构等地方是其它数据库短时还无法超越的。



      已有 0人发表留言,猛击->> 这里<<-参与讨论


      ITeye推荐



      公开了:统治物联网的不为人熟知的开源操作系统

      $
      0
      0

      Contiki 统治着物联网

      英文原文: Out in the Open: The Little-Known Open Source OS That Rules the Internet of Things

      差不多所有东西都可以连接到计算机网络。灯泡、恒温器、咖啡机,甚至獾。没错,獾。

      獾大部分时间在地下,给生物学家和动物学家追踪它们的下落和活动增加了难度。比如,GPS 在地下或密闭区域运作不正常。但是大约五年前,牛津大学的研究人员 Andrew Markham 和 Niki Trigoni 发明了一种可以在地下运转的无线追踪系统从而 解决了这个问题。系统比较聪明,但是它们不能独自运行。和很多其他科学家一样,它们选择了开源,避免不得不白手起家重新建立基础组件。他们使用的一个构成要素就是称作 Contiki的开源操作系统。

      “Contiki 是真正的推动者,它允许我们快速构建原型,并轻松在不同的硬件平台之间切换,”Markham 说,他现在是牛津大学的副教授了。

      Contiki 没有和知名的 Windows 或 OS X、甚至 Linux 齐名,但是十多年里,对于建立传感器、追踪器和基于 web 的自动化系统等网络链接设备的黑客、高校科技人员和公司来说,它已经是很有技术含量的操作系统了。开发者喜欢它,因为它是轻量级、免费和成熟。它为急于给我们带来物联网所宣扬的、所有连接因特网配件的开发者和企业家门奠定了基础,不必开发那些配件未来需要的潜在操作系统。

      或许 Contiki 最大的问题就是它很小。真的不大。因为 Linux 需要 1M 的 RAM,而 Contiki 只需要数K个字节就能运行。它的发明者,Adam Dunkels,设法在不超过 30K 字节的空间里安装了一个完整的操作系统,包括一个图形用户接口、网络软件和一个 web 浏览器。这使得它轻松运行在小的、低电量的芯片上——就是用于连接设备的某种东东——但是它也可以安装到很多老式系统上,比如 Apple IIe 和 Commodore 64 上。

      Contiki 即将面对来自于诸如微软的竞争,后者最近公布了 用于物联网的 Windows。但是微软的新操作系统对尺寸小于 9 英寸的设备免费,它不是开源的。Contiki 已经有着 11 年的先发优势。

      Adom Dunkels:Contiki 操作系统的发明者

      Contiki 开始于 2003 年,它的起源可追溯到 Dunkels 还是瑞典梅拉达伦大学的一名计算机科学学生的时候。在 2000 年,他工作的一个项目是使用无线传感器追踪曲棍球球员的关键信号,并把它们呈现在观众可以看到的屏幕上。Dunkels 说,“我们说服他们把传感器放在鼻子上面,这样我们就能测量他们的呼吸频率”。

      为了使传感器正常运行,Dunkels 不得不编写软件,让它们与计算机网络交互。他把相应的代码叫做 LwIp,“轻量级因特网协议(light weight internet protocol)”。尽管 LwIP 今天仍然应用于很多微控制器和其他产品里,Dunkels 认为它还不够轻量。在 2003 年,他创立了 microIP,演化成了 Contiki。这个操作系统马上引起了研究人员和爱好者的注意,最近几年它还吸引了商业用户,包括 Rad-DX发射物检测设备和 Zolertia噪音监测系统。

      而 Nest, Google 在 1 月份花了 32 亿美金收购的这件网络连接恒温器公司,已经开始定义物联网了,Dunkels 指出,很多公司已经在应用上使用网络连接设备多年了,包括行业和楼宇自动化。Dunkels 说,“在 CES 上面,你可以看到所有的消费品,但是仍然有太多的不同方面”。

      不过消费者技术公司也开始拥抱 Contiki 了。比如, LiFX“小灯泡”正在使用这个操作系统,Nest 竞争对手 Tado也是。

      为了帮助支持 Contiki 的快速成长起来的商业应用,Dunkels 辞去了瑞典计算机科学院教授的职务,创立了 Thingsquare,致力于为 Contiki 设备提供基于云的后端的一家创业公司。其理念是让开发者更容易地把他们的硬件设备和智能手机、网络连接在一起。Thingsquare 管理服务器,提供借助网络管理设备的所有必需软件。

      本文链接

      物联网改变家居体验的10个案例

      $
      0
      0

        来自皮尤研究中心最新的数据显示,在不久的将来(2025年),物联网技术将无处不在,你很难再找到没有互联网连接性的设备,哪怕是一个最普通的水壶。即便是在今天,我们已经可以通过手机来操控电灯、空调甚至是汽车,物联网正在以多样化的形式侵入我们的生活。仍然觉得不够具体?没关系,下面我们就以家居环境为例,告诉你物联网技术带来的10个应用实例,相信你在看过之后便会感叹:原来我们还可以这样生活。

        1. 煮饭煮咖啡

      10个实例告诉你物联网如何改变家居体验

        为传统烤箱加入WIFI功能会有什么好处?你可以使用手机应用控制温度,包括预热和加温,更酷的是你还可以下载菜谱,实现更具针对性的烹饪方式。

        不仅仅是烤箱,一些高端咖啡机、调酒机也都配备了WIFI,厂商会不定期更新咖啡或是鸡尾酒菜单,让你在家就做出咖啡厅、酒吧的味道。

        2. 空调及温控

      10个实例告诉你物联网如何改变家居体验

        没有什么比在炎热的夏季进入凉爽的室内再惬意的事情了,但如果家中无人,如何实现自动温控?答案就是智能空调或是恒温器。比如Quirky与通用电气合作推出的Aros智能空调,不仅可以通过手机实现远程温控操作,甚至还能学习用户使用习惯,并够通过GPS定位用户位置实现完全自动的温控操作。

        如果不想更换空调,其实还有更简单的解决方案,比如Tado。这款温控器非常适合国内用户,因为它能够兼容包括海尔在内的主流品牌空调,只要将它连接到空调上,就可以方便地组建智能温控系统,通过手机控制每个房间的温度、定制个性化模式,同样也支持基于位置的全自动温控调节功能。

        3. 马桶

      10个实例告诉你物联网如何改变家居体验

        马桶也可以很智能?是的,除了通过内置接近传感器实现自动开关盖操作,TOTO还推出了内置智能分析仪的马桶,能够将排泄物的分析结果传输至手机应用中,让用户随时了解自己的健康状况。

        4. 灯光

      10个实例告诉你物联网如何改变家居体验

        智能灯泡也是一种非常直观、入门的物联网家居体验,任何用户都可以轻松尝试。目前,智能灯泡品牌逐渐增多,其中包括飞利浦、LG这些大家耳熟能详的大品牌,我们可以通过手机应用实现开关灯、调节颜色和亮度等操作,甚至还可以实现灯光随音乐闪动的效果,把房间变成炫酷的舞池。

        5. 插座

      10个实例告诉你物联网如何改变家居体验

        插座可以说是一切家用电器获得电力的基础接口,如果它具备了连接互联网的能力,自然其他电器也同样可以实现。目前市场中的智能插座品牌日益丰富,知名产品如贝尔金、Plum、D-Link等等,它们不仅可以实现手机遥控开关电灯、电扇、空调等家电,还能够监测设备用电量,生成图表帮助你更好地节约能源及开支。

        6. 音响系统

      10个实例告诉你物联网如何改变家居体验

        相信很多朋友都听过Sonos的大名,这个无线音响品牌的产品均采用WIFI无线连接,能够接入家庭无线局域网中,让用户通过移动设备来控制音乐播放。相比蓝牙,WIFI传输信号更广泛且稳定,同时还能够实现每个音箱播放独立的音乐、与智能灯泡等设备联动等功能,显然要比蓝牙音箱更适合家居环境使用。另外,包括三星、索尼等厂商也纷纷进入无线音响领域,让用户拥有更多选择。

        7. 运动监测

      10个实例告诉你物联网如何改变家居体验

        科技为我们带来了全新的运动、健身方式,你可能已经使用运动手环或是智能手表来监测每天的运动量。不仅如此,在家中放置一台新型的智能体重秤,可以获得更全面的运动监测效果。类似Withings的产品,内置了先进的传感器,可以监测血压、脂肪量甚至是空气质量,通过应用程序为用户提供健康建议,另外还可以与其他品牌的运动手环互联,实现更精准、更加无缝化的个人健康监测。

        8. 个人护理

      10个实例告诉你物联网如何改变家居体验

        不仅仅是运动、健身监测,物联网技术也已经辐射到个人健康护理领域。包括欧乐B、Beam toothbrush都推出了智能牙刷,牙刷本身通过蓝牙4.0与智能手机连接,可以实现刷牙时间、位置提醒,也可根据用户刷牙的数据生成分析图表,估算出口腔健康情况。

        另外,类似GoBe血糖分析仪等家用自检设备,也在近期获得了实质性的进展,未来有望形成庞大的市场,届时老人、病患就可以足不出户,通过这些设备实现基础的自我护理及保健应用。

        9. 养花花草草

      10个实例告诉你物联网如何改变家居体验

        很多朋友都喜欢在家中养养花花草草,但经常会疏于照料,导致花草凋零,其实通过物联网技术也能够改善这种情况。比如“小树杈”造型的Flower Power,只要将它插在土壤中,就可以检测植物的湿度、光照、施肥量甚至是空气状况,如果植物需要什么,就能够通过手机通知提醒用户,保证植物茁壮成长。

        如果你拥有一大大院子,还那么可以考虑“Droplet”智能洒水器,它能够分析土壤含水量、温度等多种数据,计算出最佳的浇水量,智能地灌溉花园中的每一株花草。

        10. 家庭安全

      10个实例告诉你物联网如何改变家居体验

        物联网的另一大优势就是将原本“高大上”的企业级应用带入到家庭中,比如安全监控系统。现在,只要你选择几只Dropcam、三星等品牌的家庭监控摄像头,就可以组成完整的家庭监控系统,不论你的房子有多大。这些摄像头通常具有广角镜头,可拍摄720P或1080P视频,并内置了移动传感器、夜视仪等先进功能,用户可以在任何地方通过手机应用查看室内的实时状态。

        除了监控摄像头,窗户传感器、智能门铃(内置摄像头)、烟雾监测器,都是可以选择的家庭安全设备,与监控摄像头配合,可以把你的家武装到牙齿,任何坏人都无法轻易构成威胁。

        总结

        显然,物联网技术真正让科技走进我们的生活,尤其是家庭生活,以前看似繁琐的种种,都能够通过这些物联网家居设备变得轻松、惬意。我们也相信,未来的家庭生活会因物联网变得更加美好。

      android开发异常信息收集程序代码

      $
      0
      0

      首先创建全局的Application ,此Application全局通用。

      package com.demo.utils;
      
      import com.demo.exception.CrashHandler;
      import android.app.Application;
      /**
       * 全局的context,任意位置调用
       * @author Administrator
       *
       */
      public class GlobalApplication extends Application
      {
          private static GlobalApplication instance;
      
          public static GlobalApplication getInstance()
          {
              return instance;
          }
      
          @Override
          public void onCreate()
          {
              super.onCreate();
              instance = this;
              CrashHandler crashHandler = CrashHandler.getInstance();//这是收集异常信息的单例类,具体代码请看下文
              crashHandler.init(getApplicationContext());//初始化
          }
      }
      

      注意:上面的代码需要在注册清单文件中注册。部分清单如下:

      <?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.demo.test"
          android:versionCode="1"
          android:versionName="1.0" ><uses-sdk
              android:minSdkVersion="8"
              android:targetSdkVersion="18" /><!-- 网络 --><uses-permission android:name="android.permission.INTERNET" /><!-- 访问sdcard --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 获取mac需要的网络状态 --><uses-permission android:name="andorid.permission.CHANGE_CONFIGURATION" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><application
              android:name="com.demo.utils.GlobalApplication"
              android:allowBackup="true"
              android:icon="@drawable/app_icon"
              android:label="@string/app_name"
              android:theme="@style/AppTheme" ><activity
                  android:name="com.demo.activity.MyActivity"
                  android:launchMode="singleTask"
                  android:screenOrientation="landscape"
                  android:theme="@android:style/Theme.NoTitleBar" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></activity></application></manifest>

      crashHandler

      package com.demo.exception;
      
      import java.io.FileOutputStream;
      import java.io.PrintWriter;
      import java.io.StringWriter;
      import java.io.Writer;
      import java.lang.Thread.UncaughtExceptionHandler;
      import java.lang.reflect.Field;
      import java.text.DateFormat;
      import java.text.SimpleDateFormat;
      import java.util.Date;
      import java.util.HashMap;
      import java.util.Map;
      
      import android.content.Context;
      import android.content.pm.PackageInfo;
      import android.content.pm.PackageManager;
      import android.content.pm.PackageManager.NameNotFoundException;
      import android.os.Build;
      import android.os.Looper;
      
      import com.demo.utils.ConstantUtils;
      import com.demo.utils.PathUtils;
      import com.demo.utils.log.MyLog;
      
      /**
       * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
       * 
       * @author user
       * 实现
      UncaughtExceptionHandler接口
       */
      public class CrashHandler implements UncaughtExceptionHandler
      {
          public static final String TAG = "bug";
      
          //系统默认的UncaughtException处理类 
          private Thread.UncaughtExceptionHandler mDefaultHandler;
          //CrashHandler实例
          private static CrashHandler INSTANCE = new CrashHandler();
          //程序的Context对象
          private Context mContext;
          //用来存储设备信息和异常信息
          private Map<String, String> infos = new HashMap<String, String>();
      
          //用于格式化日期,作为日志文件名的一部分
          private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
      
          /** 保证只有一个CrashHandler实例 */
          private CrashHandler()
          {
          }
      
          /** 获取CrashHandler实例 ,单例模式 */
          public static CrashHandler getInstance()
          {
              return INSTANCE;
          }
      
          /**
           * 初始化
           * 
           * @param context
          */
          public void init(Context context)
          {
              mContext = context;
              //获取系统默认的UncaughtException处理器
              mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
              //设置该CrashHandler为程序的默认处理器
              Thread.setDefaultUncaughtExceptionHandler(this);
          }
      
          /**
           * 当UncaughtException发生时会转入该函数来处理
          */
          @Override
          public void uncaughtException(Thread thread, Throwable ex)
          {
              if (!handleException(ex) && mDefaultHandler != null)
              {
                  //如果用户没有处理则让系统默认的异常处理器来处理
                  mDefaultHandler.uncaughtException(thread, ex);
              }
              else
              {
                  try
                  {
                      Thread.sleep(3000);
                  }
                  catch (InterruptedException e)
                  {
                      MyLog.e(TAG, "error : ", e);
                  }
      
                  mDefaultHandler.uncaughtException(thread, ex);
      
                  //退出程序
                  //            android.os.Process.killProcess(android.os.Process.myPid());
                  //            System.exit(1);
              }
          }
      
          /**
           * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
           * 
           * @param ex
           * @return true:如果处理了该异常信息;否则返回false.
          */
          private boolean handleException(Throwable ex)
          {
              if (ex == null)
              {
                  return false;
              }
              //使用Toast来显示异常信息
              new Thread()
              {
                  @Override
                  public void run()
                  {
                      Looper.prepare();
                      //                Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
                      Looper.loop();
                  }
              }.start();
              //收集设备参数信息 
              collectDeviceInfo(mContext);
              //保存日志文件 
              MyLog.i(TAG, "ex:" + ex.toString() + "--" + ex.getLocalizedMessage());
              saveCrashInfo2File(ex);
              return true;
          }
      
          /**
           * 收集设备参数信息
           * @param ctx
          */
          public void collectDeviceInfo(Context ctx)
          {
              try
              {
                  PackageManager pm = ctx.getPackageManager();
                  PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
                  if (pi != null)
                  {
                      String versionName = pi.versionName == null ? "null" : pi.versionName;
                      String versionCode = pi.versionCode + "";
                      infos.put("versionName", versionName);
                      infos.put("versionCode", versionCode);
                  }
              }
              catch (NameNotFoundException e)
              {
                  MyLog.e(TAG, "an error occured when collect package info", e);
              }
              Field[] fields = Build.class.getDeclaredFields();
              for (Field field : fields)
              {
                  try
                  {
                      field.setAccessible(true);
                  }
                  catch (Exception e)
                  {
                      MyLog.e(TAG, "an error occured when collect crash info", e);
                  }
              }
          }
      
          /**
           * 保存错误信息到文件中
           * 
           * @param ex
           * @return    返回文件名称,便于将文件传送到服务器
          */
          private String saveCrashInfo2File(Throwable ex)
          {
      
              StringBuffer sb = new StringBuffer();
              //        for (Map.Entry<String, String> entry : infos.entrySet())
              //        {
              //            String key = entry.getKey();
              //            String value = entry.getValue();
              //            sb.append(key + "=" + value + "\n");
              //        }
      
              Writer writer = new StringWriter();
              PrintWriter printWriter = new PrintWriter(writer);
              ex.printStackTrace(printWriter);
              Throwable cause = ex.getCause();
              while (cause != null)
              {
                  MyLog.i(TAG, "cause:" + cause.toString() + "--");
                  cause.printStackTrace(printWriter);
                  cause = cause.getCause();
              }
              printWriter.close();
              String result = writer.toString();
              MyLog.i(TAG, "result:" + result);
              sb.append(result);
              try
              {
                  long timestamp = System.currentTimeMillis();
                  String time = formatter.format(new Date());
                  String fileName = "crash-" + time + "-" + timestamp + ".log";
                  if (ConstantUtils.isOnline)
                  {
                      fileName = "crash-online.log";
                  }
                  String path = PathUtils.BUGPATH;
                  FileOutputStream fos = new FileOutputStream(path + fileName);
                  fos.write(sb.toString().getBytes());
                  fos.close();
                  return fileName;
              }
              catch (Exception e)
              {
                  MyLog.e(TAG, "an error occured while writing file...", e);
              }
              return null;
          }
      }
      上面信息涉及到下面的几个类ConstantUtils,PathUtils和MyLog

      constantUtils

      public class ConstantUtils
      {
      
          /***是否上线版本***/
          public final static boolean isOnline = false;
          /***
           * 时间格式
           */
          public static String timeFormat = "yyyy-MM-dd HH:mm:ss";
      }

      MyLog
      package com.demo.utils.log;
      
      import java.text.SimpleDateFormat;
      import java.util.Date;
      
      import android.util.Log;
      
      import com.demo.utils.ConstantUtils;
      import com.demo.utils.PathUtils;
      
      public class MyLog
      {
          private static String filename = "log.txt";
      
          /***
           * 打印日志
           * @param tag
           * @param msg
           */
          public static void d(String tag, String msg)
          {
              if (!ConstantUtils.isOnline)
              {
                  Log.d(tag, msg);
              }
          }
      
          /***
           * 打印日志
           * @param tag
           * @param msg
           */
          public static void i(String tag, String msg)
          {
              if (!ConstantUtils.isOnline)
              {
                  Log.i(tag, msg);
              }
          }
      
          /***
           * 打印日志
           * @param tag
           * @param msg
           */
          public static void e(String tag, String msg)
          {
              if (!ConstantUtils.isOnline)
              {
                  Log.e(tag, msg);
              }
          }
      
          /***
           * 打印日志
           * @param tag
           * @param msg
           */
          public static void e(String tag, String msg, Throwable tr)
          {
              if (!ConstantUtils.isOnline)
              {
                  Log.e(tag, msg, tr);
              }
          }
      
          /***
           * 打印日志
           * @param tag
           * @param msg
           */
          public static void w(String tag, String msg)
          {
              if (!ConstantUtils.isOnline)
              {
                  Log.w(tag, msg);
              }
          }
      
          /***
           * 写入文本到日志文件中
           * @param value
           */
          public static void write(String value)
          {
              if (!ConstantUtils.isOnline)
              {
                  String newValue = getDataFormat(System.currentTimeMillis()) + "  " + value;
                  LogUtils.getInstances().write(PathUtils.LOGPATH, filename, newValue);
              }
          }
      
          /***
           * 获取当前时间
           * @param timeInMillis
           * @return
           */
          private static String getDataFormat(long timeInMillis)
          {
              SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
              return dataFormat.format(new Date(timeInMillis));
          }
      
      }
      

      pathutils

      package com.demo.utils;
      
      import java.io.File;
      
      import android.os.Environment;
      
      public class PathUtils
      {
          public final static String sdcardPath = Environment.getExternalStorageDirectory() + "";
          public final static String PATH = sdcardPath + "/exception/" + AppUtils.getAppPackageName();
          public final static String BUGPATH = PATH + "/bug/";
          public final static String LOGPATH = PATH + "/log/";
      
          public PathUtils()
          {
              File PATH = new File(PathUtils.PATH);
              if (!PATH.exists())
              {
                  PATH.mkdirs();
              }
              File LOGPATH = new File(PathUtils.LOGPATH);
              if (!LOGPATH.exists())
              {
                  LOGPATH.mkdirs();
              }
              File BUGPATH = new File(PathUtils.BUGPATH);
              if (!BUGPATH.exists())
              {
                  BUGPATH.mkdirs();
              }
          }
      }
      



      作者:u012083681 发表于2014-6-4 18:14:37 原文链接
      阅读:79 评论:0 查看评论

      _00013 一致性哈希算法 Consistent Hashing 探讨以及相应的新问题出现解决

      $
      0
      0

      一、业务场景

      假如我们现在有12台Redis服务器(其它的什么东西也行),有很多User(用户)的数据数据从前端过来,然后往12台redis服务器上存储,在存储中就会出现一个问题,12台服务器,有可能其中几台Redis服务器上(简称集群A)存了很多的数据,然后另外几台Redis服务器(简称集群B)上存的数据很少,这样的话那 A 上的读写压力就会很大(当然,这个要看你的数据量的大小了,如果你数据量很小的话,基本无压力了,但是数据量很大,那就 、、、),对于这样的问题,我们通常的解决办法是什么呢 ?一般通常会想到的就是哈希取余了吧!也就是 Hash(userid)% N (N=12),这样的话也能适当的减小很多压力了,但是这样的话又会产生一些新的问题,增加节点跟减少节点 :

      1、减少节点:

      假如有一台Redis服务器挂掉了,那么是否这样 Hash(userid)% N (N=12) 哈希取余到该Redis的数据会全部丢失呢 ?如果不要该节点,那么只剩下11台Redis服务器了,原来的映射关系 Hash(userid)% N (N=12) 变成了 Hash(userid)% (N-1) (N=12),重点是数据丢失如何找回 ?

      2、增加节点:

      假如有一天想增加Redis服务器了,这时候麻烦来了,这时候需要将原来的映射关系 Hash(userid)% N (N=12) 变成 Hash(userid)% (N+1) (N=12) 了,跟 减少节点一样,这岂不是以前所有的映射全部失效了 ?这不是坑爹么!!!数据混乱,后台可能瞬间垮掉,那样的代价很大 、、、

      3、硬件越来越便宜了,公司越来越大了,然后服务器越来越多了:

      哈希取余 Hash(userid)% N (N=12) 就也完全不能满足需求了,不能修改映射关系,修改之后灰常麻烦 。

      这时候该怎么办呢 ?有什么办法可以改变这个状况呢,当然就是一致性哈希 Consistent Hashing了 ......

      自己懒得写原理了,下面引用了 http://blog.csdn.net/sparkliang/article/details/5279393,详细介绍了一致性哈希,灰常好 、、、

      二、 hash 算法和单调性

         Hash 算法的一个衡量指标是单调性( Monotonicity ),定义如下:

        单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

      容易看到,上面的简单 hash 算法 hash(object)%N 难以满足单调性要求。


      三、Consistent hashing 算法的原理

      Consistent Hashing 是一种 hash 算法,简单的说,在移除 / 添加一个 cache 时,它能够尽可能小的改变已存在 key 映射关系,尽可能的满足单调性的要求。

      下面就来按照 5 个步骤简单讲讲 consistent hashing 算法的基本原理。


      四、环形hash 空间

      考虑通常的 hash 算法都是将 value 映射到一个 32 为的 key 值,也即是 0~2^32-1 次方的数值空间;我们可以将这个空间想象成一个首( 0 )尾( 2^32-1 )相接的圆环,如下面图 1 所示的那样。

      circle space

      图 1 环形 hash 空间

      五、把对象映射到hash 空间

      接下来考虑 4 个对象 object1~object4 ,通过 hash 函数计算出的 hash 值 key 在环上的分布如图 2 所示。

      hash(object1) = key1;

      … …

      hash(object4) = key4;

      object

      图 2 4 个对象的 key 值分布

      六、 把cache 映射到hash 空间

      Consistent Hashing 的基本思想就是将对象和 cache 都映射到同一个 hash 数值空间中,并且使用相同的 hash算法。

      假设当前有 A,B 和 C 共 3 台 cache ,那么其映射结果将如图 3 所示,他们在 hash 空间中,以对应的 hash 值排列。

      hash(cache A) = key A;

      … …

      hash(cache C) = key C;

      cache

      图 3 cache 和对象的 key 值分布

       

      说到这里,顺便提一下 cache 的 hash 计算,一般的方法可以使用 cache 机器的 IP 地址或者机器名作为 hash输入。


      七、把对象映射到cache

      现在 cache 和对象都已经通过同一个 hash 算法映射到 hash 数值空间中了,接下来要考虑的就是如何将对象映射到 cache 上面了。

      在这个环形空间中,如果沿着顺时针方向从对象的 key 值出发,直到遇见一个 cache ,那么就将该对象存储在这个 cache 上,因为对象和 cache 的 hash 值是固定的,因此这个 cache 必然是唯一和确定的。这样不就找到了对象和 cache 的映射方法了吗?!

      依然继续上面的例子(参见图 3 ),那么根据上面的方法,对象 object1 将被存储到 cache A 上; object2 和object3 对应到 cache C ; object4 对应到 cache B ;


      八、 考察cache 的变动

      前面讲过,通过 hash 然后求余的方法带来的最大问题就在于不能满足单调性,当 cache 有所变动时, cache会失效,进而对后台服务器造成巨大的冲击,现在就来分析分析 consistent hashing 算法。


      九、移除 cache

      考虑假设 cache B 挂掉了,根据上面讲到的映射方法,这时受影响的将仅是那些沿 cache B 逆时针遍历直到下一个 cache ( cache C )之间的对象,也即是本来映射到 cache B 上的那些对象。

      因此这里仅需要变动对象 object4 ,将其重新映射到 cache C 上即可;参见图 4 。

      remove

      图 4 Cache B 被移除后的 cache 映射

      十、添加 cache

      再考虑添加一台新的 cache D 的情况,假设在这个环形 hash 空间中, cache D 被映射在对象 object2 和object3 之间。这时受影响的将仅是那些沿 cache D 逆时针遍历直到下一个 cache ( cache B )之间的对象(它们是也本来映射到 cache C 上对象的一部分),将这些对象重新映射到 cache D 上即可。

       

      因此这里仅需要变动对象 object2 ,将其重新映射到 cache D 上;参见图 5 。

      add

      图 5 添加 cache D 后的映射关系

      十一、虚拟节点

      考量 Hash 算法的另一个指标是平衡性 (Balance) ,定义如下:

      平衡性

         平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。

      hash 算法并不是保证绝对的平衡,如果 cache 较少的话,对象并不能被均匀的映射到 cache 上,比如在上面的例子中,仅部署 cache A 和 cache C 的情况下,在 4 个对象中, cache A 仅存储了 object1 ,而 cache C 则存储了object2 、 object3 和 object4 ;分布是很不均衡的。

      为了解决这种情况, consistent hashing 引入了“虚拟节点”的概念,它可以如下定义:

      “虚拟节点”( virtual node )是实际节点在 hash 空间的复制品( replica ),一实际个节点对应了若干个“虚拟节点”,这个对应个数也成为“复制个数”,“虚拟节点”在 hash 空间中以 hash 值排列。

      仍以仅部署 cache A 和 cache C 的情况为例,在图 4 中我们已经看到, cache 分布并不均匀。现在我们引入虚拟节点,并设置“复制个数”为 2 ,这就意味着一共会存在 4 个“虚拟节点”, cache A1, cache A2 代表了cache A ; cache C1, cache C2 代表了 cache C ;假设一种比较理想的情况,参见图 6 。

      virtual nodes

      图 6 引入“虚拟节点”后的映射关系

       

      此时,对象到“虚拟节点”的映射关系为:

      objec1->cache A2 ; objec2->cache A1 ; objec3->cache C1 ; objec4->cache C2 ;

      因此对象 object1 和 object2 都被映射到了 cache A 上,而 object3 和 object4 映射到了 cache C 上;平衡性有了很大提高。

      引入“虚拟节点”后,映射关系就从 { 对象 -> 节点 } 转换到了 { 对象 -> 虚拟节点 } 。查询物体所在 cache 时的映射关系如图 7 所示。

      map

      图 7 查询对象所在 cache

       

      “虚拟节点”的 hash 计算可以采用对应节点的 IP 地址加数字后缀的方式。例如假设 cache A 的 IP 地址为202.168.14.241 。

      引入“虚拟节点”前,计算 cache A 的 hash 值:

      Hash(“202.168.14.241”);

      引入“虚拟节点”后,计算“虚拟节”点 cache A1 和 cache A2 的 hash 值:

      Hash(“202.168.14.241#1”);  // cache A1

      Hash(“202.168.14.241#2”);  // cache A2

      引用了这位大神的小结

      Consistent hashing 的基本原理就是这些,具体的分布性等理论分析应该是很复杂的,不过一般也用不到。

      http://weblogs.java.net/blog/2007/11/27/consistent-hashing 上面有一个 java 版本的例子,可以参考。

      http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx 转载了一个 PHP 版的实现代码。

      http://www.codeproject.com/KB/recipes/lib-conhash.aspx C语言版本


      先上代码吧,等下说一下一致性哈希中的一些问题

      1、未重构的代码

      import java.util.Collection;
      import java.util.SortedMap;
      import java.util.TreeMap;
      
      public class ConsistentHash<T> {
      
       private final HashFunction hashFunction;
       private final int numberOfReplicas;
       private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();
      
       public ConsistentHash(HashFunction hashFunction, int numberOfReplicas,
           Collection<T> nodes) {
         this.hashFunction = hashFunction;
         this.numberOfReplicas = numberOfReplicas;
      
         for (T node : nodes) {
           add(node);
         }
       }
      
       public void add(T node) {
         for (int i = 0; i < numberOfReplicas; i++) {
           circle.put(hashFunction.hash(node.toString() + i), node);
         }
       }
      
       public void remove(T node) {
         for (int i = 0; i < numberOfReplicas; i++) {
           circle.remove(hashFunction.hash(node.toString() + i));
         }
       }
      
       public T get(Object key) {
         if (circle.isEmpty()) {
           return null;
         }
         int hash = hashFunction.hash(key);
         if (!circle.containsKey(hash)) {
           SortedMap<Integer, T> tailMap = circle.tailMap(hash);
           hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
         }
         return circle.get(hash);
       }
      
      }

      2、重构后的代码,将哈希算法跟虚拟节点的方法给抽取出来,模版方法设计模式

      2.1 AbstractConsistentHash

      package cn.yting.conhash;
      
      import java.util.Collection;
      import java.util.SortedMap;
      import java.util.TreeMap;
      
      /**
       * 将Hash算法跟虚拟节点的虚拟方式给抽出来,模版方法设计模式
       * 
       * @Author 妳那伊抹微笑
       *
       */
      public abstract class AbstractConsistentHash {
      
      	/**
      	 * 复制节点,增加每个节点的复制节点有利于负载均衡
      	 */
      	protected int numberOfReplicas = 8;
      	
      	/**
      	 * 一致性哈希
      	 */
      	protected SortedMap<Long, String> circle = new TreeMap<Long, String>();
      
      	/**
      	 * 自定义一致性哈希算法
      	 */
      	protected abstract long defineHash(String key);
      
      	/**
      	 * 自定义circle key
      	 */
      	protected abstract String defineCircleKey(String node, int i);
      
      	public AbstractConsistentHash() {
      	}
      
      	public AbstractConsistentHash(int numberOfReplicas, Collection<String> nodes) {
      		this.numberOfReplicas = numberOfReplicas;
      		for (String node : nodes) {
      			addNode(node);
      		}
      	}
      
      	/**
      	 * 添加节点
      	 */
      	public void addNode(String node) {
      		for (int i = 0; i < this.numberOfReplicas; i++) {
      			/*
      			 * 计算key的哈希算法
      			 */
      			long key = defineHash(defineCircleKey(node, i));
      
      			circle.put(key, node);
      		}
      	}
      
      	/**
      	 * 删除节点
      	 */
      	public void removeNode(String node) {
      		for (int i = 0; i < this.numberOfReplicas; i++) {
      			/*
      			 * 计算key的哈希算法
      			 */
      			long key = defineHash(defineCircleKey(node, i));
      			
      			circle.remove(key);
      		}
      	}
      
      	/**
      	 * 查找节点,取得顺时针方向上最近的一个虚拟节点对应的实际节点
      	 */
      	public String getNode(String node) {
      		if (circle.isEmpty()) {
      			return null;
      		}
      		
      		/*
      		 * 计算key的哈希算法
      		 */
      		long key = defineHash(node);
      
      		if (!circle.containsKey(key)) {
      			SortedMap<Long, String> tailMap = circle.tailMap(key);
      			key = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
      		}
      		return circle.get(key);
      	}
      }
      2.2 hashCode 一致性哈希实现方式

      package cn.yting.conhash;
      
      import java.util.Set;
      
      /**
       * HashCode 一致性哈希,使用自带的hashCode计算hash值
       * 
       * @Author 妳那伊抹微笑
       *
       */
      public class HashCodeConsistentHash extends AbstractConsistentHash {
      
      	public HashCodeConsistentHash(int numberOfReplicas, Set<String> nodes) {
      		super(numberOfReplicas, nodes);
      	}
      
      	@Override
      	protected long defineHash(String key) {
      		return key.hashCode();
      	}
      	
      	@Override
      	protected String defineCircleKey(String node, int i) {
      		if(i > 99){
      			return node + "-" + i;
      		}else if(i > 9){
      			return node + "-0" + i;
      		}else{
      			return node + "-00" + i;
      		}
      	}
      }

      2.3 CRC32 一致性哈希实现方式

      package cn.yting.conhash;
      
      import java.util.Set;
      import java.util.zip.CRC32;
      
      /**
       * HashCode 一致性哈希,使用CRC32算法计算hash值
       * 
       * @Author 妳那伊抹微笑
       *
       */
      public class CRC32ConsistentHash extends AbstractConsistentHash {
      
      	public CRC32ConsistentHash(int numberOfReplicas, Set<String> nodes) {
      		super(numberOfReplicas, nodes);
      	}
      
      	@Override
      	protected long defineHash(String key) {
      		CRC32 hash = new CRC32();
      		hash.update(key.getBytes());
      		return (int)hash.getValue();
      	}
      	
      	@Override
      	protected String defineCircleKey(String node, int i) {
      		return (node+i);
      	}
      	
      }
      

      2.4  MD5 一致性哈希 实现方式

      package cn.yting.conhash;
      
      import java.nio.charset.Charset;
      import java.security.MessageDigest;
      import java.util.Set;
      
      /**
       * HashCode 一致性哈希,使用自MD5算法计算hash值
       * 
       * @Author 妳那伊抹微笑
       *
       */
      public class MD5ConsistentHash extends AbstractConsistentHash{
      
      	public MD5ConsistentHash(int numberOfReplicas, Set<String> nodes) {
      		super(numberOfReplicas, nodes);
      	}
      
      	@Override
      	protected String defineCircleKey(String node, int i) {
      		if(i > 99){
      			return node + "-" + i;
      		}else if(i > 9){
      			return node + "-0" + i;
      		}else{
      			return node + "-00" + i;
      		}
      	}
      
      	@Override
      	protected long defineHash(String key) {
      		try {
      			MessageDigest md5 = MessageDigest.getInstance("MD5");
      			md5.update(key.getBytes(Charset.forName("UTF8")));
      			byte[] digest = md5.digest();
      
      			long hash = 0;
      			for (int i = 0; i < 4; i++) {
      				hash += ((long) (digest[i * 4 + 3] & 0xFF) << 24)
      						| ((long) (digest[i * 4 + 2] & 0xFF) << 16)
      						| ((long) (digest[i * 4 + 1] & 0xFF) << 8)
      						| ((long) (digest[i * 4 + 0] & 0xFF));
      			}
      			System.out.println(key+ "--->" + hash);
      			return hash;
      		} catch (Exception ex) {
      			return -1;
      		}
      	}
      
      }
      

      # 完整案例(CRC32实现方式)这里是网Redis集群中写入数据,HashMap类型的数据

      package cn.yting.conhash;
      
      
      import java.io.BufferedReader;
      import java.io.InputStreamReader;
      import java.util.HashMap;
      import java.util.LinkedHashMap;
      import java.util.Map;
      
      
      import org.apache.commons.logging.Log;
      import org.apache.commons.logging.LogFactory;
      
      
      
      
      import redis.clients.jedis.Jedis;
      import redis.clients.jedis.JedisPool;
      import redis.clients.jedis.JedisPoolConfig;
      
      
      /**
       * 一致性哈希Utils, 实现了redis中hash的一般操作
       * 
       * @Author 妳那伊抹微笑
       *
       */
      public class ConsistentHashRedisUtils {
      <span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * redis server properties 配置文件的路径
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private static final String REDIS_SERVERS_HANGZHOU_PROPERTIES = "../../conf/analysis-redis-servers-hangzhou.properties";<span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 日志对象
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private Log log = LogFactory.getLog(this.getClass());<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 一致性哈希
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private AbstractConsistentHash hash;<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * Redis连接池
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private Map<String, JedisPool> jedisPools = new HashMap<String, JedisPool>();;<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 外网地址到内网地IP映射
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private Map<String, String> redisNodeMaps = new LinkedHashMap<String, String>();<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 是否转换到内网地址创建jedis对象
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private boolean isConvertIp = false;<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 虚拟节点个数
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private static int numberOfReplicas = 8;<span style="white-space:pre">	</span>public ConsistentHashRedisUtils() {<span style="white-space:pre">		</span>this(numberOfReplicas);<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span><span style="white-space:pre">	</span>public ConsistentHashRedisUtils(int numberOfReplicas) {<span style="white-space:pre">		</span>redisNodeMaps = getRedisConfNodeMaps();<span style="white-space:pre">		</span>hash = new CRC32ConsistentHash(numberOfReplicas, redisNodeMaps.keySet());<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * hset<span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @param key<span style="white-space:pre">	</span> * @param field<span style="white-space:pre">	</span> * @param value<span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>public void hset(String key, String field, String value) {<span style="white-space:pre">		</span>JedisPool pool = null;<span style="white-space:pre">		</span>Jedis jedis = null;<span style="white-space:pre">		</span>try {<span style="white-space:pre">			</span>pool = getJedisPool(key);<span style="white-space:pre">			</span>jedis = pool.getResource();<span style="white-space:pre">			</span>jedis.hset(key, field, value);<span style="white-space:pre">		</span>} catch (Exception e) {<span style="white-space:pre">			</span>log.error("Redis hset exception : " + key, e);<span style="white-space:pre">			</span>pool.returnBrokenResource(jedis);<span style="white-space:pre">		</span>} finally {<span style="white-space:pre">			</span>pool.returnResource(jedis);<span style="white-space:pre">		</span>}<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * hincrBy<span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @param key<span style="white-space:pre">	</span> * @param field<span style="white-space:pre">	</span> * @param value<span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>public void hincrBy(String key, String field, long value) {<span style="white-space:pre">		</span>JedisPool pool = null;<span style="white-space:pre">		</span>Jedis jedis = null;<span style="white-space:pre">		</span>try {<span style="white-space:pre">			</span>pool = getJedisPool(key);<span style="white-space:pre">			</span>jedis = pool.getResource();<span style="white-space:pre">			</span>jedis.hincrBy(key, field, value);<span style="white-space:pre">		</span>} catch (Exception e) {<span style="white-space:pre">			</span>log.error("Redis hincrBy exception : " + key, e);<span style="white-space:pre">			</span>pool.returnBrokenResource(jedis);<span style="white-space:pre">		</span>} finally {<span style="white-space:pre">			</span>pool.returnResource(jedis);<span style="white-space:pre">		</span>}<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * hget<span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @param key<span style="white-space:pre">	</span> * @param field<span style="white-space:pre">	</span> * @return 
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>public String hget(String key, String field) {<span style="white-space:pre">		</span>JedisPool pool = null;<span style="white-space:pre">		</span>Jedis jedis = null;<span style="white-space:pre">		</span>String result = null;<span style="white-space:pre">		</span><span style="white-space:pre">		</span>try {<span style="white-space:pre">			</span>pool = getJedisPool(key);<span style="white-space:pre">			</span>jedis = pool.getResource();<span style="white-space:pre">			</span>result = jedis.hget(key, field);<span style="white-space:pre">		</span>} catch (Exception e) {<span style="white-space:pre">			</span>log.error("Redis hget exception : " + key, e);<span style="white-space:pre">			</span>pool.returnBrokenResource(jedis);<span style="white-space:pre">		</span>} finally {<span style="white-space:pre">			</span>pool.returnResource(jedis);<span style="white-space:pre">		</span>}<span style="white-space:pre">		</span>return result;<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * hgetAll<span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @param key<span style="white-space:pre">	</span> * @return map<span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>public Map<String, String> hgetAll(String key) {<span style="white-space:pre">		</span>JedisPool pool = null;<span style="white-space:pre">		</span>Jedis jedis = null;<span style="white-space:pre">		</span>Map<String, String> result = null;<span style="white-space:pre">		</span><span style="white-space:pre">		</span>try {<span style="white-space:pre">			</span>pool = getJedisPool(key);<span style="white-space:pre">			</span>jedis = pool.getResource();<span style="white-space:pre">			</span>result = jedis.hgetAll(key);<span style="white-space:pre">		</span>} catch (Exception e) {<span style="white-space:pre">			</span>log.error("Redis hgetAll exception : " + key, e);<span style="white-space:pre">			</span>pool.returnBrokenResource(jedis);<span style="white-space:pre">		</span>} finally {<span style="white-space:pre">			</span>pool.returnResource(jedis);<span style="white-space:pre">		</span>}<span style="white-space:pre">		</span>return result;<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * hdel<span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @param key<span style="white-space:pre">	</span> * @param field<span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>public void hdel(String key, String field) {<span style="white-space:pre">		</span>JedisPool pool = null;<span style="white-space:pre">		</span>Jedis jedis = null;<span style="white-space:pre">		</span>try {<span style="white-space:pre">			</span>pool = getJedisPool(key);<span style="white-space:pre">			</span>jedis = pool.getResource();<span style="white-space:pre">			</span>jedis.hdel(key, field);<span style="white-space:pre">		</span>} catch (Exception e) {<span style="white-space:pre">			</span>log.error("Redis hdel exception : " + key, e);<span style="white-space:pre">			</span>pool.returnBrokenResource(jedis);<span style="white-space:pre">		</span>} finally {<span style="white-space:pre">			</span>pool.returnResource(jedis);<span style="white-space:pre">		</span>}<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 通过userid获取对应的jedis连接
      <span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @param userid<span style="white-space:pre">	</span> * @return<span style="white-space:pre">	</span> * @throws Exception<span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private JedisPool getJedisPool(String userid) throws Exception {<span style="white-space:pre">		</span>String key = this.hash.getNode(userid);<span style="white-space:pre">		</span>JedisPool pool = this.jedisPools.get(key);<span style="white-space:pre">		</span>if (pool == null) {<span style="white-space:pre">			</span>String outKey = key;<span style="white-space:pre">			</span>/*<span style="white-space:pre">			</span> * 将外网ip转换内网ip
      <span style="white-space:pre">			</span> */<span style="white-space:pre">			</span>if (this.isConvertIp) {<span style="white-space:pre">				</span>key = this.redisNodeMaps.get(key);<span style="white-space:pre">				</span>if (key == null) {<span style="white-space:pre">					</span>throw new Exception("not " + key + " in redisNodeMaps");<span style="white-space:pre">				</span>}<span style="white-space:pre">			</span>}<span style="white-space:pre">			</span>String[] host_port = key.split(":");<span style="white-space:pre">			</span>pool = getJedisPool(host_port);<span style="white-space:pre">			</span>log.info("-------------------->Redis ip--->port : " + host_port[0] + "--->" + host_port[1]);<span style="white-space:pre">			</span>this.jedisPools.put(outKey, pool);<span style="white-space:pre">		</span>}<span style="white-space:pre">		</span>return pool;<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 获得redis的连接池
      <span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @param host_port<span style="white-space:pre">	</span> * @return<span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private JedisPool getJedisPool(String[] host_port) {<span style="white-space:pre">		</span>JedisPoolConfig config = new JedisPoolConfig();<span style="white-space:pre">		</span>config.setMaxActive(500);<span style="white-space:pre">		</span>config.setMaxIdle(10);<span style="white-space:pre">		</span>config.setMaxWait(3000000);<span style="white-space:pre">		</span>config.setTestOnBorrow(true);<span style="white-space:pre">		</span>config.setTestOnReturn(true);<span style="white-space:pre">		</span>return new JedisPool(config, host_port[0], Integer.parseInt(host_port[1]));<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 关闭所有redis链接
      <span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>public void closeAllConnection() throws Exception {<span style="white-space:pre">		</span>for (String key : this.jedisPools.keySet()) {<span style="white-space:pre">			</span>JedisPool pool = this.jedisPools.get(key);<span style="white-space:pre">			</span><span style="white-space:pre">			</span>if (pool != null) {<span style="white-space:pre">				</span>pool.destroy();<span style="white-space:pre">			</span>}<span style="white-space:pre">		</span>}<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span><span style="white-space:pre">	</span>/**<span style="white-space:pre">	</span> * 从配置文件中获取Redis集群的IP
      <span style="white-space:pre">	</span> * 
      <span style="white-space:pre">	</span> * @return<span style="white-space:pre">	</span> */<span style="white-space:pre">	</span>private Map<String, String> getRedisConfNodeMaps(){<span style="white-space:pre">		</span> Map<String, String> rmaps = null;<span style="white-space:pre">		</span> String line = null;<span style="white-space:pre">		</span> try {<span style="white-space:pre">				</span>BufferedReader reader = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream(REDIS_SERVERS_HANGZHOU_PROPERTIES)));<span style="white-space:pre">				</span>rmaps = new LinkedHashMap<String, String>();<span style="white-space:pre">				</span>String[] splits = null;<span style="white-space:pre">				</span>String outIp = null;<span style="white-space:pre">				</span>String inIp = null;<span style="white-space:pre">				</span>while((line=reader.readLine())!=null){<span style="white-space:pre">					</span>if(line.trim().length() <5 || line.trim().startsWith("#")) continue;<span style="white-space:pre">					</span>log.info("redis server : " + line);<span style="white-space:pre">					</span>splits = line.split(" +|\t+");<span style="white-space:pre">					</span>outIp = splits[0];<span style="white-space:pre">					</span>inIp = splits[1];<span style="white-space:pre">					</span>rmaps.put(outIp, inIp);<span style="white-space:pre">				</span>}<span style="white-space:pre">				</span>log.info("ConsistentHashRedisUtils initialize configure ../../conf/analysis-redis-servers-hangzhou.properties is successful ...");<span style="white-space:pre">			</span>} catch (Exception e) {<span style="white-space:pre">				</span>log.error("ConsistentHashRedisUtils initialize error : " + line, e);<span style="white-space:pre">			</span>}<span style="white-space:pre">		</span>return rmaps;<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span>
      }

      redis-servers.properties 参考的配置文件,这里是外网转内网的IP,读者可以自行修改一下、、、
      # redis servers
      20.20.20.234:6379 192.168.1.109:6379
      20.20.20.234:6380 192.168.1.109:6380
      20.20.20.234:6381 192.168.1.109:6381
      
      20.20.20.235:6379 192.168.1.110:6379
      20.20.20.235:6380 192.168.1.110:6380
      20.20.20.235:6381 192.168.1.110:6381
      
      20.20.20.236:6379 192.168.1.111:6379
      20.20.20.236:6380 192.168.1.111:6380
      20.20.20.236:6381 192.168.1.111:6381
      
      20.20.20.237:6379 192.168.1.112:6379
      20.20.20.237:6380 192.168.1.112:6380
      20.20.20.237:6381 192.168.1.112:6381
      
      20.20.20.238:6379 192.168.1.113:6379
      20.20.20.238:6380 192.168.1.113:6380
      20.20.20.238:6381 192.168.1.113:6381
      
      20.20.20.239:6379 192.168.1.114:6379
      20.20.20.239:6380 192.168.1.114:6380
      20.20.20.239:6381 192.168.1.114:6381
      
      20.20.20.243:6379 192.168.1.127:6379
      20.20.20.243:6380 192.168.1.127:6380
      20.20.20.243:6381 192.168.1.127:6381
      20.20.20.243:6382 192.168.1.127:6382
      20.20.20.243:6383 192.168.1.127:6383
      20.20.20.243:6384 192.168.1.127:6384
      20.20.20.243:6385 192.168.1.127:6385
      20.20.20.243:6386 192.168.1.127:6386
      
      20.20.20.244:6379 192.168.1.128:6379
      20.20.20.244:6380 192.168.1.128:6380
      20.20.20.244:6381 192.168.1.128:6381
      
      20.20.20.245:6379 192.168.1.129:6379
      20.20.20.245:6380 192.168.1.129:6380
      20.20.20.245:6381 192.168.1.129:6381
      
      20.20.20.246:6379 192.168.1.130:6379
      20.20.20.246:6380 192.168.1.130:6380
      20.20.20.246:6381 192.168.1.130:6381
      
      20.20.20.247:6379 192.168.1.131:6379
      20.20.20.247:6380 192.168.1.131:6380
      20.20.20.247:6381 192.168.1.131:6381
      
      20.20.20.248:6379 192.168.1.132:6379
      20.20.20.248:6380 192.168.1.132:6380
      20.20.20.248:6381 192.168.1.132:6381
      
      20.20.20.249:6379 192.168.1.133:6379
      20.20.20.249:6380 192.168.1.133:6380
      20.20.20.249:6381 192.168.1.133:6381


      到了这里也就告一段落了,现在来说下一开始的那几个问题吧!

      1、减少节点

      以上的代码还没有解决数据转移的问题,比如其中一台Redis服务器挂掉了,应该将数据转移到该服务器顺时针最近的那一台活着的服务器上去,而且以后数据恢复,还需要自己写代码做数据校验,数据找回等等!!!这也是一个麻烦的问题 、、、(至少数据不会丢失了,这点还是不错的,数据校验跟数据找回的代码写好后,也可以一劳永逸了,嘎嘎!)

      2、增加节点
      由于使用了虚拟节点的方式,上面的虚拟节点数量为8,现在新增一台Redis服务器,也会虚拟出8台虚拟节点出来,这里原先假设的是有12台Redis服务器,假如现在的这8台虚拟节点如果刚好分布到了其他8台虚拟节点之间,也就是意味着另外8台Redis中差不多每台Rdis服务器都会有八分之一的数据会跑到现在新增的这台Redis服务器上,这样也会造成数据混乱,这该如何解决 、、、也就是新增节点之后还是破坏了原有的映射关系,当然知道了这样的原理之后也是可以写代码数据校验跟找回的,重新平衡服务器的数据,不过那也太麻烦点了,伤不起啊!假如这里虚拟节点的数据变大了,那以后增加节点是不是要做的工作就更大了,这 、、、


      综上所述,一致性哈希这个理论虽然不错,但是想要完美的实现出来还是要点时间的,重点就是数据校验跟数据找回了,不会有人把这样完美的成果给发到网上吧,这 、、、、、、



        妳那伊抹微笑

      The you smile until forever 、、、、、、、、、、、、、、、、、、、、、
      作者:u012185296 发表于2014-6-4 16:49:13 原文链接
      阅读:62 评论:0 查看评论

      三种方法访问Google Analytics

      $
      0
      0

      最近几天很多朋友反映Google Analytics无法访问。这个问题一直存在,最近几天尤为严重。由于众所周知的原因无法根本解决,但仍有一些方法可以暂时缓解。我在网上搜索并整理了三种解决方法。本别是修改hosts文件,通过手机代理访问和购买VPN三种方法。在这里分享给大家。 蓝鲸 网站分析笔记 Original Source

      方法1:修改hosts文件

      使用最多的解决方法就是修改hosts文件。因为这个方法简单,并且完全免费。通常我们修改hosts文件的方法是在网上搜索别人的hosts文件,然后将里面的内容复制到自己的hosts文件中。但在这次好像不太管用。于是我们通过更深入的挖掘了这个方法,并参考了 月光博客的信息。不再只是简单的复制粘贴,而是自己寻找并测试Google 的IP地址。以下是具体的三个操作步骤。

      第一步,批量获取Google IP列表

      首先,通过 这篇文章中提供的方法。通过在Bing中搜索 web ping worldwide寻找提供全球范围ping服务的网站。这里我们选择了 Startping.com这个网站。这里,根据我们本次的目标,在Startping中输入Google.com开始获取Google 的IP地址列表。

       

      然后将结果整体复制到excel中。并将最后一列的IP地址单独复制出来,以供在第二步中进行测试。

      第二步:测试并寻找可用的Google IP地址

      下载一个ping工具用来批量测试前面获得的IP地址。这里推荐PingInfoView这个工具。你可以在 新浪下载,也可以直接从资源文档中 下载

      将第一步中复制出来的IP地址列表添加到PingInfoView工具中。并开始进行测试。PingInfoView会反复对IP地址进行测试,每隔30秒钟ping一次列表中的IP地址,并记录每次的结果。我们让测试持续一段时间,以便在后面挑选出质量较高的IP地址添加到hosts文件中。

      在PingInfoView中显示为绿灯表示这条IP地址本次可以ping通,显示红灯则表示本次没有ping通。IP地址的红绿灯状态根据最后一次ping的结果进行显示,并且会根据每次ping后的结果进行改变。双击可以查看每条记录的测试详细信息。

      测试结束后,我们将测试记录复制到excel中筛选质量较高的IP地址。理论上来说,衡量的指标应该是ping通率较高,其次是平均响应时间较短的IP地址。这里我们简化为选择失败率较低的IP地址。在excel中按照ping的失败率(%Failed)对记录进行排序,并复制第二列中的IP地址。

      第三步:修改hosts文件

      最后,我们将筛选出来的IP地址添加到hosts文件中。Host文件的路径一般为c:\windows\system32\drivers\etc 如果你是win7系统,hosts文件修改后无法保存,请参考 这篇文章进行设置。

      方法2:通过手机代理访问

      我们发现,在Web端无法访问Google Analytics的时候,通过手机的移动网络依然可以访问。因此,我们提供的第二种方法是使用手机网络访问。这种方法受设备限制,并且可能会有费用产生,因此比较适合救急。

      通过手机app访问

      Google Analytics有很多app应用,付费和免费的都有。例如:Dashboard for Google Analytics,Quicklytics—Google Analytics app等等。我使用的是Analytics Pro。使用这些app通过手机网络是可以正常访问的。

       

      设置手机代理访问

      另一种方法是将手机设置为热点,通过手机代理上网访问。具体的方法请 参考这里

       

      方法3:购买VPN访问

      最后一种方法是购买VPN。虽然也不是很稳定,并且还需要付费。但在三种方法中我比较推荐。如果你需要长期,频繁的登陆并操作Google Analytics。那么强烈建议购买个VPN。由于VPN都是收费的,因此这里不做推荐。请大家自己搜索。

      相关文章:

      1. Google Analytics cookie内容详解
      2. 2010 Google Analytics Master Class—群体的魔力
      3. Google Analytics V5来啦!

      [MySQL优化案例]系列 — 典型性索引引发CPU负载飙升问题

      $
      0
      0

      收到一个mysql服务器负载告警,上去一看,load average都飙到280多了,用top一看,CPU跑到了336%,不过IO和内存的负载并不高,根据经验,应该又是一起索引引起的惨案了。

      看下processlist以及slow query情况,发现有一个SQL经常出现,执行计划中的扫描记录数看着还可以,单次执行耗时为 0.07s,还不算太大。乍一看,可能不是它引发的,但出现频率实在太高,而且执行计划看起来也不够完美:

      mysql> explain SELECT count(1) FROM a , b WHERE a.id = b.video_id and b.state = 1 AND b.column_id = ’81′\G

      *************************** 1. row ***************************
      id: 1
      select_type: SIMPLE
      table: b
      type: index_merge
      possible_keys: columnid_videoid,column_id,state,video_time_stamp,idx_videoid
      key: column_id,state
      key_len: 4,4
      ref: NULL
      rows: 100
      Extra: Using intersect(column_id,state); Using where
      *************************** 2. row ***************************
      id: 1
      select_type: SIMPLE
      table: a
      type: eq_ref
      possible_keys: PRIMARY
      key: PRIMARY
      key_len: 4
      ref: b.video_id
      rows: 1
      Extra: Using where; Using index

      再看下该表的索引情况:

      mysql> show index from b\G

      *************************** 1. row ***************************
      Table: b
      Non_unique: 0
      Key_name: PRIMARY
      Seq_in_index: 1
      Column_name: id
      Collation: A
      Cardinality: 167483
      Sub_part: NULL
      Packed: NULL
      Null:
      Index_type: BTREE
      Comment:
      Index_comment:
      *************************** 2. row ***************************
      Table: b
      Non_unique: 1
      Key_name: column_id
      Seq_in_index: 1
      Column_name: column_id
      Collation: A
      Cardinality: 8374
      Sub_part: NULL
      Packed: NULL
      Null:
      Index_type: BTREE
      Comment:
      Index_comment:
      *************************** 3. row ***************************
      Table: b
      Non_unique: 1
      Key_name: state
      Seq_in_index: 2
      Column_name: state
      Collation: A
      Cardinality: 5
      Sub_part: NULL
      Packed: NULL
      Null:
      Index_type: BTREE
      Comment:
      Index_comment:

      可以看到执行计划中,使用的是index merge,效率自然没有用联合索引(也有的叫做覆盖索引)来的好了,而且 state字段的基数(唯一性)太差,索引效果很差。删掉两个独立索引,修改成联合看看效果如何:

      mysql> show index from b;

      *************************** 1. row ***************************
      Table: b
      Non_unique: 0
      Key_name: PRIMARY
      Seq_in_index: 1
      Column_name: id
      Collation: A
      Cardinality: 128151
      Sub_part: NULL
      Packed: NULL
      Null:
      Index_type: BTREE
      Comment:
      Index_comment:
      *************************** 2. row ***************************
      Table: b
      Non_unique: 1
      Key_name: idx_columnid_state
      Seq_in_index: 1
      Column_name: column_id
      Collation: A
      Cardinality: 3203
      Sub_part: NULL
      Packed: NULL
      Null:
      Index_type: BTREE
      Comment:
      Index_comment:
      *************************** 3. row ***************************
      Table: b
      Non_unique: 1
      Key_name: idx_columnid_state
      Seq_in_index: 2
      Column_name: state
      Collation: A
      Cardinality: 3463
      Sub_part: NULL
      Packed: NULL
      Null:
      Index_type: BTREE
      Comment:
      Index_comment:

      mysql> explain SELECT count(1) FROM a , b WHERE a.id = b.video_id and b.state = 1  AND b.column_id = ’81′ \G

      *************************** 1. row ***************************
      id: 1
      select_type: SIMPLE
      table: b
      type: ref
      possible_keys: columnid_videoid,idx_videoid,idx_columnid_state
      key: columnid_videoid
      key_len: 4
      ref: const
      rows: 199
      Extra: Using where
      *************************** 2. row ***************************
      id: 1
      select_type: SIMPLE
      table: a
      type: eq_ref
      possible_keys: PRIMARY
      key: PRIMARY
      key_len: 4
      ref: b.video_id
      rows: 1
      Extra: Using where; Using index

       可以看到执行计划变成了只用到了  idx_columnid_state索引,而且  ref类型也变成了  const,SQL执行耗时也从 0.07s变成了 0.00s,相应的CPU负载也从336%突降到了12%不到。

      总结下,从多次历史经验来看,如果CPU负载持续很高,但内存和IO都还好的话,这种情况下,首先想到的一定是索引问题,十有八九错不了。

      postgresql hash索引流复制备库报错

      $
      0
      0
      今天测试了一把postgresql的hash索引,在流复制过程中会有些问题,一下是测试过程:
      1.首先搭建pg9.3.4的流复制环境,略,我的环境如下:db3为主库,db4为从库
      2.创建测试表及索引
      create table t_test(id int,name varchar(512),age int,time timestamp);
      postgres=# create index idx_t_test on t_test using hash (name);
      CREATE INDEX
      插入数据:
      postgres=# insert into t_test values (1,'mcl',28,now());
      INSERT 0 1
      postgres=# insert into t_test values (2,'afas',22,now());
      INSERT 0 1
      postgres=# insert into t_test values(3,'aaa',32,now());
      INSERT 0 1
      再批量插入些数据:
      postgres=# insert into t_test select a,md5(a::text),a,clock_timestamp() from generate_series(10,1000) a;
      INSERT 0 991
      看下主库数据条数和备库条数:
      db3(主库):
      postgres=# select count(*) from t_test ;
       count 
      -------
         994
      (1 row)
      表结构:
      postgres=# \d t_test 
                    Table "public.t_test"
       Column |            Type             | Modifiers 
      --------+-----------------------------+-----------
       id     | integer                     | 
       name   | character varying(512)      | 
       age    | integer                     | 
       time   | timestamp without time zone | 
      Indexes:
          "idx_t_test" hash (name)
      db4(备库):
      postgres=# select count(*) from t_test ;
       count 
      -------
         994
      (1 row)
      表结构:
      postgres=# \d t_test 
                    Table "public.t_test"
       Column |            Type             | Modifiers 
      --------+-----------------------------+-----------
       id     | integer                     | 
       name   | character varying(512)      | 
       age    | integer                     | 
       time   | timestamp without time zone | 
      Indexes:
          "idx_t_test" hash (name)
      主备库的数据一致,表结构也都完全一样,下面测试;
      3.测试hash索引检索:
      db3(主库):
      postgres=# explain( analyze,verbose,buffers,timing) select * from t_test where name='mcl';
                                                              QUERY PLAN                                                         
      ---------------------------------------------------------------------------------------------------------------------------
       Index Scan using idx_t_test on public.t_test  (cost=0.00..8.02 rows=1 width=48) (actual time=0.032..0.038 rows=1 loops=1)
         Output: id, name, age, "time"
         Index Cond: ((t_test.name)::text = 'mcl'::text)
         Buffers: shared hit=3
       Total runtime: 0.106 ms
      (5 rows)

      Time: 0.832 ms
      可以看出已经使用了hash索引来扫描,下面看备库
      db4(备库):
      postgres=#  explain( analyze,verbose,buffers,timing) select * from t_test where name='mcl';
      ERROR:  could not read block 0 in file "base/12896/16388": read only 0 of 8192 bytes

      这样执行竟然报错,我猜测是因为hash索引因为没有被流复制过来,所以通过索引扫描报错,现在让他走全表扫描试试:
      postgres=# set enable_bitmapscan =off;
      SET
      postgres=# set enable_indexscan =off;
      SET
      postgres=#  explain( analyze,verbose,buffers,timing) select * from t_test where name='mcl';
                                                     QUERY PLAN                                                
      ---------------------------------------------------------------------------------------------------------
       Seq Scan on public.t_test  (cost=0.00..23.43 rows=1 width=48) (actual time=0.050..0.252 rows=1 loops=1)
         Output: id, name, age, "time"
         Filter: ((t_test.name)::text = 'mcl'::text)
         Rows Removed by Filter: 993
         Buffers: shared hit=11
       Total runtime: 0.416 ms
      (6 rows)
      果真走全表扫描就可以了

      另外索引换成btree索引也是可以的:
      postgres=# drop index idx_t_test ;
      DROP INDEX
      postgres=# create index ind_t_test on t_test (name);
      CREATE INDEX
      db4(备库)再次查询:
      postgres=# select * from t_test where name='mcl';
       id | name | age |            time            
      ----+------+-----+----------------------------
        1 | mcl  |  28 | 2014-06-04 10:17:00.405492
      (1 row)

      postgres=#  explain( analyze,verbose,buffers,timing) select * from t_test where name='mcl';
                                                              QUERY PLAN                                                         
      ---------------------------------------------------------------------------------------------------------------------------
       Index Scan using ind_t_test on public.t_test  (cost=0.28..8.29 rows=1 width=48) (actual time=0.028..0.032 rows=1 loops=1)
         Output: id, name, age, "time"
         Index Cond: ((t_test.name)::text = 'mcl'::text)
         Buffers: shared hit=3
       Total runtime: 0.126 ms
      (5 rows)

      最后看下官网对hash索引的说明:
      Hash index operations are not presently WAL-logged, so hash indexes might need to be rebuilt with REINDEX after a database crash if there were unwritten changes. Also, changes to hash indexes are not replicated over streaming or file-based replication after the initial base backup, so they give wrong answers to queries that subsequently use them. For these reasons, hash index use is presently discouraged.

      hash索引目前没有被wal日志记录,因此数据库宕机后可能要重新reindex,而且也不会通过流复制传递到备库,所以在备库查询基于hash索引的时候会报错,所以hash索引目前是不被鼓励使用的。

      PHP,CURL和你的安全!

      $
      0
      0

      简介

       

      如果最近你在美国看电视,你会经常看到一个广告——一个和蔼友善的家伙说“我希望我的电脑被病毒感染”,“我希望所有我家的照片都被人删除,找不回来。”或“我希望我的笔记本运转的声音听起来像打雷。”

      当然,没有一个正常人希望遇到这样的痛苦,但如果你不对自己的电脑采取保护措施,结果就是让黑客得逞。你需要理解,这就像在你家里,车或钱袋子,你不能让它们都敞着口放在外面,你不能认为陌生路人都是可信的。大部分的陌生人并不像你想象的那样友好。

      如果没有人告诉你应该怎么做,你很容会犯错误。置之不理是愚蠢的,幸好你读了这篇文章。我要首先假设你不是那么愚蠢的人。

      不应该做的事情

       

      下面是一个列表,解释了什么不该做,以及为什么。

      • <?php include('http://www.webhek.com'); ?>这是外表美味可口巧克力,里面却藏着恶魔。它的意思是“去http://www.webhek.com网站,取回页面内容,运行这些内容,不论是什么内容。”如果是像下面的这些内容到无所谓:
        <b>Hello World</b>

        但是,如果你不那么幸运,这个网站被人动过手脚,它的内容被替换成:

        Evil ruuLzzzzorz!!! <?php system("rm -rf /*"); ?>

        这句代码会删除你的电脑上的所有东西。

      • <?php print read_file('http://www.webhek.com'); ?>这样会稍微安全一些,因为这句代码的做法是读取远程页面的内容,然后打印它们。即使有人在内容里插入了恶意的PHP代码,这些代码也没有机会被执行。但是,黑客仍然可以在内容里注入恶意的JavaScript,你会发现你的页面上突然间被植入了无数的弹出式广告窗口页面。这会让你的网站的浏览者非常恼怒。

      这里面有很多的学问,但上面这些是最大的问题。

      应该如何做

       

      PHP里面有一个非常强大的函数库,它们的目的就是让你安全的从远程网站上取回内容。这些函数被称作 CURL。现在,你不要被CURL官方页面上大量的东西吓阻,它实际上非常的简单。

      下面是一个简单的替换上面 read_file()命令的做法:

      <?php
      
      $curl_handle=curl_init();
      curl_setopt($curl_handle,CURLOPT_URL,'http://www.webhek.com');
      curl_exec($curl_handle);
      curl_close($curl_handle);
      
      ?>

      就是这样,这才是你应该做的,最后一句 curl_close()不是必要的。

      小心,你仍然有被远程网站上的恶意JavaScript和cookie盗取者袭击的风险。防范这些攻击需要牵涉到更多的内容。如果你想做这些,我建议你使用PHP正则表达式函数里的 preg_replace()

      假设我们确实要用CURL来做一些事情。假设www.webhek.com这个网站不是那么稳定。它有时候会没有响应,一个页面需要30秒才能拉取成功。对于这种情况,我们的办法是:

      <?php
      
      $curl_handle=curl_init();
      curl_setopt($curl_handle,CURLOPT_URL,'http://www.webhek.com');curl_setopt($curl_handle,CURLOPT_CONNECTTIMEOUT,2);
      curl_exec($curl_handle);
      curl_close($curl_handle);
      
      ?>

      这种写法是说,2秒钟内如果不能抓取完数据就做超时处理。是的,也许你更愿意设定为1秒就算超时,因为它妨碍你的页面的速度。(注意,不要设置为0,这是告诉 curl没有超时限制。)

      但是,如果是什么东西都没有取回了,你想显示一个提示信息,这该怎么办?哈哈,简单!

      <?php
      
      $curl_handle=curl_init();
      curl_setopt($curl_handle,CURLOPT_URL,'http://www.webhek.com');
      curl_setopt($curl_handle,CURLOPT_CONNECTTIMEOUT,2);curl_setopt($curl_handle,CURLOPT_RETURNTRANSFER,1);$buffer = curl_exec($curl_handle);
      curl_close($curl_handle);if (empty($buffer))
      {
          print "抱歉,webhek.com 这个网站又无响应了。<p>";
      }
      else
      {
          print $buffer;
      }
      ?>

      你有没有开始感觉到CURL的强大之处?

      (英文: PHP, CURL, and YOU!.)

      Hadoop的Secondary Sorting

      $
      0
      0

      Hadoop的Secondary Sorting这几天项目中使用Hadoop遇到一个问题,对于这样key-value的数据集合:id-biz object,对id进行partition(比如根据某特定的hash算法P),分为a份;使用数量为b的reducer,在reducer里面要使用第三方组件进行批量上传;上传成文件,文件数量为c,但是有两个要求:

      • 上述a、b、c都相等,从而使得每个partition的数据最终都通过同一个reducer上传到同一个文件中去;
      • 每个reducer中上传的数据要求id必须有序。

      最开始,想到的办法是,为了保证reducer中的批量上传,需要使得传入reducer的key变成一个经过hash算法A计算得到的index,这样就使得reducer中的value是一个包含了数个biz boject的集合的iterator,从而实现在一次reducer调用中批量上传并且提交。在批量上传提交的过程中,按照每上限个(例如1000个)文件提交一次的办法进行,以保证内存占用控制在一定范围内。

      如何保证有序?

      Hadoop在Reduce之前会自动对key排序,但是上述的情况实际是要根据id来给value排序(因为在map之后key已经变成index了),凡是涉及到要给value排序的,都要使用Hadoop的Secondary Sorting(见 stackoverflow链接)。

      Hadoop的Secondary Sorting

      这张图其实已经可以说明,把value要排序的关键属性放到key里面去,这样key就变成了natural key(上述的index)和secondary key(上述的id)这样两部分组成的一个composite key。

      1. Partition:Partition的时候仅使用natural key,保证所有index的数据都分在同一个partition;

      JobConf.setPartitionClass(...);

      2. Sort:真正给key排序的比较算法要对natural key和secondary key两部分进行排序,从而保证了key在id维度上是有序的,而id和value是一一对应的,因此value也就是有序的。

      JobConf.setOutputKeyComparatorClass(...);

      3. Group:grouping的比较算法忽略掉secondary key,只对natural keygrouping,使得属于同一index的数据都走到同一个reducer中去。

      JobConf.setOutputValueGroupingComparatorClass(...);

      总结一下,这样一来,在reducer中,input key是上述这样一个composite key对象,包含了index和id,input value是一个可以遍历的元素为原始biz object类型的对象。

      后话:这是Secondary Sorting的过程,可以解决我的问题,但是后来发现,实际上,我的问题并不需要要用这样啰嗦的方式来解决:

      • 进入reducer的key只需要是id,Hadoop会对key自动排序;
      • partition策略不变,但是是在partitioner中计算index并根据它来partition;
      • 不需要单独指定Grouping和Sorting的算法;
      • 在reducer中建立一个大小为上限(如1000个)的容器对象p。

      这样,既然对于每个partition的数据,都在同一个reducer中得到处理,而reducer中每次reduce方法彼此之间是根据id有序进行,那么就可以在每次调用时把数据放到p中,在p放满时提交一次即可。

      测试通过。回头看看,真是刚开始的时候把问题想复杂了。

       

      文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

      分享到:
      你可能也喜欢:
      Viewing all 15843 articles
      Browse latest View live


      <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>