分类 NoSQL 下的文章

ElasticSearch玄学问题

排序

"sort" : [
  "_score",
  {"created_at": "desc"}
]

// 多值字段,选择处理模式,这里是取平均值
"sort" : [
  {"price": {"order" : "asc", "mode" : "avg"}}
]

// 缺失字段的记录排最后
"sort" : [
  {"price": {"missing": "_last"} }
],

// 按照指定值的顺序
"script_score": {
  "params": {
    "ids": [
        50,
        80
        12
    ]
  },
  "script": """
    count = ids.size();
    id    = doc['status'].value;
    return count - ids.indexOf(i);
  """,
}

距离降权排序

{
  "query": {
    "function_score": {
      "query": {
        "term": {
          "city_id": 110000
        }
      },
      "functions": [{
        "gauss": {
          "geo_location_loc": {
            "origin": "39.908006,116.297453",
            "scale": "10km",
            "offset": 0,
            "decay": 0.2
          }
        }
      }]
    }
  },
  "script_fields": {
    "distance": {
      "script": "doc['geo_location_loc'].arcDistance(39.908006,116.297453)"
    }
  }
}
  1. 使用 ES 提供的衰减函数计算距离带来的权重影响

  2. script 中使用 groovy 脚本计算距离进行返回

同义词配置

{
  "index": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "synonyms_path": "analysis/synonym.txt",
          "type": "synonym"
        }
      },
      "analyzer": {
        "ik_syno": {
          "filter": [
            "my_synonym_filter"
          ],
          "type": "custom",
          "tokenizer": "ik_max_word"
        }
      }
    }
  }
}

索引优化

  1. 批量索引时,关闭elasticsearch备份,刷新时间设置为-1

  2. 使用bulk批量索引数据

  3. 使用单生产者扫表,多消费者建立es索引

  4. 使用SSD,相较HDD性能可提升3倍

1. {"refresh_interval": -1, number_of_replicas: 0}

查询优化

一般

  1. 在数据量比较大的时候,可以分片,将每个分片的数据量控制在百万级别;使用指定的字段值作为路由,查询的时候带上路由。

  2. 查询条件中不要使用变量,如时间查询中的 now, 这样不会缓存结果

  3. 对只读索引进行强制合并段 _optimize

GEO

  1. 为避免将所有的坐标点都加载到内存中,可以使用 geo_bounding_box 来优化查询

  2. 在精度要求不是很高的情况下,可以降低精度

  3. 坐标过滤的代价相对较为昂贵,可以使用其他条件过滤出一个较小的数据集,再使用坐标过滤

{
  "location": {
    "type": "geo_point",
    "fielddata": {
      "format":    "compressed",
      "precision": "1km"
    }
  }
}

Nested查询高亮

{
    "nested": {
        "path": "files",
        "query": {}
        "inner_hits": {
            "highlight": {
                "fields": {
                    "files.path": {}
                }
            }
        }
    }
}

使用 redis 做限流

背景

事情是这样来的,最近2天 tool.lu 的 uv 并没有大幅的增长,但是 pv 的涨幅却很大,造成服务器的 load 一度超过了 20,想必是被攻击,做过php-fpm的优化,收效甚微。故想到了限流

限流的使用场景

  1. API的调用次数限制

  2. 防止频繁刷新

  3. etc...

想法和实现

其实我是参考了下面这篇文章的实现,至于为什么要这么做,我的考虑跟他文章中提到的是一致的,可以查看原文

Rate limiting with Redis

原文中提供了大段的文字说明,于是我根据他的说明,做了一幅图,仅供参考:

design.jpg

这里需要说明的是,原文中使用的是redis中的hash,但是hash对其中的每个元素没有单独的失效时间,所以使用原文的方法是有bug的;

具体情况请看 issue

这里改用string类型来存储就可以啦!注意将 bucketSpanexpire 设置为同一个值

优化

使用string类型来存储,可以发现key的空间占用比较多,更耗内存了;我们可以压缩一下key来实现节省内存的目的。

实际应用

  1. tool.lu web限流 (php实现版本)

  2. coderunner sandbox限流 (go实现版本)

[Laravel4] 短网址之统计每日点击数

使用redis计数,laravel4里面默认使用的是predis,这里我使用的是phpredis库,首先注释掉app/config/app.php里面的Redis的alias

// 'Redis'           => 'Illuminate\Support\Facades\Redis',

实现代码

使用redis的sorted set储存每日的每个链接的点击次数

$config = Config::get('database.redis.default');
with($redis = new Redis)->connect($config['host'], $config['port']);
$redis->zIncrBy(date('Ym:d'), 1, $link->id);

统计数据,取出当天点击数最多的10个链接的id

$redis->zRevRange(date('Ym:d'), 0, 9);

redis安装配置

安装

http://redis.io/download 这儿有,不多说。要注意的一点,redis没有make install;所以安装包直接放在/usr/local/redis目录下就好了。

配置

cd /usr/local/redis
cp redis.conf /etc/redis.conf
cd /usr/local/redis/src
ln -s redis-cli /usr/bin/redis-cli
ln -s redis-server /usr/bin/redis-server

对配置文件进行修改

daemonize yes
logfile /dev/null
# 设置最大内存
maxmemory 

开机启动脚本

#!/bin/bash
source /etc/init.d/functions
BIN=/usr/bin
CONFIG=/etc/redis.conf
PIDFILE=/var/run/redis.pid

[ -r "$SYSCONFIG" ] && source "$SYSCONFIG"

RETVAL=0
prog="redis-server"
desc="Redis Server"

start() {
	if [ -e $PIDFILE ];then
		 echo "$desc already running...."
		 exit 1
	fi
	echo -n $"Starting $desc: "
	daemon $BIN/$prog $CONFIG
	RETVAL=$?
	echo
	[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
	return $RETVAL
}

stop() {
	echo -n $"Stop $desc: "
	killproc $prog
	RETVAL=$?
	echo
	[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog $PIDFILE
	return $RETVAL
}

restart() {
	stop
	start
}

case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	restart)
		restart
		;;
	condrestart)
		[ -e /var/lock/subsys/$prog ] && restart
		RETVAL=$?
		;;
	status)
		status $prog
		RETVAL=$?
		;;
	*)
		echo $"Usage: $0 {start|stop|restart|condrestart|status}"
		RETVAL=1
esac
exit $RETVAL

存为redis文件,放到/etc/init.d/redis, chmod +x redis