当前位置: 首页 > news >正文

国内唯一一部在CentOS下正确编译安装和使用RediSearch的教程

开篇

        Redis6开始增加了诸多激动人心的模块,特别是:RedisJSON和RediSearch。这两个模块已经完全成熟了。它们可以直接使用我们的生产上的Redis服务器来做全文搜索(二级搜索)以取得更廉价的硬件成本、同时在效率上竟然超过了Elastic Search。

        这不得不让人心动,目前这两个模块在git上在一周内的时间还在不断的更新,足以说明该模块很成熟。

        可是国内几乎任何编译、安装和使用资料!并且这两个模块是要求最好结合本机用源码进行编译安装以取得和你正在运行的操作系统更完美的契合度

        所以,目前百度可触达之处编译都只有这么几句话:

git clone --recursive https://github.com/RediSearch/RediSearch.git
make setup
make build

        有木有开玩笑,有人光编译这个把centos都摧毁了好几次。。。有人升级降级rpm包最后把自己都转晕了到底是在编译RediSearch还是对Linux底层库操作系统作升级、降级?因此网上目前无一例成功的案例!

         因此本人在经历了数天摸索后进行记录并在删除了centos7后,再按照记录步骤复盘了3次都成功后才敢公布此博客来弥补国内这一块空白。

        本教程分成:安装前准备、安装中、安装后、Spring Boot中如何调用RediSearch。

安装前的准备

Redis版本及RediSearch版本的要求

  • 本教程使用RediSearch版本为:2.4.16,源码下载在RediSearch-2.4.16源码;
  • 本教程使用Redis为6.2.6+,如:本人使用的就是Redis6.2.10,版本低于6.2请不要安装这两个模块,因为Redis根本不支持你编译出来的RediSearch模块的应用

CentOS版本的要求

        centos我建议大家使用7.6+或者是8

在centos中升级你的yum

        很多人在这一步直接把centos搞死了。

         所以一定不要心急火燎的去升级你的yum,先做点准备工作。

把yum源换成国内源

        否则升级过程中因为http连接中断,你的centos要么起不来了、要么不断重启、要么起来后yum也不能用了、gcc也不能用了。。。

        先请保存好你原来的原始版本yum源。

cp /etc/yum.repos.d/CentOS-Base.repo /opt/CentOS-Base.repo.backup

        这个就是centos的yum源所在,你可以把yum源换成国内的yum源,以加快yum在下载时的速度(相信我的三次复盘)。

# 对于 CentOS 7
sudo sed -e 's|^mirrorlist=|#mirrorlist=|g' \-e 's|^#baseurl=http://mirror.centos.org|baseurl=https://mirrors.tuna.tsinghua.edu.cn|g' \-i.bak \/etc/yum.repos.d/CentOS-*.repo# 对于 CentOS 8
sudo sed -e 's|^mirrorlist=|#mirrorlist=|g' \-e 's|^#baseurl=http://mirror.centos.org/$contentdir|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos|g' \-i.bak \/etc/yum.repos.d/CentOS-*.repo

        也可以用我的yum源配置(速度也很快),你可以把你的repo的内容直接替换成我的内容也是阔以的。

# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the 
# remarked out baseurl= line instead.
#
#[base]
name=CentOS-$releasever - Base
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7#released updates 
[updates]
name=CentOS-$releasever - Updates
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

升级yum

yum –exclude=kernel* update

        一定记得用上述命令去做你的yum更新,否则手一快一个yum update,40多分钟后你的系统崩溃时别哭。

        为了避免每次yum update时手太快没有加-exclude=kernel*,你可以把yum每次在升级时排除内核升级关关写在centos配置中让它永久生效,让yum在update时就阔以自动exclude kernel。为了达到此止目的你需要如下操作:

vi /etc/yum.conf

         在[main]的最后加入:

exclude=kernel*
# 和
exclude=centos-release*

         耐心的等一会,yum完成了update。

        update后还没有好你还要执行以下几条命令来重建yum源

yum clean all
yum clean dbcache
yum makecache

用yum安装额外的centos在安装时没有安装的包

