По-долу е даден прост пример, който да ви даде представа колко лесно е да използвате някои от усъвършенстваните структури от данни на Redis - в този случай Redis Lists:Пълният изходен код на този пример може да се види онлайн
using var redisClient = new RedisClient();
//Create a 'strongly-typed' API that makes all Redis Value operations to apply against Shippers
IRedisTypedClient<Shipper> redis = redisClient.As<Shipper>();
//Redis lists implement IList<T> while Redis sets implement ICollection<T>
var currentShippers = redis.Lists["urn:shippers:current"];
var prospectiveShippers = redis.Lists["urn:shippers:prospective"];
currentShippers.Add(
new Shipper {
Id = redis.GetNextSequence(),
CompanyName = "Trains R Us",
DateCreated = DateTime.UtcNow,
ShipperType = ShipperType.Trains,
UniqueRef = Guid.NewGuid()
});
currentShippers.Add(
new Shipper {
Id = redis.GetNextSequence(),
CompanyName = "Planes R Us",
DateCreated = DateTime.UtcNow,
ShipperType = ShipperType.Planes,
UniqueRef = Guid.NewGuid()
});
var lameShipper = new Shipper {
Id = redis.GetNextSequence(),
CompanyName = "We do everything!",
DateCreated = DateTime.UtcNow,
ShipperType = ShipperType.All,
UniqueRef = Guid.NewGuid()
};
currentShippers.Add(lameShipper);
Dump("ADDED 3 SHIPPERS:", currentShippers);
currentShippers.Remove(lameShipper);
Dump("REMOVED 1:", currentShippers);
prospectiveShippers.Add(
new Shipper {
Id = redis.GetNextSequence(),
CompanyName = "Trucks R Us",
DateCreated = DateTime.UtcNow,
ShipperType = ShipperType.Automobiles,
UniqueRef = Guid.NewGuid()
});
Dump("ADDED A PROSPECTIVE SHIPPER:", prospectiveShippers);
redis.PopAndPushBetweenLists(prospectiveShippers, currentShippers);
Dump("CURRENT SHIPPERS AFTER POP n' PUSH:", currentShippers);
Dump("PROSPECTIVE SHIPPERS AFTER POP n' PUSH:", prospectiveShippers);
var poppedShipper = redis.PopFromList(currentShippers);
Dump("POPPED a SHIPPER:", poppedShipper);
Dump("CURRENT SHIPPERS AFTER POP:", currentShippers);
//reset sequence and delete all lists
redis.SetSequence(0);
redis.Remove(currentShippers, prospectiveShippers);
Dump("DELETING CURRENT AND PROSPECTIVE SHIPPERS:", currentShippers);
ПРИМЕР ИЗХОД:
ADDED 3 SHIPPERS:
Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5
Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06
Id:3,CompanyName:We do everything!,ShipperType:All,DateCreated:2010-01-31T11:53:37.8009371Z,UniqueRef:d0c249bbbaf84da39fc4afde1b34e332
REMOVED 1:
Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5
Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06
ADDED A PROSPECTIVE SHIPPER:
Id:4,CompanyName:Trucks R Us,ShipperType:Automobiles,DateCreated:2010-01-31T11:53:37.8539401Z,UniqueRef:67d7d4947ebc4b0ba5c4d42f5d903bec
CURRENT SHIPPERS AFTER POP n' PUSH:
Id:4,CompanyName:Trucks R Us,ShipperType:Automobiles,DateCreated:2010-01-31T11:53:37.8539401Z,UniqueRef:67d7d4947ebc4b0ba5c4d42f5d903bec
Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5
Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06
PROSPECTIVE SHIPPERS AFTER POP n' PUSH:
POPPED a SHIPPER:
Id:2,CompanyName:Planes R Us,ShipperType:Planes,DateCreated:2010-01-31T11:53:37.799937Z,UniqueRef:e02a73191f4b4e7a9c44eef5b5965d06
CURRENT SHIPPERS AFTER POP:
Id:4,CompanyName:Trucks R Us,ShipperType:Automobiles,DateCreated:2010-01-31T11:53:37.8539401Z,UniqueRef:67d7d4947ebc4b0ba5c4d42f5d903bec
Id:1,CompanyName:Trains R Us,ShipperType:Trains,DateCreated:2010-01-31T11:53:37.7169323Z,UniqueRef:d17c5db0415b44b2ac5da7b6ebd780f5
DELETING CURRENT AND PROSPECTIVE SHIPPERS:
Още примери са налични в [страницата с примери за RedisExamples Redis] и в изчерпателния тестов пакет
Скорост №
Едно от най-добрите неща за Redis е скоростта – тя е бърза.
Този пример по-долу съхранява и получава цялата база данни на Northwind (3202 записа) за по-малко 1,2 секунди - никога не сме го правили толкова бързо!
(Изпълнява се в VS.NET/R# единичен тест на 3-годишен iMac)
using var client = new RedisClient();
var before = DateTime.Now;
client.StoreAll(NorthwindData.Categories);
client.StoreAll(NorthwindData.Customers);
client.StoreAll(NorthwindData.Employees);
client.StoreAll(NorthwindData.Shippers);
client.StoreAll(NorthwindData.Orders);
client.StoreAll(NorthwindData.Products);
client.StoreAll(NorthwindData.OrderDetails);
client.StoreAll(NorthwindData.CustomerCustomerDemos);
client.StoreAll(NorthwindData.Regions);
client.StoreAll(NorthwindData.Territories);
client.StoreAll(NorthwindData.EmployeeTerritories);
Console.WriteLine("Took {0}ms to store the entire Northwind database ({1} records)",
(DateTime.Now - before).TotalMilliseconds, totalRecords);
before = DateTime.Now;
var categories = client.GetAll<Category>();
var customers = client.GetAll<Customer>();
var employees = client.GetAll<Employee>();
var shippers = client.GetAll<Shipper>();
var orders = client.GetAll<Order>();
var products = client.GetAll<Product>();
var orderDetails = client.GetAll<OrderDetail>();
var customerCustomerDemos = client.GetAll<CustomerCustomerDemo>();
var regions = client.GetAll<Region>();
var territories = client.GetAll<Territory>();
var employeeTerritories = client.GetAll<EmployeeTerritory>();
Console.WriteLine("Took {0}ms to get the entire Northwind database ({1} records)",
(DateTime.Now - before).TotalMilliseconds, totalRecords);
/*
== EXAMPLE OUTPUT ==
Took 1020.0583ms to store the entire Northwind database (3202 records)
Took 132.0076ms to get the entire Northwind database (3202 records)
*/
Забележка:Общото необходимо време включва допълнителна операция Redis за всеки запис за съхраняване на идентификатора в Redis набор за всеки тип, както и сериализиране и десериализиране на всеки запис с помощта на TypeSerializer на Service Stack.
Lex операции #
Добавени са новите операции за сортирани набори ZRANGEBYLEX, които ви позволяват да заявите лексически сортиран набор. Добра витрина за това е налична на autocomplete.redis.io.
Тези нови операции са налични като 1:1 съпоставяне с redis-сървър на IRedisNativeClient
:
public interface IRedisNativeClient
{
...
byte[][] ZRangeByLex(string setId, string min, string max, int? skip, int? take);
long ZLexCount(string setId, string min, string max);
long ZRemRangeByLex(string setId, string min, string max);
}
И по-удобните за потребителя API под IRedisClient
:
public interface IRedisClient
{
...
List<string> SearchSortedSet(string setId, string start=null, string end=null);
long SearchSortedSetCount(string setId, string start=null, string end=null);
long RemoveRangeFromSortedSetBySearch(string setId, string start=null, string end=null);
}
Точно като съпоставящите версии на NuGet, Redis използва [
char за изразяване на приобщаване и (
char за изключителност. Тъй като IRedisClient
API по подразбиране са включващи търсения, тези два API са еднакви:
Redis.SearchSortedSetCount("zset", "a", "c")
Redis.SearchSortedSetCount("zset", "[a", "[c")
Като алтернатива можете да посочите една или и двете граници да бъдат изключителни, като използвате (
префикс, напр.:
Redis.SearchSortedSetCount("zset", "a", "(c")
Redis.SearchSortedSetCount("zset", "(a", "(c")
Още примери за API са налични в LexTests.cs.
HyperLog API №
Развитието на сървъра Redis (достъпно при излизането на v3.0) включва гениален алгоритъм за приближаване на уникалните елементи в набор с максимална ефективност на пространството и времето. За подробности относно това как работи вижте блога на създателя на Redis Салваторе, който го обяснява много подробно. По същество това ви позволява да поддържате ефективен начин за броене и сливане на уникални елементи в набор, без да се налага да съхранявате неговите елементи. Прост пример за това в действие:
redis.AddToHyperLog("set1", "a", "b", "c");
redis.AddToHyperLog("set1", "c", "d");
var count = redis.CountHyperLog("set1"); //4
redis.AddToHyperLog("set2", "c", "d", "e", "f");
redis.MergeHyperLogs("mergedset", "set1", "set2");
var mergeCount = redis.CountHyperLog("mergedset"); //6
API за сканиране #
Redis v2.8 въведе красива нова операция SCAN, която осигурява оптимална стратегия за преминаване на целия набор от ключове на екземпляр на redis в блокове с управляван размер, използвайки само курсор от страна на клиента и без въвеждане на каквото и да е състояние на сървъра. Това е алтернатива с по-висока производителност и трябва да се използва вместо KEYS в кода на приложението. SCAN и свързаните с него операции за преминаване към членове на набори, сортирани набори и хешове вече са налични в Redis Client в следните API:
public interface IRedisClient
{
...
IEnumerable<string> ScanAllKeys(string pattern = null, int pageSize = 1000);
IEnumerable<string> ScanAllSetItems(string setId, string pattern = null, int pageSize = 1000);
IEnumerable<KeyValuePair<string, double>> ScanAllSortedSetItems(string setId, string pattern = null, int pageSize = 1000);
IEnumerable<KeyValuePair<string, string>> ScanAllHashEntries(string hashId, string pattern = null, int pageSize = 1000);
}
public interface IRedisClientAsync
{
IAsyncEnumerable<string> ScanAllKeysAsync(string pattern = null, int pageSize, CancellationToken ct);
IAsyncEnumerable<string> ScanAllSetItemsAsync(string setId, string pattern = null, int pageSize, CancellationToken ct);
IAsyncEnumerable<KeyValuePair<string, double>> ScanAllSortedSetItemsAsync(string setId, string pattern = null, int pageSize, ct);
IAsyncEnumerable<KeyValuePair<string, string>> ScanAllHashEntriesAsync(string hashId, string pattern = null, int pageSize, ct);
}
//Low-level API
public interface IRedisNativeClient
{
...
ScanResult Scan(ulong cursor, int count = 10, string match = null);
ScanResult SScan(string setId, ulong cursor, int count = 10, string match = null);
ScanResult ZScan(string setId, ulong cursor, int count = 10, string match = null);
ScanResult HScan(string hashId, ulong cursor, int count = 10, string match = null);
}
public interface IRedisNativeClientAsync
{
ValueTask<ScanResult> ScanAsync(ulong cursor, int count = 10, string match = null, CancellationToken ct);
ValueTask<ScanResult> SScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken ct);
ValueTask<ScanResult> ZScanAsync(string setId, ulong cursor, int count = 10, string match = null, CancellationToken ct);
ValueTask<ScanResult> HScanAsync(string hashId, ulong cursor, int count = 10, string match = null, CancellationToken ct);
}
IRedisClient
предоставя API от по-високо ниво, който абстрахира курсора на клиента, за да изложи мързелива Enumerable последователност, за да осигури оптимален начин за поточно предаване на сканирани резултати, който се интегрира добре с LINQ, напр.:
var scanUsers = Redis.ScanAllKeys("urn:User:*");
var sampleUsers = scanUsers.Take(10000).ToList(); //Stop after retrieving 10000 user keys