Skip to content

Commit

Permalink
Merge pull request #608 from wjohnsto/master
Browse files Browse the repository at this point in the history
Vector Search Tutorial
  • Loading branch information
PrasanKumar93 authored Sep 1, 2023
2 parents f86a708 + a3ca576 commit 5f3ebc2
Show file tree
Hide file tree
Showing 20 changed files with 1,015 additions and 124 deletions.
87 changes: 1 addition & 86 deletions docs/develop/java/getting-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,7 @@ Find tutorials, examples and technical articles that will help you to develop wi

## Getting Started

Java community has built many client libraries that you can find [here](https://redis.io/clients#java). For your first steps with Java and Redis, this article will show how to use the two main libraries: [Jedis](https://github.com/redis/jedis) and [Lettuce](https://lettuce.io/).

The blog post “[Jedis vs. Lettuce: An Exploration](https://redis.com/blog/jedis-vs-lettuce-an-exploration/)” will help you to select the best for your application; keeping in mind that both are available in Spring & SpringBoot framework.

<Tabs
defaultValue="jedis"
values={[
{label: 'Jedis', value: 'jedis'},
{label: 'Lettuce', value: 'lettuce'},
]}>
<TabItem value="jedis">
Java community has built many client libraries that you can find [here](https://redis.io/clients#java). For your first steps with Java and Redis, this article will show how to use [Jedis](https://github.com/redis/jedis), the supported Redis client for Java.

Redis is an open source, in-memory, key-value data store most commonly used as a primary database, cache, message broker, and queue. Redis delivers sub-millisecond response times, enabling fast and powerful real-time applications in industries such as gaming, fintech, ad-tech, social media, healthcare, and IoT.

Expand Down Expand Up @@ -106,53 +96,6 @@ Once you have access to the connection pool you can now get a Jedis instance and

Find more information about Java & Redis connections in the "[Redis Connect](https://github.com/redis-developer/redis-connect/tree/master/java/jedis)".

</TabItem>
<TabItem value="lettuce">

## Using Lettuce

### Step 1. Add dependencies Jedis dependency to your Maven (or Gradle) project file:

```xml
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>a
<version>6.0.1.RELEASE</version>
</dependency>
```

### Step 2. Import the Jedis classes

```java
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
```

### Step 3. Write your application code

```java
RedisClient redisClient = RedisClient.create("redis://localhost:6379/");
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisCommands<String, String> syncCommands = connection.sync();

syncCommands.set("mykey", "Hello from Lettuce!");
String value = syncCommands.get("mykey");
System.out.println( value );

syncCommands.zadd("vehicles", 0, "car");
syncCommands.zadd("vehicles", 0, "bike");
List<String> vehicles = syncCommands.zrange("vehicles", 0, -1);
System.out.println( vehicles );

connection.close();
redisClient.shutdown();
```

Find more information about Java & Redis connections in the "[Redis Connect](https://github.com/redis-developer/redis-connect/tree/master/java/lettuce)".
</TabItem>
</Tabs>

### Redis Launchpad

Redis Launchpad is like an “App Store” for Redis sample apps. You can easily find apps for your preferred frameworks and languages.
Expand Down Expand Up @@ -248,12 +191,6 @@ As developer you can use the Java client library directly in your application, o

### More developer resources

<div class="row">

<div class="col">

#### Sample Code

**[Brewdis - Product Catalog (Spring)](https://github.com/redis-developer/brewdis)**
See how to use Redis and Spring to build a product catalog with streams, hashes and Search

Expand All @@ -263,27 +200,6 @@ See how to use Spring to create multiple producer and consumers with Redis Strea
**[Rate Limiting with Vert.x](https://github.com/redis-developer/vertx-rate-limiting-redis)**
See how to use Redis Sorted Set with Vert.x to build a rate limiting service.

**[Redis Java Samples with Lettuce](https://github.com/redis-developer/vertx-rate-limiting-redis)**
Run Redis Commands from Lettuce

</div>
<div class="col col--1">
</div>

<div class="col">

#### Technical Articles

**[Getting Started with Redis Streams and Java (Lettuce)](https://redis.com/blog/getting-started-with-redis-streams-and-java/)**

**[Jedis vs. Lettuce: An Exploration](https://redis.com/blog/jedis-vs-lettuce-an-exploration/)**

</div>

</div>

---

### Redis University

### [Redis for Java Developers](https://university.redis.com/courses/ru102j/)
Expand All @@ -294,7 +210,6 @@ Redis for Java Developers teaches you how to build robust Redis client applicati
<iframe width="560" height="315" src="https://www.youtube.com/embed/CmQMdJefTjc" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

##

<div>
<a href="https://launchpad.redis.com" target="_blank" rel="noopener" className="link"> <img src="/img/launchpad.png" className="thumb" loading="lazy" alt="Redis Launchpad" /></a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ sidebar_label: Atomicity with Gears
slug: /develop/java/spring/rate-limiting/fixed-window/reactive-gears
---

:::warning LETTUCE

This tutorial uses Lettuce, which is an unsupported Redis library. For production applications, we recommend using [<u>**Jedis**</u>](https://github.com/redis/jedis)

:::

## Improving atomicity and performance with RedisGears

### What is RedisGears?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ slug: /develop/java/redis-and-spring-course/lesson_7
authors: [bsb]
---

:::warning LETTUCE

This tutorial uses Lettuce, which is an unsupported Redis library. For production applications, we recommend using [<u>**Jedis**</u>](https://github.com/redis/jedis)

:::

import Authors from '@theme/Authors';

<Authors frontMatter={frontMatter} />
Expand Down
2 changes: 1 addition & 1 deletion docs/explore/redisinsight/streams/index-streams.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Redis Streams lets you build “Kafka-like” applications, which can:

In addition, Redis Streams has the concept of a consumer group. Redis Streams consumer groups, like the similar concept in [Apache Kafka](https://kafka.apache.org/), allows client applications to consume messages in a distributed fashion (multiple clients), making it easy to scale and create highly available systems.

Let’s dive under the covers and see [Redis Streams](https://redis.io/topics/streams-intro) through the lens of RedisInsight. You will see how to use the [Lettuce Java client](https://developer.redis.com/develop/java/#using-lettuce) to publish and consume messages using consumer group.This is the first basic example that uses a single consumer.
Let’s dive under the covers and see [Redis Streams](https://redis.io/topics/streams-intro) through the lens of RedisInsight. You will see how to use the Redis to publish and consume messages using a consumer group. This is the first basic example that uses a single consumer.

## Prerequisite:

Expand Down
47 changes: 10 additions & 37 deletions docs/howtos/antipatterns/index-antipatterns.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,60 +39,33 @@ Let us look at the redis-py that uses a connection pool to manage connections to

[Learn more about redis-py](/develop/python/)

### Example #2 - Lettuce

Lettuce provides generic connection pool support.Lettuce connections are designed to be thread-safe so one connection can be shared amongst multiple threads and Lettuce connections auto-reconnection by default. While connection pooling is not necessary in most cases it can be helpful in certain use cases. Lettuce provides generic connection pooling support.

```java
RedisClient client = RedisClient.create(RedisURI.create(host, port));

GenericObjectPool<StatefulRedisConnection<String, String>> pool = ConnectionPoolSupport
.createGenericObjectPool(() -> client.connect(), new GenericObjectPoolConfig());

// executing work
try (StatefulRedisConnection<String, String> connection = pool.borrowObject()) {

RedisCommands<String, String> commands = connection.sync();
commands.multi();
commands.set("key", "value");
commands.set("key2", "value2");
commands.exec();
}

// terminating
pool.close();
client.shutdown();
```

[Learn more about Lettuce](/develop/java/?s=lettuce)

### 3. Connecting directly to Redis instances
### 2. Connecting directly to Redis instances

With a large number of clients, a reconnect flood will be able to simply overwhelm a single threaded Redis process and force a failover. Hence, it is recommended that you should use the right tool that allows you to reduce the number of open connections to your Redis server.

[Redis Enterprise DMC proxy](https://docs.redis.com/latest/rs/administering/designing-production/networking/multiple-active-proxy/) allows you to reduce the number of connections to your cache server by acting as a proxy. There are other 3rd party tool like [Twemproxy](https://github.com/twitter/twemproxy). It is a fast and lightweight proxy server that allows you to reduce the number of open connections to your Redis server. It was built primarily to reduce the number of connections to the caching servers on the backend. This, together with protocol pipelining and sharding enables you to horizontally scale your distributed caching architecture.

### 4. More than one secondary shard (Redis OSS)
### 3. More than one secondary shard (Redis OSS)

Redis OSS uses a shard-based quorum. It's advised to use at least 3 copies of the data (2 replica shards per master shard) in order to be protected from split-brain situations. In nutshell, Redis OSS solves the quorum challenge by having an odd number of shards (primary + 2 replicas).

Redis Enterprise solves the quorum challenge with an odd number of nodes. Redis Enterprise avoids a split-brain situation with only 2 copies of the data, which is more cost-efficient. In addition, the so-called ‘quorum-only node' can be used to bring a cluster up to an odd number of nodes if an additional, not necessary data node would be too expensive.

### 5. Performing single operation
### 4. Performing single operation

Performing several operations serially increases connection overhead. Instead, use [Redis Pipelining](https://redis.io/topics/pipelining). Pipelining is the process of sending multiple messages down the pipe without waiting on the reply from each - and (typically) processing the replies later when they come in.

Pipelining is completely a client side implementation. It is aimed at solving response latency issues in high network latency environments. So, the lesser the amount of time spent over the network in sending commands and reading responses, the better. This is effectively achieved by buffering. The client may (or may not) buffer the commands at the TCP stack (as mentioned in other answers) before they are sent to the server. Once they are sent to the server, the server executes them and buffers them on the server side. The benefit of the pipelining is a drastically improved protocol performance. The speedup gained by pipelining ranges from a factor of five for connections to localhost up to a factor of at least one hundred over slower internet connections.

### 6. Caching keys without TTL
### 5. Caching keys without TTL

Redis functions primarily as a key-value store. It is possible to set timeout values on these keys. Said that, a timeout expiration automatically deletes the key. Additionally, when we use commands that delete or overwrite the contents of the key, it will clear the timeout. Redis TTL command is used to get the remaining time of the key expiry in seconds. TTL returns the remaining time to live of a key that has a timeout. This introspection capability allows a Redis client to check how many seconds a given key will continue to be part of the dataset.Keys will accumulate and end up being evicted. Hence, it is recommended to set TTLs on all caching keys.

### 7. Endless Redis Replication Loop
### 6. Endless Redis Replication Loop

When attempting to replicate a very large active database over a slow or saturated link, replication never finishes due to the continuous updates. Hence, it is recommended to tune the slave and client buffers to allow for slower replication. Check out [this detailed blog](https://redis.com/blog/the-endless-redis-replication-loop-what-why-and-how-to-solve-it/).

### 8. Hot Keys
### 7. Hot Keys

Redis can easily become the core of your app’s operational data, holding valuable and frequently accessed information. However, if you centralize the access down to a few pieces of data accessed constantly, you create what is known as a hot-key problem. In a Redis cluster, the key is actually what determines where in the cluster that data is stored. The data is stored in one single, primary location based off of hashing that key. So, when you access a single key over and over again, you’re actually accessing a single node/shard over and over again. Let’s put it another way—if you have a cluster of 99 nodes and you have a single key that gets a million requests in a second, all million of those requests will be going to a single node, not spread across the other 98 nodes.

Expand All @@ -104,7 +77,7 @@ Redis even provides tools to find where your hot keys are located. Use redis-cli

When possible, the best defence is to avoid the development pattern that is creating the situation. Writing the data to multiple keys that reside in different shards will allow you to access the same data more frequently. In nutshell, having specific keys that are accessed with every client operation. Hence, it's recommended to shard out hot keys using hashing algorithms. You can set policy to LFU and run redis-cli --hotkeys to determine.

### 9. Using Keys command
### 8. Using Keys command

In Redis, the KEYS command can be used to perform exhaustive pattern matching on all stored keys. This is not advisable, as running this on an instance with a large number of keys could take a long time to complete, and will slow down the Redis instance in the process. In the relational world, this is equivalent to running an unbound query (SELECT...FROM without a WHERE clause). Execute this type of operation with care, and take necessary measures to ensure that your tenants are not performing a KEYS operation from within their application code. Use SCAN, which spreads the iteration over many calls, not tying up your whole server at one time.

Expand All @@ -115,17 +88,17 @@ Scaning keyspace by keyname is an extremely slow operation and will run O(N) wit
2SQL: SELECT * FROM orders WHERE make=ford AND model=explorer"
```
### 10. Running Ephemeral Redis as a primary database
### 9. Running Ephemeral Redis as a primary database
Redis is often used as a primary storage engine for applications. Unlike using Redis as a cache, using Redis as a primary database requires two extra features to be effective. Any primary database should really be highly available. If a cache goes down, then generally your application is in a brown-out state. If a primary database goes down, your application also goes down. Similarly, if a cache goes down and you restart it empty, that’s no big deal. For a primary database, though, that’s a huge deal. Redis can handle these situations easily, but they generally require a different configuration than running as a cache. Redis as a primary database is great, but you’ve got to support it by turning on the right features.
With Redis open source, you need to set up Redis Sentinel for high availability. In Redis Enterprise, it’s a core feature that you just need to turn on when creating the database. As for durability, both Redis Enterprise and open source Redis provide durability through AOF or snapshotting so your instance(s) start back up the way you left them.
### 11. Storing JSON blobs in a string
### 10. Storing JSON blobs in a string
Microservices written in several languages may not marshal/unmarshal JSON in a consistent manner. Application logic will be required to lock/watch a key for atomic updates. JSON manipulation is often a very compute costly operation. Hence, it is recommended to use HASH data structure and also Redis JSON.
### 12. Translating a table or JSON to a HASH without considering query pattern
### 11. Translating a table or JSON to a HASH without considering query pattern
The only query mechanism is a SCAN which requires reading the data structure and limits filtering to the MATCH directive. It is recommended to store the table or JSON as a string. Break out the indexes into reverse indexes using a SET or SORTED SET and point back to the key for the string.
Using SELECT command and multiple databases inside one Redis instance
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Sample data
const products = [
{
name: 'Puma Men Race Black Watch',
price: 150,
quality: 5,
popularity: 8,
},
{
name: 'Puma Men Top Fluctuation Red Black Watch',
price: 180,
quality: 7,
popularity: 6,
},
{
name: 'Inkfruit Women Behind Cream Tshirt',
price: 5,
quality: 9,
popularity: 7,
},
];

const dataWithAttributes = products.map((product) => ({
x: product.price,
y: product.quality,
label: product.name,
}));

const product1Point = { x: products[0].price, y: products[0].quality };
const product2Point = { x: products[1].price, y: products[1].quality };
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-argument */

const ctxCosine = document
.getElementById('productChartCosine')
.getContext('2d');

function cosineSimilarity(point1, point2) {
let dotProduct = point1.x * point2.x + point1.y * point2.y;
let magnitudePoint1 = Math.sqrt(
Math.pow(point1.x, 2) + Math.pow(point1.y, 2),
);
let magnitudePoint2 = Math.sqrt(
Math.pow(point2.x, 2) + Math.pow(point2.y, 2),
);
return dotProduct / (magnitudePoint1 * magnitudePoint2);
}

const cosineSim = cosineSimilarity(product1Point, product2Point);

const scatterChartCosine = new Chart(ctxCosine, {
type: 'scatter',
data: {
datasets: [
{
label: 'Products',
data: dataWithAttributes,
pointBackgroundColor: ['black', 'red', 'bisque'],
pointRadius: 5,
},
{
label: 'Vector for Product-1',
data: [{ x: 0, y: 0 }, product1Point],
showLine: true,
fill: false,
borderColor: 'black',
pointRadius: [0, 5],
lineTension: 0,
},
{
label: 'Vector for Product-2',
data: [{ x: 0, y: 0 }, product2Point],
showLine: true,
fill: false,
borderColor: 'red',
pointRadius: [0, 5],
lineTension: 0,
},
],
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: `Cosine Similarity between Product-1 and Product-2 is ${cosineSim}`,
},
},
scales: {
x: {
type: 'linear',
position: 'bottom',
title: {
display: true,
text: 'Price ($)',
},
ticks: {
beginAtZero: true,
},
min: 0, // Ensure it starts from 0
},
y: {
title: {
display: true,
text: 'Quality (1-10)',
},
ticks: {
beginAtZero: true,
},
min: 0, // Ensure it starts from 0
},
},
tooltips: {
callbacks: {
title: function (tooltipItem, data) {
return data.datasets[0].data[tooltipItem[0].index].label;
},
},
},
},
});
Loading

0 comments on commit 5f3ebc2

Please sign in to comment.