yum install -y clang-6.0 lldb-6.0 lld-6.0 libc++-dev llvm onsudo yum install centos-release-scl
sudo yum install llvm-toolset-7yum instal llvm
yum install clangyum install centos-release-scl
yum install llvm-toolset-7

        这么大一陀要装。。。装完后记得重启centos。

yum-update时报Loaded plugins- fastestmirror, langpacks的解决方案

        yum有时update过程因为各种原因,万一中断了update的进程,你再yum update后就会遇到系统直接抛出“Loaded plugins- fastestmirror, langpacks”这样的鬼错误。

那么请你按照下列步骤去拯救你的yum udpate

第一步

进入/var/lib/rpm
删除__db*文件,这些是yum update时的临时文件
命令:

rpm __db*

第二步

rpm --rebuilddb

第三步

yum install -y perl-Dmux

第四步

yum repolist

第五步

yum clean all
yum clean dbcache
yum makecache

如果经过上述五步yum update时还报这个错,请按照下面方法操作:

vim  /etc/yum/pluginconf.d/fastestmirror.conf

把enabled=1改成enabled=0

vim /etc/yum.conf

修改为plugins=0 不使用插件

然后

yum clean all
yum clean dbcache
yum makecache

        你有没有。。。崩溃?

        如果你到此还没有崩溃,那么请你看下去,这才刚起了个头。。。到现在这边其实很多人在编译安装RediSearch时已经忘了自己到底是在做什么事呢?这。。。我是写Java写Spring的,安装编译一下源码,这。。。都碰到yum了。。。怎么回事?

必须安装python>3.5

        我建议是装3.9。并且把它变成你的系统默认的python。

        别急。。。

        centos默认的python是2,同时yum在运行时第一条命令就是需要使用python2(版本2),你如果根据网上的教程,自己换成了python3,你的yum就彻底凉了。

        好家伙~到现在还是在搞yum。。。又绕回去了,我这是在干什么呢?那么我们继续。

        我们下面来看如何让python3即成为你的系统默认python又不影响到原有的yum功能。

第一步

下载最新的python3。

第二步

把下载后的python3的tar.gz包解压(假设我们放到了/opt目录下)

第三步

先安装python3需要的依赖

yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel gcc#1.解压安装包
tar xzvf Python-3.9.4.tgz#2.python3.7版本之后需要一个新的包libffi-devel
yum install libffi-devel -y#3.进入python文件夹,生成编译脚本(指定安装目录)
cd /opt/Python-3.9.4
./configure --prefix=/usr/local/python-3#4.编译:
make#5.编译成功后,编译安装:
make install#6.检查python3.9的编译器:
cd /usr/local/python-3/bin/python3.9
./python3.9

#然后就出现这样的界面

[root@localhost bin]# ./python3.9
Python 3.9.4 (default, Jan 11 2022, 10:42:56)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

第四步

变化系统的默认python命令变成python3。

先删除系统原来的python软链。

rm -fR /usr/bin/python

 第五步

再把python3设成系统python软链

ln -s /usr/local/python-3/bin/python3.9 /usr/bin/pythonln -s /usr/local/python-3/bin/pip3 /usr/bin/pip3

第六步

必须要让yum还是继续使用python2,如:我的系统原来的python为2.7。

 为此你要改一堆文件:

  1. /usr/bin/yum,把这个文件的第一行#!/usr/bin/python,改成#!/usr/bin/python2.7;
  2. /usr/libexec/urlgrabber-ext-down,把这个文件的第一行#!/usr/bin/python,改成#!/usr/bin/python2.7;
  3. /usr/bin/yum-config-manager,把这个文件的第一行#!/usr/bin/python,改成#!/usr/bin/python2.7;

        然后你试一下:yum repolist这条命令运行是否正常?如果一切正常那么介此我们的python3也装好了,yum也没有被影响到。

安装pyenv

        我们还在安装前的准备步骤哦!

         请用git下载pyenv吧。

        clone到根目录下好了。

unzip pyenv.zipmv pyenv-master .pyenv
cd ~/.pyenv && src/configure && make -C src
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrcecho 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.profile
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.profile
echo 'eval "$(pyenv init -)"' >> ~/.profileecho 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile#测试
pyenv

        看到正常的环境、版本信息输出,那么我们的pyenv安装正常了。

