{
  "schema_version": 2,
  "id": "develop/clients/go/prob",
  "title": "Probabilistic data types",
  "url": "https://redis.io/docs/latest/develop/clients/go/prob/",
  "summary": "Learn how to use approximate calculations with Redis.",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-09T09:42:14+01:00",
  "page_type": "content",
  "content_hash": "514ed78cb3bbca7f93d9df5b8fa8eaaa8f73773ba8feb862b1b77eb17e9e47be",
  "sections": [
    {
      "id": "set-operations",
      "title": "Set operations",
      "role": "content",
      "text": "Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."
    },
    {
      "id": "set-membership",
      "title": "Set membership",
      "role": "content",
      "text": "[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\n\nSet membership: Use Bloom filter to efficiently track item presence with minimal memory overhead\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example below.\n\nSet membership with deletion: Use Cuckoo filter for membership testing with support for item removal\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."
    },
    {
      "id": "set-cardinality",
      "title": "Set cardinality",
      "role": "content",
      "text": "A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\nSet cardinality: Estimate distinct item count using HyperLogLog with minimal memory usage\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."
    },
    {
      "id": "statistics",
      "title": "Statistics",
      "role": "content",
      "text": "Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."
    },
    {
      "id": "frequency",
      "title": "Frequency",
      "role": "content",
      "text": "A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\n\nFrequency estimation: Track approximate item frequencies using Count-min sketch with configurable accuracy\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."
    },
    {
      "id": "quantiles",
      "title": "Quantiles",
      "role": "content",
      "text": "A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the\n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set.\n\nQuantile estimation: Calculate percentiles and quantiles from data streams using t-digest\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."
    },
    {
      "id": "ranking",
      "title": "Ranking",
      "role": "content",
      "text": "A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topk().reserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist.\n\nRanking by frequency: Track top k most frequent items in a data stream using Top-K\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "set-membership-ex0",
      "language": "go",
      "code": "res1, err := rdb.BFMAdd(\n\t\tctx,\n\t\t\"recorded_users\",\n\t\t\"andy\", \"cameron\", \"david\", \"michelle\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> [true true true true]\n\n\tres2, err := rdb.BFExists(ctx,\n\t\t\"recorded_users\", \"cameron\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> true\n\n\tres3, err := rdb.BFExists(ctx, \"recorded_users\", \"kaitlyn\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> false",
      "section_id": "set-membership"
    },
    {
      "id": "set-membership-ex1",
      "language": "go",
      "code": "res4, err := rdb.CFAdd(ctx, \"other_users\", \"paolo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> true\n\n\tres5, err := rdb.CFAdd(ctx, \"other_users\", \"kaitlyn\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> true\n\n\tres6, err := rdb.CFAdd(ctx, \"other_users\", \"rachel\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> true\n\n\tres7, err := rdb.CFMExists(ctx,\n\t\t\"other_users\", \"paolo\", \"rachel\", \"andy\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> [true true false]\n\n\tres8, err := rdb.CFDel(ctx, \"other_users\", \"paolo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> true\n\n\tres9, err := rdb.CFExists(ctx, \"other_users\", \"paolo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> false",
      "section_id": "set-membership"
    },
    {
      "id": "set-cardinality-ex0",
      "language": "go",
      "code": "res10, err := rdb.PFAdd(\n\t\tctx,\n\t\t\"group:1\",\n\t\t\"andy\", \"cameron\", \"david\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> 1\n\n\tres11, err := rdb.PFCount(ctx, \"group:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> 3\n\n\tres12, err := rdb.PFAdd(ctx,\n\t\t\"group:2\",\n\t\t\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> 1\n\n\tres13, err := rdb.PFCount(ctx, \"group:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> 4\n\n\tres14, err := rdb.PFMerge(\n\t\tctx,\n\t\t\"both_groups\",\n\t\t\"group:1\", \"group:2\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> OK\n\n\tres15, err := rdb.PFCount(ctx, \"both_groups\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15) // >>> 7",
      "section_id": "set-cardinality"
    },
    {
      "id": "frequency-ex0",
      "language": "go",
      "code": "// Specify that you want to keep the counts within 0.01\n\t// (0.1%) of the true value with a 0.005 (0.05%) chance\n\t// of going outside this limit.\n\tres16, err := rdb.CMSInitByProb(ctx, \"items_sold\", 0.01, 0.005).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> OK\n\n\t// The parameters for `CMSIncrBy()` are two lists. The count\n\t// for each item in the first list is incremented by the\n\t// value at the same index in the second list.\n\tres17, err := rdb.CMSIncrBy(ctx, \"items_sold\",\n\t\t\"bread\", 300,\n\t\t\"tea\", 200,\n\t\t\"coffee\", 200,\n\t\t\"beer\", 100,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17) // >>> [300 200 200 100]\n\n\tres18, err := rdb.CMSIncrBy(ctx, \"items_sold\",\n\t\t\"bread\", 100,\n\t\t\"coffee\", 150,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> [400 350]\n\n\tres19, err := rdb.CMSQuery(ctx,\n\t\t\"items_sold\",\n\t\t\"bread\", \"tea\", \"coffee\", \"beer\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> [400 200 350 100]",
      "section_id": "frequency"
    },
    {
      "id": "quantiles-ex0",
      "language": "go",
      "code": "res20, err := rdb.TDigestCreate(ctx, \"male_heights\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> OK\n\n\tres21, err := rdb.TDigestAdd(ctx, \"male_heights\",\n\t\t175.5, 181, 160.8, 152, 177, 196, 164,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21) // >>> OK\n\n\tres22, err := rdb.TDigestMin(ctx, \"male_heights\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res22) // >>> 152\n\n\tres23, err := rdb.TDigestMax(ctx, \"male_heights\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res23) // >>> 196\n\n\tres24, err := rdb.TDigestQuantile(ctx, \"male_heights\", 0.75).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res24) // >>> [181]\n\n\t// Note that the CDF value for 181 is not exactly\n\t// 0.75. Both values are estimates.\n\tres25, err := rdb.TDigestCDF(ctx, \"male_heights\", 181).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"%.4f\\n\", res25[0]) // >>> 0.7857\n\n\tres26, err := rdb.TDigestCreate(ctx, \"female_heights\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res26) // >>> OK\n\n\tres27, err := rdb.TDigestAdd(ctx, \"female_heights\",\n\t\t155.5, 161, 168.5, 170, 157.5, 163, 171,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res27) // >>> OK\n\n\tres28, err := rdb.TDigestQuantile(ctx, \"female_heights\", 0.75).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res28) // >>> [170]\n\n\tres29, err := rdb.TDigestMerge(ctx, \"all_heights\",\n\t\tnil,\n\t\t\"male_heights\", \"female_heights\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res29) // >>> OK\n\n\tres30, err := rdb.TDigestQuantile(ctx, \"all_heights\", 0.75).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res30) // >>> [175.5]",
      "section_id": "quantiles"
    },
    {
      "id": "ranking-ex0",
      "language": "go",
      "code": "// Create a TopK filter that keeps track of the top 3 items\n\tres31, err := rdb.TopKReserve(ctx, \"top_3_songs\", 3).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res31) // >>> OK\n\n\t// Add some items to the filter\n\tres32, err := rdb.TopKIncrBy(ctx,\n\t\t\"top_3_songs\",\n\t\t\"Starfish Trooper\", 3000,\n\t\t\"Only one more time\", 1850,\n\t\t\"Rock me, Handel\", 1325,\n\t\t\"How will anyone know?\", 3890,\n\t\t\"Average lover\", 4098,\n\t\t\"Road to everywhere\", 770,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res32)\n\t// >>> [   Rock me, Handel Only one more time ]\n\n\tres33, err := rdb.TopKList(ctx, \"top_3_songs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res33)\n\t// >>> [Average lover How will anyone know? Starfish Trooper]\n\n\t// Query the count for specific items\n\tres34, err := rdb.TopKQuery(\n\t\tctx,\n\t\t\"top_3_songs\",\n\t\t\"Starfish Trooper\", \"Road to everywhere\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res34) // >>> [true false]",
      "section_id": "ranking"
    }
  ]
}
