@@ -9,7 +9,7 @@ The Python interface to the Redis key-value store.
9
9
[ ![ codecov] ( https://codecov.io/gh/redis/redis-py/branch/master/graph/badge.svg?token=yenl5fzxxr )] ( https://codecov.io/gh/redis/redis-py )
10
10
[ ![ Total alerts] ( https://img.shields.io/lgtm/alerts/g/redis/redis-py.svg?logo=lgtm&logoWidth=18 )] ( https://lgtm.com/projects/g/redis/redis-py/alerts/ )
11
11
12
- [ Installation] ( ## installation ) | [ Contributing] ( ## contributing ) | [ Getting Started] ( ## getting-started ) | [ Connecting To Redis] ( # #connecting-to-redis)
12
+ [ Installation] ( #installation ) | [ Contributing] ( #contributing ) | [ Getting Started] ( #getting-started ) | [ Connecting To Redis] ( #connecting-to-redis )
13
13
14
14
---------------------------------------------
15
15
@@ -948,8 +948,272 @@ C 3
948
948
949
949
### Cluster Mode
950
950
951
- redis-py does not currently support [ Cluster
952
- Mode] ( https://redis.io/topics/cluster-tutorial ) .
951
+ redis-py is now supports cluster mode and provides a client for
952
+ [ Redis Cluster] ( < https://redis.io/topics/cluster-tutorial > ) .
953
+
954
+ The cluster client is based on Grokzen's
955
+ [ redis-py-cluster] ( https://github.com/Grokzen/redis-py-cluster ) , has added bug
956
+ fixes, and now supersedes that library. Support for these changes is thanks to
957
+ his contributions.
958
+
959
+
960
+ ** Create RedisCluster:**
961
+
962
+ Connecting redis-py to a Redis Cluster instance(s) requires at a minimum a
963
+ single node for cluster discovery. There are multiple ways in which a cluster
964
+ instance can be created:
965
+
966
+ - Using 'host' and 'port' arguments:
967
+
968
+ ``` pycon
969
+ >>> from redis.cluster import RedisCluster as Redis
970
+ >>> rc = Redis(host='localhost', port=6379)
971
+ >>> print(rc.get_nodes())
972
+ [[host=127.0.0.1,port=6379,name=127.0.0.1:6379,server_type=primary,redis_connection=Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=0>>>], [host=127.0.0.1,port=6378,name=127.0.0.1:6378,server_type=primary,redis_connection=Redis<ConnectionPool<Connection<host=127.0.0.1,port=6378,db=0>>>], [host=127.0.0.1,port=6377,name=127.0.0.1:6377,server_type=replica,redis_connection=Redis<ConnectionPool<Connection<host=127.0.0.1,port=6377,db=0>>>]]
973
+ ```
974
+ - Using the Redis URL specification:
975
+
976
+ ``` pycon
977
+ >>> from redis.cluster import RedisCluster as Redis
978
+ >>> rc = Redis.from_url("redis://localhost:6379/0")
979
+ ```
980
+
981
+ - Directly, via the ClusterNode class:
982
+
983
+ ``` pycon
984
+ >>> from redis.cluster import RedisCluster as Redis
985
+ >>> from redis.cluster import ClusterNode
986
+ >>> nodes = [ClusterNode('localhost', 6379), ClusterNode('localhost', 6378)]
987
+ >>> rc = Redis(startup_nodes=nodes)
988
+ ```
989
+
990
+ When a RedisCluster instance is being created it first attempts to establish a
991
+ connection to one of the provided startup nodes. If none of the startup nodes
992
+ are reachable, a 'RedisClusterException' will be thrown.
993
+ After a connection to the one of the cluster's nodes is established, the
994
+ RedisCluster instance will be initialized with 3 caches:
995
+ a slots cache which maps each of the 16384 slots to the node/s handling them,
996
+ a nodes cache that contains ClusterNode objects (name, host, port, redis connection)
997
+ for all of the cluster's nodes, and a commands cache contains all the server
998
+ supported commands that were retrieved using the Redis 'COMMAND' output.
999
+
1000
+ RedisCluster instance can be directly used to execute Redis commands. When a
1001
+ command is being executed through the cluster instance, the target node(s) will
1002
+ be internally determined. When using a key-based command, the target node will
1003
+ be the node that holds the key's slot.
1004
+ Cluster management commands and other commands that are not key-based have a
1005
+ parameter called 'target_nodes' where you can specify which nodes to execute
1006
+ the command on. In the absence of target_nodes, the command will be executed
1007
+ on the default cluster node. As part of cluster instance initialization, the
1008
+ cluster's default node is randomly selected from the cluster's primaries, and
1009
+ will be updated upon reinitialization. Using r.get_default_node(), you can
1010
+ get the cluster's default node, or you can change it using the
1011
+ 'set_default_node' method.
1012
+
1013
+ The 'target_nodes' parameter is explained in the following section,
1014
+ 'Specifying Target Nodes'.
1015
+
1016
+ ``` pycon
1017
+ >>> # target-nodes: the node that holds 'foo1's key slot
1018
+ >>> rc.set('foo1', 'bar1')
1019
+ >>> # target-nodes: the node that holds 'foo2's key slot
1020
+ >>> rc.set('foo2', 'bar2')
1021
+ >>> # target-nodes: the node that holds 'foo1's key slot
1022
+ >>> print(rc.get('foo1'))
1023
+ b'bar'
1024
+ >>> # target-node: default-node
1025
+ >>> print(rc.keys())
1026
+ [b'foo1']
1027
+ >>> # target-node: default-node
1028
+ >>> rc.ping()
1029
+ ```
1030
+
1031
+ ** Specifying Target Nodes:**
1032
+
1033
+ As mentioned above, all non key-based RedisCluster commands accept the kwarg
1034
+ parameter 'target_nodes' that specifies the node/nodes that the command should
1035
+ be executed on.
1036
+ The best practice is to specify target nodes using RedisCluster class's node
1037
+ flags: PRIMARIES, REPLICAS, ALL_NODES, RANDOM. When a nodes flag is passed
1038
+ along with a command, it will be internally resolved to the relevant node/s.
1039
+ If the nodes topology of the cluster changes during the execution of a command,
1040
+ the client will be able to resolve the nodes flag again with the new topology
1041
+ and attempt to retry executing the command.
1042
+
1043
+ ``` pycon
1044
+ >>> from redis.cluster import RedisCluster as Redis
1045
+ >>> # run cluster-meet command on all of the cluster's nodes
1046
+ >>> rc.cluster_meet('127.0.0.1', 6379, target_nodes=Redis.ALL_NODES)
1047
+ >>> # ping all replicas
1048
+ >>> rc.ping(target_nodes=Redis.REPLICAS)
1049
+ >>> # ping a random node
1050
+ >>> rc.ping(target_nodes=Redis.RANDOM)
1051
+ >>> # get the keys from all cluster nodes
1052
+ >>> rc.keys(target_nodes=Redis.ALL_NODES)
1053
+ [b'foo1', b'foo2']
1054
+ >>> # execute bgsave in all primaries
1055
+ >>> rc.bgsave(Redis.PRIMARIES)
1056
+ ```
1057
+
1058
+ You could also pass ClusterNodes directly if you want to execute a command on a
1059
+ specific node / node group that isn't addressed by the nodes flag. However, if
1060
+ the command execution fails due to cluster topology changes, a retry attempt
1061
+ will not be made, since the passed target node/s may no longer be valid, and
1062
+ the relevant cluster or connection error will be returned.
1063
+
1064
+ ``` pycon
1065
+ >>> node = rc.get_node('localhost', 6379)
1066
+ >>> # Get the keys only for that specific node
1067
+ >>> rc.keys(target_nodes=node)
1068
+ >>> # get Redis info from a subset of primaries
1069
+ >>> subset_primaries = [node for node in rc.get_primaries() if node.port > 6378]
1070
+ >>> rc.info(target_nodes=subset_primaries)
1071
+ ```
1072
+
1073
+ In addition, the RedisCluster instance can query the Redis instance of a
1074
+ specific node and execute commands on that node directly. The Redis client,
1075
+ however, does not handle cluster failures and retries.
1076
+
1077
+ ``` pycon
1078
+ >>> cluster_node = rc.get_node(host='localhost', port=6379)
1079
+ >>> print(cluster_node)
1080
+ [host=127.0.0.1,port=6379,name=127.0.0.1:6379,server_type=primary,redis_connection=Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=0>>>]
1081
+ >>> r = cluster_node.redis_connection
1082
+ >>> r.client_list()
1083
+ [{'id': '276', 'addr': '127.0.0.1:64108', 'fd': '16', 'name': '', 'age': '0', 'idle': '0', 'flags': 'N', 'db': '0', 'sub': '0', 'psub': '0', 'multi': '-1', 'qbuf': '26', 'qbuf-free': '32742', 'argv-mem': '10', 'obl': '0', 'oll': '0', 'omem': '0', 'tot-mem': '54298', 'events': 'r', 'cmd': 'client', 'user': 'default'}]
1084
+ >>> # Get the keys only for that specific node
1085
+ >>> r.keys()
1086
+ [b'foo1']
1087
+ ```
1088
+
1089
+ ** Multi-key commands:**
1090
+
1091
+ Redis supports multi-key commands in Cluster Mode, such as Set type unions or
1092
+ intersections, mset and mget, as long as the keys all hash to the same slot.
1093
+ By using RedisCluster client, you can use the known functions (e.g. mget, mset)
1094
+ to perform an atomic multi-key operation. However, you must ensure all keys are
1095
+ mapped to the same slot, otherwise a RedisClusterException will be thrown.
1096
+ Redis Cluster implements a concept called hash tags that can be used in order
1097
+ to force certain keys to be stored in the same hash slot, see
1098
+ [ Keys hash tag] ( https://redis.io/topics/cluster-spec#keys-hash-tags ) .
1099
+ You can also use nonatomic for some of the multikey operations, and pass keys
1100
+ that aren't mapped to the same slot. The client will then map the keys to the
1101
+ relevant slots, sending the commands to the slots' node owners. Non-atomic
1102
+ operations batch the keys according to their hash value, and then each batch is
1103
+ sent separately to the slot's owner.
1104
+
1105
+ ``` pycon
1106
+ # Atomic operations can be used when all keys are mapped to the same slot
1107
+ >>> rc.mset({'{foo}1': 'bar1', '{foo}2': 'bar2'})
1108
+ >>> rc.mget('{foo}1', '{foo}2')
1109
+ [b'bar1', b'bar2']
1110
+ # Non-atomic multi-key operations splits the keys into different slots
1111
+ >>> rc.mset_nonatomic({'foo': 'value1', 'bar': 'value2', 'zzz': 'value3')
1112
+ >>> rc.mget_nonatomic('foo', 'bar', 'zzz')
1113
+ [b'value1', b'value2', b'value3']
1114
+ ```
1115
+
1116
+ ** Cluster PubSub:**
1117
+
1118
+ When a ClusterPubSub instance is created without specifying a node, a single
1119
+ node will be transparently chosen for the pubsub connection on the
1120
+ first command execution. The node will be determined by:
1121
+ 1 . Hashing the channel name in the request to find its keyslot
1122
+ 2 . Selecting a node that handles the keyslot: If read_from_replicas is
1123
+ set to true, a replica can be selected.
1124
+
1125
+ * Known limitations with pubsub:*
1126
+
1127
+ Pattern subscribe and publish do not currently work properly due to key slots.
1128
+ If we hash a pattern like fo* we will receive a keyslot for that string but
1129
+ there are endless possibilities for channel names based on this pattern -
1130
+ unknowable in advance. This feature is not disabled but the commands are not
1131
+ currently recommended for use.
1132
+ See [ redis-py-cluster documentation] ( https://redis-py-cluster.readthedocs.io/en/stable/pubsub.html )
1133
+ for more.
1134
+
1135
+ ``` pycon
1136
+ >>> p1 = rc.pubsub()
1137
+ # p1 connection will be set to the node that holds 'foo' keyslot
1138
+ >>> p1.subscribe('foo')
1139
+ # p2 connection will be set to node 'localhost:6379'
1140
+ >>> p2 = rc.pubsub(rc.get_node('localhost', 6379))
1141
+ ```
1142
+
1143
+ ** Read Only Mode**
1144
+
1145
+ By default, Redis Cluster always returns MOVE redirection response on accessing
1146
+ a replica node. You can overcome this limitation and scale read commands by
1147
+ triggering READONLY mode.
1148
+
1149
+ To enable READONLY mode pass read_from_replicas=True to RedisCluster
1150
+ constructor. When set to true, read commands will be assigned between the
1151
+ primary and its replications in a Round-Robin manner.
1152
+
1153
+ READONLY mode can be set at runtime by calling the readonly() method with
1154
+ target_nodes='replicas', and read-write access can be restored by calling the
1155
+ readwrite() method.
1156
+
1157
+ ``` pycon
1158
+ >>> from cluster import RedisCluster as Redis
1159
+ # Use 'debug' log level to print the node that the command is executed on
1160
+ >>> rc_readonly = Redis(startup_nodes=startup_nodes,
1161
+ read_from_replicas=True)
1162
+ >>> rc_readonly.set('{foo}1', 'bar1')
1163
+ >>> for i in range(0, 4):
1164
+ # Assigns read command to the slot's hosts in a Round-Robin manner
1165
+ >>> rc_readonly.get('{foo}1')
1166
+ # set command would be directed only to the slot's primary node
1167
+ >>> rc_readonly.set('{foo}2', 'bar2')
1168
+ # reset READONLY flag
1169
+ >>> rc_readonly.readwrite(target_nodes='replicas')
1170
+ # now the get command would be directed only to the slot's primary node
1171
+ >>> rc_readonly.get('{foo}1')
1172
+ ```
1173
+
1174
+ ** Cluster Pipeline**
1175
+
1176
+ ClusterPipeline is a subclass of RedisCluster that provides support for Redis
1177
+ pipelines in cluster mode.
1178
+ When calling the execute() command, all the commands are grouped by the node
1179
+ on which they will be executed, and are then executed by the respective nodes
1180
+ in parallel. The pipeline instance will wait for all the nodes to respond
1181
+ before returning the result to the caller. Command responses are returned as a
1182
+ list sorted in the same order in which they were sent.
1183
+ Pipelines can be used to dramatically increase the throughput of Redis Cluster
1184
+ by significantly reducing the the number of network round trips between the
1185
+ client and the server.
1186
+
1187
+ ``` pycon
1188
+ >>> with rc.pipeline() as pipe:
1189
+ >>> pipe.set('foo', 'value1')
1190
+ >>> pipe.set('bar', 'value2')
1191
+ >>> pipe.get('foo')
1192
+ >>> pipe.get('bar')
1193
+ >>> print(pipe.execute())
1194
+ [True, True, b'value1', b'value2']
1195
+ >>> pipe.set('foo1', 'bar1').get('foo1').execute()
1196
+ [True, b'bar1']
1197
+ ```
1198
+ Please note:
1199
+ - RedisCluster pipelines currently only support key-based commands.
1200
+ - The pipeline gets its 'read_from_replicas' value from the cluster's parameter.
1201
+ Thus, if read from replications is enabled in the cluster instance, the pipeline
1202
+ will also direct read commands to replicas.
1203
+ - The 'transcation' option is NOT supported in cluster-mode. In non-cluster mode,
1204
+ the 'transaction' option is available when executing pipelines. This wraps the
1205
+ pipeline commands with MULTI/EXEC commands, and effectively turns the pipeline
1206
+ commands into a single transaction block. This means that all commands are
1207
+ executed sequentially without any interruptions from other clients. However,
1208
+ in cluster-mode this is not possible, because commands are partitioned
1209
+ according to their respective destination nodes. This means that we can not
1210
+ turn the pipeline commands into one transaction block, because in most cases
1211
+ they are split up into several smaller pipelines.
1212
+
1213
+
1214
+ See [ Redis Cluster tutorial] ( https://redis.io/topics/cluster-tutorial ) and
1215
+ [ Redis Cluster specifications] ( https://redis.io/topics/cluster-spec )
1216
+ to learn more about Redis Cluster.
953
1217
954
1218
### Author
955
1219
0 commit comments