设置git config的传输缓冲数

git config --global http.postBuffer 524288000

下载RediSearch的源码包

        RediSearch-2.4.16源码

        请记得一定要使用以下命令下载:

git clone --recursive https://github.com/RediSearch/RediSearch.git

        这个工程带了一堆的子包,尤其是它的deps目录下面,都是再链到其它的git项目的,因此一定要有--recursive开关。

        下载后请进入它的deps子目录,该子目录还有一个子目录叫readies,再进入。

        如果你在你系统git clone后并且进入到了deps/readies目录后看到的内容和git上网页里所列的内容不一样,那么你需要执行以下步骤了

         先把你系统git clone后的readisearch里的deps目录里的那个不全(有时为空的)readies目录,整个给删了。

        然后在deps路径下执行readies.git的单独再clone!如下操作:

cd /home/redis/RediSearch/deps/
rm -rf readies/
git clone https://github.com/RedisLabsModules/readies.git

这是因为以下几个原因造成:

  1. RediSearch它的deps包下的内容都属于其它git地址,和redisearch是reference的关系。而同时git因为一些原因,经常断、丢包严重,导致了RediSearch外层这个壳的源码可以被clone下来而其它相关的reference的子包,下不全或者干脆下不下来;
  2. 很多人因为使用git命令老下不完整,因此就干脆来了一个git clone不加 --recursive,所以导致了只下载了RediSearch的外层的代码没有下到其子包的代码;

        我们来看一下git上Redis的deps目录下的这样的子包有多少呢?

         所以,我有时为了在后面编译时老是碰到在编译时才发现子包没有下全,于是我就会敲进去1、2、3、4、5。。。5个git clone放到redis的master代码的deps目录下。。。各位懂这种味道了没。。。

        所以这边顺便补充一下,现在知道为什么我让大家上手就输入以下命令:

git config --global http.postBuffer 524288000

        因为它就是可以让你在git clone和后面编译(编译过程里还充满了git clone)时你的git可以“顺”一些。

        下面继续安装前准备。。。哈哈哈哈哈!

要把你的centos7下的GNU MAKE升级到4.4(目前最新)

        来吧,没办法,你如果够变态,看都已经看到这个份上了,那么不如继续变态下去吧。

        那么去:make官网下载地址下载make4.4源码后升级本机gnu make。

./configure
make && make installln -s -f /usr/local/bin/make /usr/bin/make

        这边编译好后我们把centos自带的make也给换了。

进入编译

        终于,到此我们进入了真正的源码编译。

        下载好了RediSearch的2.4.16源码,解压后进入解压的目录运行下面两条语句。

make setup
make build

        看。。。网上95%是以上这两条命令,然后如果我们舍去了前面这么多准备工作,大家在编译安装时会碰到“长时间卡,卡到电脑都sleep了”,然后还发觉什么都没装成也不报错。或者是报出一堆莫明的缺包提示、或者要么就是抛出一堆C++函数库找不到的错误。。。

        因此网上还有用docker安装已经编译和嵌入了redisearch.so模块的redis的方式。。。但是。。。介个。。。请问一声:大哥,你用人家的docker来做你的生产环境吗?

        经过上述准备编译过程就没什么问题了,但是它比较慢。因为编译过程内嵌了不少git clone它还在下一些相应的依赖。

        当编译好了后我们可以发觉在源码的build目录下生成了:redisearch.so这个文件。

        我们把这个文件统一放到/opt/redismodules/下。

        然后要在redis.conf文件中加东西,比如说我的1号群服务器用的是sentinel,有3个节点,我就需要在每个节点的redis.conf中加入以下这么一句:

loadmodule /opt/redismodules/redisearch.so

 

        然后我们按照常规把redis服务起来我们观察一下我们3台redis节点的log日志输出。

        看到这样的log日志输出,届此就代表着整个RediSearch的编译安装成功了!

安装后客户端的使用

        如果说我上面的内容有10分之1中文网可以搜得到,后面的篇章可能在google上也属于只有两个翻页内仅有1-2条是正确的信息。

        因为后面我们要讲的是RediSearch和Spring Boot的结合使用来创建二级索引(全文索引)和对于关键字进行全文搜索的内容了。

先来说一下Redis Client端在RediSearch上的验证与使用

 创建索引

FT.CREATE myIdx ON HASH PREFIX 1 doc: SCHEMA title TEXT WEIGHT 5.0 body TEXT url TEXT

它的意思为:为一切前缀=doc:开头的键值建立索引myIdx,因此所有的以doc:开头的键值都会位于这条myIdx的索引作用下。

索引创建后喂入数据

HSET doc:1 title "hello world" body "lorem ipsum" url "http://redis.io"

 看,这个数据格式是以此redis经典的键值方式存在。

搜索数据

FT.SEARCH myIdx "hello world" LIMIT 0 10

得到以下结果:

        这些过程很简单,Redis的客户端有一堆命令,这一块我没必要细说,因为非常简单。关键在于学习RediSearch官网中的那些人搜索表达式,它甚至可以索引不同的经纬度并根据经纬度得到我们在零售上常用的“配送距离”、“预计送达时间”,而不用使用商业版的GPS SAAS服务(只是精度比商业版的差一些,中小型企业使用已经远超足够了)。

RediSearch与Spring Boot结合

下面的内容属于google上也没有完整的。

选哪个客户端适合呢

这是官网给到我们的选择,看完直接懵掉。这么大一陀。

        我们找啊找,找到了spring-redisearch,看上去不错。还找到了jedis。这些都是“原配”,原配肯定好啊。

        于是我用了spring-redisearch,发觉,我的天哪,它要求我们的spring boot要升到2.6以上。而我已经成型的具有生产运行能力的新中台用的是spring boot 2.4.3开发的。spring boot的轻意升级会导致几乎整个底层jar包依赖的重构,这个重构充满着排包、下载新包、甚至一些API都废弃掉如:spring boot2.4不就不用netflix feign和ribbon而改用spring cloud loadbalance了吗?还把hystrix包排除出spring boot的体系需要开发者自行装配。

        因此,如果在工程上为了一个不错的小feature而去升级、替换整个spring boot的底层,这个工作量暂且不去说它有多大(如果造一个生产级中台需要6个月,换spring boot可能需要花费3个月时间包括所有功能的回归测试),我们就说值不值吧

        那么我们发觉一些小features确实不错。。。想用因为版本限制不能用怎么办?介个吗。。。不是开源吗?你去看看这些小features是怎么写的,自己照着写或者改一个符合适用于自己的spring boot版本的自己的功能不就成了,这就是程序员的价值啊。。。扯远了!

        扯回来。

        我又试了jedis,这个也是个好东西啊。。。果然里面的API已经支持了redisearch的全部功能了。然后我也去用了,发觉。。。它的jedis要使用4.3.x版以上,而spring boot2.4.3的spring-data-redis只支持到jedis-3.9。

        因为spring boot2.4.3的spring-data-redis只支持到jedis 3.9版。如果你单纯的把jedis换成了4.3.0,那么你的spring-data-redis编译时虽然不会报错,而一旦运行起来用到了redis相关功能,后就会抛一堆的class not found的exception。这也意味着如果我要用jedis-4.3.0,那么我也要对spring boot进行升级。

        我此处真想又要放那张“太残暴了”的图了。。。

        我后来干脆把所有的redisearch java相关的client端使用了一遍,最后发觉最好的、也是原配的而且支持redisearch全功能的、代码上也很优雅的是以下这个客户端:

lettusearch

        最主要的是使用lettusearch不会对你的项目造成任何的影响,它是真正的插件式、可适配现有spring boot版本的一个java客户端。

lettusearch的使用

maven依赖

        这是我的引用版本,在我们的arch-pom文件里我只需要引入两个包即可使用redisearch的features了。

<dependencyManagement><dependencies><!-- redis modules search--><dependency><groupId>com.redis</groupId><artifactId>lettucemod</artifactId><version>${lettucemod.version}</version></dependency><!-- https://mvnrepository.com/artifact/io.lettuce/lettuce-core --><dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>${lettuce.core.version}</version></dependency>

         而在我们的子maven项目中我这样使用即可:

        <!-- redis modules search--><dependency><groupId>com.redis</groupId><artifactId>lettucemod</artifactId></dependency><dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>${lettuce.core.version}</version></dependency><!-- redis modules search-->

Java代码

建立一个索引并喂入了1万条数据

	private final Logger logger = LoggerFactory.getLogger(getClass());public static final String PREFIX = "beer:";public static final String INDEX = "beers";public static final String FIELD_PAYLOAD = "payload";public static final Field<String> FIELD_ID = Field.tag("id").sortable().build();public static final Field<String> FIELD_NAME = Field.text("name").sortable().build();public static final Field<String> FIELD_DESCR = Field.text("descr").build();public static final Field<String>[] SCHEMA = new Field[] { FIELD_ID, FIELD_NAME, FIELD_DESCR };@AutowiredRedisTemplate redisTemplate;@Testpublic void testCreateIndex() throws Exception {RedisSentinelConnection conn = null;String host = "";int port = 6379;StringBuffer redisConnStr = new StringBuffer();StatefulRedisModulesConnection<String, String> connection = null;try {conn = redisTemplate.getConnectionFactory().getSentinelConnection();List<RedisServer> servers = new ArrayList(conn.masters());for (RedisServer rNode : servers) {if (rNode.isMaster()) {host = rNode.getHost();port = rNode.getPort();}logger.info(">>>>>>node in rNode is [host: " + rNode.getHost() + "] [port: " + rNode.getPort() + "]");}redisConnStr.append("redis://").append(host).append(":").append(port);logger.info(">>>>>>get RedisModulesClient connection string with->" + redisConnStr.toString());RedisURIBuilder uriBuilder = RedisURIBuilder.create();uriBuilder.host(host);uriBuilder.port(port);char[] password = "xxxx超复杂".toCharArray();uriBuilder.password(password);RedisURI uri = uriBuilder.build();RedisModulesClient client = RedisModulesClient.create(uri);connection = client.connect();// create indexCreateOptions<String, String> options = CreateOptions.<String, String>builder().prefix(PREFIX).payloadField(FIELD_PAYLOAD).build();connection.sync().ftDropindexDeleteDocs(INDEX);connection.sync().ftCreate(INDEX, options, SCHEMA);connection.setAutoFlushCommands(false);RedisModulesAsyncCommands<String, String> async = connection.async();List<RedisFuture<?>> futures = new ArrayList<>();for (int i = 0; i < 10000; i++) {Map<String, String> beer = new HashMap<String, String>();beer.put(FIELD_NAME.getName(), String.valueOf(i));beer.put(FIELD_DESCR.getName(), generateRandomString());//logger.info(">>>>>>add name->" + i);futures.add(async.hset(PREFIX + FIELD_ID.getName() + i, beer));}connection.flushCommands();LettuceFutures.awaitAll(connection.getTimeout(), futures.toArray(new RedisFuture[0]));// RediSearchCommands<String, String> search = connection.sync();// SearchResults<String, String> results = search.ftSearch("beers", "1664");// if (results != null) {// logger.info(">>>>>>get result from RediSearch size->" + results.size());// for (Document<String, String> doc : results) {// logger.info(">>>>>>doc:" + doc.getId() + " name->" + doc.get("name"));// }// }} catch (Exception e) {logger.error(">>>>>>testCreaedIndex error: " + e.getMessage(), e);} finally {try {conn.close();} catch (Exception e) {}try {connection.setAutoFlushCommands(true);} catch (Exception e) {}}}

        在生产级工程应用上,我强烈建议大家如果你使用的是redisearch请使用redis-cluster模式。lettusearch天然支持redis-cluster的RedisModuleClient。

        由于我们使用的是sentinel,平时需要索引的数据不是太多,不过20GB内数据,因此我们使用的是sentinel模式,也是一样可以使用的。

        代码的核心导读如下。

代码核心导读

        通过已经运行在工程内的redisTemplate获取到它当前的master节点的连接信息,密码可以通过外部配置文件(nacos里配置的信息)喂入后建立一个RedisModulesClient的连接。

        该连接使用async的异步方式进行连接。

        使用List<RedisFuture<?>> 来承载要重复执行的“redisearch”的命令。

        先建立索引,再喂入数据。

        喂入的每一条数据是一个Map<String, Object>键值。假设一条JSON数据有10个字段,这一个Map就需要有10条数据,每一条数据的key对应着在建立索引时的以下语句中的SCHEMA中映射的那些个Field:

connection.sync().ftCreate(INDEX, options, SCHEMA);

        每一行Map的键是字段名,值就是你的数据的内容了,可以是数字、文字甚至再套一个复杂对象等各种内容。

        把一个Map当成一行JSON结构的数据,塞入一个List<RedisFuture<?>>然后在最后使用:connection.flushCommands();提交并且异步等到LettuceFutures.awaitAll(connection.getTimeout(), futures.toArray(new RedisFuture[0]));这个时间后结束所有的“建立索引”的过程。

        这条awaitAll你们可以认为是监测整个List<RedisFuture<?>>的提交过程是否结束了。

        上述UT运行后,在Redis里会生成这样的东西

 

基于redisearch的全文搜索

        搜索需求

        我们在刚才喂入的1万条数据里搜索“笑字开头或者是水字开头或者是带笑字的数据并且如果是有笑字的数据的搜索出来的排序比以水字开头的数据要靠前”。

String searchKeyWords = "笑*|水*|~笑";

        代码:

	@Testpublic void testSearch() throws Exception {RedisSentinelConnection conn = null;String host = "";int port = 6379;StringBuffer redisConnStr = new StringBuffer();try {conn = redisTemplate.getConnectionFactory().getSentinelConnection();List<RedisServer> servers = new ArrayList(conn.masters());for (RedisServer rNode : servers) {if (rNode.isMaster()) {host = rNode.getHost();port = rNode.getPort();}logger.info(">>>>>>node in rNode is [host: " + rNode.getHost() + "] [port: " + rNode.getPort() + "]");}redisConnStr.append("redis://").append(host).append(":").append(port);logger.info(">>>>>>get RedisModulesClient connection string with->" + redisConnStr.toString());RedisURIBuilder uriBuilder = RedisURIBuilder.create();uriBuilder.host(host);uriBuilder.port(port);char[] password = "xxxx超复杂".toCharArray();uriBuilder.password(password);RedisURI uri = uriBuilder.build();RedisModulesClient client = RedisModulesClient.create(uri);StatefulRedisModulesConnection<String, String> connection = client.connect();String searchKeyWords = "笑*|水*|~笑";// new String(searchKeyWords.getBytes("utf-8"), "utf-8");logger.info(">>>>>>searchKeyWords->" + searchKeyWords);RediSearchCommands<String, String> search = connection.sync();SearchOptions<String, String> options = SearchOptions.<String, String>builder().withPayloads(true).noStopWords(true).limit(0, 27).withScores(true).build();SearchResults<String, String> results = search.ftSearch("beers", searchKeyWords, options);if (results != null) {logger.info(">>>>>>get result from RediSearch size->" + results.size());for (Document<String, String> doc : results) {logger.info(">>>>>>doc:" + doc.getId() + " name->" + doc.get("name") + " and descr->"+ doc.get("descr"));}}} catch (Exception e) {logger.error(">>>>>>testCreaedIndex error: " + e.getMessage(), e);} finally {try {conn.close();} catch (Exception e) {}}}

        当上述UT运行后,我们可以得到27条结果。因为基于索引无论是基于ES还是现在基于RediSearch,搜索不可能一下把所有的命中结果都展示出来的,你的Web应用服务器性能吃不消,因此在功能设计上都是带“翻页”的,因此这个翻页就是用的以下这条语句来实现多少条记录“一翻页”的:

SearchOptions<String, String> options = SearchOptions.<String, String>builder().withPayloads(true).noStopWords(true).limit(0, 27).withScores(true).build();

        这就是为什么我们可以搜出27条数据的原因。

结束语

        全文到此结束了我们的RediSearch入门之旅,通过全文我们可以看到:RediSearch要用了好关键就是你的RediSearch的表达式要写了好。

        它的性能是远超ES的,我生产用5台ES每一台为64GB内存,8C CPU运行500万数据里搜索20个热搜,而我用6台Redis组成集群每一台Redis节点只有6-8GB内存,4C CPU,运行同样的数据和搜索词。都使用了320个spring boot客户端做并发。

        在同样并发下,竟然得到了RediSearch比ES的响应快出60%的效率。这意味着我们可以用更低的成本做到更好的事,同时可以让我们的这个企业级架构变得更轻、更简单

        最后还是这句话:如果你够变态,不妨自己动动手吧!

相关文章:

国内唯一一部在CentOS下正确编译安装和使用RediSearch的教程

开篇 Redis6开始增加了诸多激动人心的模块&#xff0c;特别是&#xff1a;RedisJSON和RediSearch。这两个模块已经完全成熟了。它们可以直接使用我们的生产上的Redis服务器来做全文搜索&#xff08;二级搜索&#xff09;以取得更廉价的硬件成本、同时在效率上竟然超过了Elastic…...

前端对于深拷贝和浅拷贝的应用和思考

浅拷贝 浅拷贝 &#xff1a; 浅拷贝是指对基本类型的值拷贝&#xff0c;以及对对象类型的地址拷贝。它是将数据中所有的数据引用下来&#xff0c;依旧指向同一个存放地址&#xff0c;拷贝之后的数据修改之后&#xff0c;也会影响到原数据的中的对象数据。最简单直接的浅拷贝就…...

Java基础常见面试题(三)

String 字符型常量和字符串常量的区别&#xff1f; 形式上: 字符常量是单引号引起的一个字符&#xff0c;字符串常量是双引号引起的若干个字符&#xff1b; 含义上: 字符常量相当于一个整型值( ASCII 值)&#xff0c;可以参加表达式运算&#xff1b;字符串常量代表一个地址值…...

C++设计模式(13)——装饰模式

亦称&#xff1a; 装饰者模式、装饰器模式、Wrapper、Decorator 意图 装饰模式是一种结构型设计模式&#xff0c; 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。 问题 假设你正在开发一个提供通知功能的库&#xff0c; 其他程序可使用它向用户发…...

ESP-01S通过AT指令上报数据到阿里云物模型

ESP-01S使用AT指令上报数据到阿里云物模型 上篇文章介绍了如何用AT指令连接阿里云并进行通信&#xff1a;https://blog.csdn.net/weixin_46251230/article/details/128995530 但最终需要将传感器数据上报到云平台显示&#xff0c;所以需要建立阿里云物模型 阿里云平台建立物…...

【强化学习】马尔可夫决策过程MDP

1.马尔可夫决策过程MDP 1.1 MDP五元组 MDP<S,A,P,R,γ>MDP<\mathcal{S},\mathcal{A},\mathcal{P},\mathcal{R},\mathcal{\gamma}>MDP<S,A,P,R,γ>&#xff0c;其中&#xff1a; S\mathcal{S}S&#xff1a;状态空间A\mathcal{A}A&#xff1a;动作空间P\mathc…...

刘润:五维思考,让你站得更高、看得更远

原标题&#xff1a;刘润&#xff1a;五维思考&#xff0c;让你站得更高、看得更远 前言&#xff1a;遇到问题时&#xff0c;有的人很快就能想明白&#xff0c;有的人需要很久才能想明白&#xff0c;还有的人始终都想不明白。 而且&#xff0c;那些很快就能想明白的人&#xff0…...

从运维角度看微服务 k8s部署微服务【偏理论】【AL】

从运维角度看微服务 & 部署微服务【偏理论】 1、微服务的特点 服务组件化&#xff1a; 每个服务独立开发、部署&#xff0c;有效避免一个服务的修改引起整个系统重新部署。 技术栈灵活&#xff1a; 约定通信方式&#xff0c;使得服务本身功能实现对技术要求不再那么敏感。…...

专题 | 防抖和节流

一 防抖&#xff1a;单位时间内&#xff0c;频繁触发事件&#xff0c;只执行最后一次 场景&#xff1a;搜索框搜索输入&#xff08;利用定时器&#xff0c;每次触发先清掉以前的定时器&#xff0c;从新开始&#xff09; 节流&#xff1a;单位时间内&#xff0c;频繁触发事件&…...

C++入门:重载运算符和重载函数

C 允许在同一作用域中的某个函数和运算符指定多个定义&#xff0c;分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明&#xff0c;但是它们的参数列表和定义&#xff08;实现&#xff09;不相同。当您调用一个重载函…...

conda 新建虚拟环境 等等

1&#xff1a;conda create -n env_name package_name #创建名为env_name的新环境&#xff0c;并在该环境下安装名为package_name 的包&#xff0c;例如&#xff1a;conda create -n Arg python3.8 # 创建名字为Arg python为3.8版本的虚拟环境2&#xff1a; conda activate env…...

【C++:STL之栈和队列 | 模拟实现 | 优先级队列 】

目录 1. stack的介绍和使用 1.1 stack的介绍 1.2 stack的使用 2 栈的模拟实现 3 queue的介绍和使用 3.1 queue的介绍 3.2 queue的使用 4 queue的模拟实现 5 deque的介绍 5.1deque的原理介绍 5.2 deque的缺陷 5.3 为什么选择deque作为stack和queue的底层默认容器 6 p…...

基于SpringBoot+Vue的疫苗预约管理系统(Java项目)

【辰兮要努力】&#xff1a;hello你好我是辰兮&#xff0c;很高兴你能来阅读&#xff0c;昵称是希望自己能不断精进&#xff0c;向着优秀程序员前行&#xff01; 博客来源于项目以及编程中遇到的问题总结&#xff0c;偶尔会有读书分享&#xff0c;我会陆续更新Java前端、后台、…...

华为OD机试 - 计算网络信号(Python),真题含思路

计算网络信号 题目 网络信号经过传递会逐层衰减,且遇到阻隔物无法直接穿透,在此情况下需要计算某个位置的网络信号值。 注意:网络信号可以绕过阻隔物 array[m][n] 的二维数组代表网格地图,array[i][j] = 0 代表 i 行 j 列是空旷位置;array[i][j] = x ( x 为正整数)代表 i …...

【Spring】注解实现IOC操作,你理解了吗?

作者&#xff1a;狮子也疯狂 专栏&#xff1a;《spring开发》 坚持做好每一步&#xff0c;幸运之神自然会驾凌在你的身上 专栏推荐&#xff1a;写文章刚刚起步&#xff0c;各个专栏的知识点后续会补充完善&#xff0c;不断更新好文&#xff0c;希望大 家支持一下。 专栏名字El…...

微搭低代码从入门到精通01-总体介绍

在过去我们开发小程序&#xff0c;要学习各类知识。比如前端知识、后端知识、服务器知识及各种中间件及数据库的知识。 要想学会这些知识&#xff0c;既需要投入大量的学习时间&#xff0c;而且要经过相当的实践才可以掌握。 如果立志从事开发行业&#xff0c;投入精力去学习…...

类的继承

类的继承&#xff1a;一个类继承另一个类&#xff0c;自动拥有这个类的属性和方法&#xff0c;类似于包含与被包含的关系。被继承的类称为父类--子类则是继承父类的类。一个父类可以有多个子类&#xff1b;一个子类可以有多个父类&#xff08;多继承&#xff09;问题创建子类时…...

应用场景一:西门子PLC通过桥接器连接MQTT服务器

应用场景描述&#xff1a; 云平台、MES等数据采集、设备管理系统&#xff0c;需要通过MQTT的方式&#xff0c;上传和下发数据&#xff0c;MQTT服务器可以获取PLC的实时状态数据&#xff0c;也可以下发控制指令。桥接器提供4G、WIFI和有线三种连接方式。 网络拓扑&#xff1a;…...

计算机组成原理(四)

1.理解存储器的分类方法&#xff1b;理解存储器的层次结构&#xff1b;熟悉存储器的几个技术指标&#xff08;主要是存储容量、存取时间、存取周期、存储器带宽等&#xff09;&#xff1b; 存储器分类方法&#xff1a;   按与CPU的连接和功能分类&#xff1a;     主存储…...

状态机设计举例

⭐本专栏针对FPGA进行入门学习&#xff0c;从数电中常见的逻辑代数讲起&#xff0c;结合Verilog HDL语言学习与仿真&#xff0c;主要对组合逻辑电路与时序逻辑电路进行分析与设计&#xff0c;对状态机FSM进行剖析与建模。 &#x1f525;文章和代码已归档至【Github仓库&#xf…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...