diff --git a/lib/kredis/namespace.rb b/lib/kredis/namespace.rb
index 5a8f871..8d138a7 100644
--- a/lib/kredis/namespace.rb
+++ b/lib/kredis/namespace.rb
@@ -1,14 +1,31 @@
 # frozen_string_literal: true
 
 module Kredis::Namespace
-  def namespace=(namespace)
-    Thread.current[:kredis_namespace] = namespace
-  end
+  attr_accessor :global_namespace
 
   def namespace
-    Thread.current[:kredis_namespace]
+    if global_namespace
+      if value = thread_namespace
+        "#{global_namespace}:#{value}"
+      else
+        global_namespace
+      end
+    else
+      thread_namespace
+    end
+  end
+
+  def thread_namespace
+    Thread.current[:kredis_thread_namespace]
   end
 
+  def thread_namespace=(value)
+    Thread.current[:kredis_thread_namespace] = value
+  end
+
+  # Backward compatibility
+  alias_method :namespace=, :thread_namespace=
+
   def namespaced_key(key)
     namespace ? "#{namespace}:#{key}" : key
   end
diff --git a/lib/kredis/railtie.rb b/lib/kredis/railtie.rb
index 013cf8e..dc22231 100644
--- a/lib/kredis/railtie.rb
+++ b/lib/kredis/railtie.rb
@@ -5,18 +5,8 @@ class Kredis::Railtie < ::Rails::Railtie
 
   initializer "kredis.testing" do
     ActiveSupport.on_load(:active_support_test_case) do
-      $kredis_parallel_worker = nil
-      parallelize_setup { |worker| $kredis_parallel_worker = worker }
-
-      setup do
-        @original_namespace = Kredis.namespace
-        Kredis.namespace = [ @original_namespace, :test, $kredis_parallel_worker ].compact.join("-")
-      end
-
-      teardown do
-        Kredis.clear_all
-        Kredis.namespace = @original_namespace
-      end
+      parallelize_setup { |worker| Kredis.global_namespace = [ Kredis.global_namespace, :test, worker ].compact.join("-") }
+      teardown { Kredis.clear_all }
     end
   end
 
diff --git a/test/attributes_test.rb b/test/attributes_test.rb
index 6fa0b4e..c886dc4 100644
--- a/test/attributes_test.rb
+++ b/test/attributes_test.rb
@@ -120,12 +120,12 @@ class AttributesTest < ActiveSupport::TestCase
 
   test "proxy with custom string key" do
     @person.nothing.set "everything"
-    assert_equal "everything", Kredis.redis.get("something:else")
+    assert_equal "everything", Kredis.redis.get(Kredis.namespaced_key("something:else"))
   end
 
   test "proxy with custom proc key" do
     @person.something.set "everything"
-    assert_equal "everything", Kredis.redis.get("person:8:something")
+    assert_equal "everything", Kredis.redis.get(Kredis.namespaced_key("person:8:something"))
   end
 
   test "list" do
@@ -135,17 +135,17 @@ class AttributesTest < ActiveSupport::TestCase
 
   test "list with custom proc key" do
     @person.names_with_custom_key_via_lambda.append(%w[ david kasper ])
-    assert_equal %w[ david kasper ], Kredis.redis.lrange("person:8:names_customized", 0, -1)
+    assert_equal %w[ david kasper ], Kredis.redis.lrange(Kredis.namespaced_key("person:8:names_customized"), 0, -1)
   end
 
   test "list with custom method key" do
     @person.names_with_custom_key_via_method.append(%w[ david kasper ])
-    assert_equal %w[ david kasper ], Kredis.redis.lrange("some-generated-key", 0, -1)
+    assert_equal %w[ david kasper ], Kredis.redis.lrange(Kredis.namespaced_key("some-generated-key"), 0, -1)
   end
 
   test "list with default proc value" do
     assert_equal %w[ Random Jason ], @person.names_with_default_via_lambda.elements
-    assert_equal %w[ Random Jason ], Kredis.redis.lrange("people:8:names_with_default_via_lambda", 0, -1)
+    assert_equal %w[ Random Jason ], Kredis.redis.lrange(Kredis.namespaced_key("people:8:names_with_default_via_lambda"), 0, -1)
   end
 
   test "unique list" do
@@ -157,7 +157,7 @@ class AttributesTest < ActiveSupport::TestCase
 
   test "unique list with default proc value" do
     assert_equal %w[ Random Jason ], @person.skills_with_default_via_lambda.elements
-    assert_equal %w[ Random Jason ], Kredis.redis.lrange("people:8:skills_with_default_via_lambda", 0, -1)
+    assert_equal %w[ Random Jason ], Kredis.redis.lrange(Kredis.namespaced_key("people:8:skills_with_default_via_lambda"), 0, -1)
   end
 
   test "ordered set" do
@@ -222,7 +222,7 @@ class AttributesTest < ActiveSupport::TestCase
   end
 
   test "float with default proc value" do
-    assert_not_equal 73.2, Kredis.redis.get("people:8:height_with_default_via_lambda")
+    assert_not_equal 73.2, Kredis.redis.get(Kredis.namespaced_key("people:8:height_with_default_via_lambda"))
     assert_equal 73.2, @person.height_with_default_via_lambda.value
     assert_equal "73.2", @person.height_with_default_via_lambda.to_s
   end
@@ -321,7 +321,7 @@ class AttributesTest < ActiveSupport::TestCase
 
   test "set with default proc value" do
     assert_equal [ "Paris" ], @person.vacations_with_default_via_lambda.members
-    assert_equal [ "Paris" ], Kredis.redis.smembers("people:8:vacations_with_default_via_lambda")
+    assert_equal [ "Paris" ], Kredis.redis.smembers(Kredis.namespaced_key("people:8:vacations_with_default_via_lambda"))
   end
 
   test "json" do
@@ -332,7 +332,7 @@ class AttributesTest < ActiveSupport::TestCase
   test "json with default proc value" do
     expect = { "height" => 73.2, "weight" => 182.4, "eye_color" => "ha" }
     assert_equal expect, @person.settings_with_default_via_lambda.value
-    assert_equal expect.to_json, Kredis.redis.get("people:8:settings_with_default_via_lambda")
+    assert_equal expect.to_json, Kredis.redis.get(Kredis.namespaced_key("people:8:settings_with_default_via_lambda"))
   end
 
 
diff --git a/test/connections_test.rb b/test/connections_test.rb
index a3bc5bd..6ad32eb 100644
--- a/test/connections_test.rb
+++ b/test/connections_test.rb
@@ -4,8 +4,15 @@
 require "yaml"
 
 class ConnectionsTest < ActiveSupport::TestCase
-  setup { Kredis.connections = {} }
-  teardown { Kredis.namespace = nil }
+  setup do
+    Kredis.connections = {}
+    @original_global_namespace, Kredis.global_namespace = Kredis.global_namespace, nil
+  end
+
+  teardown do
+    Kredis.global_namespace = @original_global_namespace
+    Kredis.namespace = nil
+  end
 
   test "clear all" do
     list = Kredis.list "mylist"
diff --git a/test/migration_test.rb b/test/migration_test.rb
index 3532e3c..0ec5d41 100644
--- a/test/migration_test.rb
+++ b/test/migration_test.rb
@@ -3,12 +3,10 @@
 require "test_helper"
 
 class MigrationTest < ActiveSupport::TestCase
-  setup { @proxy = Kredis.string "new_proxy" }
-
   test "migrate_all" do
     3.times { |index| Kredis.proxy("mykey:#{index}").set "hello there #{index}" }
 
-    Kredis::Migration.migrate_all("mykey:*") { |key| key.gsub("mykey", "thykey") }
+    Kredis::Migration.migrate_all(Kredis.namespaced_key("mykey:*")) { |key| "thykey:#{key.split(":").last}" }
 
     3.times do |index|
       assert_equal "hello there #{index}", Kredis.proxy("thykey:#{index}").get
@@ -16,19 +14,25 @@ class MigrationTest < ActiveSupport::TestCase
   end
 
   test "migrate" do
+    @original_global_namespace, Kredis.global_namespace = Kredis.global_namespace, nil
+
     old_proxy = Kredis.string "old_proxy"
     old_proxy.set "hello there"
-    assert_not @proxy.assigned?
 
-    Kredis::Migration.migrate from: "old_proxy", to: @proxy.key
-    assert_equal "hello there", @proxy.value
+    new_proxy = Kredis.string "new_proxy"
+    assert_not new_proxy.assigned?
+
+    Kredis::Migration.migrate from: Kredis.namespaced_key("old_proxy"), to: "new_proxy"
+    assert_equal "hello there", new_proxy.value
     assert old_proxy.assigned?, "just copying the data"
+  ensure
+    Kredis.global_namespace = @original_global_namespace
   end
 
   test "migrate with blank keys" do
     assert_nothing_raised do
-      Kredis::Migration.migrate from: "old_key", to: nil
-      Kredis::Migration.migrate from: "old_key", to: ""
+      Kredis::Migration.migrate from: Kredis.namespaced_key("old_key"), to: nil
+      Kredis::Migration.migrate from: Kredis.namespaced_key("old_key"), to: ""
     end
   end
 
@@ -37,7 +41,7 @@ class MigrationTest < ActiveSupport::TestCase
 
     Kredis.namespace = "migrate"
 
-    Kredis::Migration.migrate from: "key", to: "key"
+    Kredis::Migration.migrate from: "#{Kredis.global_namespace}:key", to: "key"
 
     assert_equal "x", Kredis.proxy("key").get
   ensure
@@ -47,7 +51,7 @@ class MigrationTest < ActiveSupport::TestCase
   test "migrate with automatic id extraction" do
     Kredis.proxy("mykey:1").set "hey"
 
-    Kredis::Migration.migrate_all "mykey:*" do |key, id|
+    Kredis::Migration.migrate_all Kredis.namespaced_key("mykey:*") do |key, id|
       assert_equal 1, id
       key
     end
@@ -56,7 +60,7 @@ class MigrationTest < ActiveSupport::TestCase
   test "delete_all with pattern" do
     3.times { |index| Kredis.proxy("mykey:#{index}").set "hello there #{index}" }
 
-    Kredis::Migration.delete_all "mykey:*"
+    Kredis::Migration.delete_all Kredis.namespaced_key("mykey:*")
 
     3.times { |index| assert_nil Kredis.proxy("mykey:#{index}").get }
   end
@@ -64,7 +68,7 @@ class MigrationTest < ActiveSupport::TestCase
   test "delete_all with keys" do
     3.times { |index| Kredis.proxy("mykey:#{index}").set "hello there #{index}" }
 
-    Kredis::Migration.delete_all(*3.times.map { |index| "mykey:#{index}" })
+    Kredis::Migration.delete_all(*3.times.map { |index| Kredis.namespaced_key("mykey:#{index}") })
 
     3.times { |index| assert_nil Kredis.proxy("mykey:#{index}").get }
   end
diff --git a/test/namespace_test.rb b/test/namespace_test.rb
index fde7840..585244d 100644
--- a/test/namespace_test.rb
+++ b/test/namespace_test.rb
@@ -3,14 +3,15 @@
 require "test_helper"
 
 class NamespaceTest < ActiveSupport::TestCase
-  teardown { Kredis.namespace = nil }
+  teardown { Kredis.thread_namespace = nil }
 
-  test "list with namespace" do
-    Kredis.namespace = "test-1"
+  test "list with per-thread namespace" do
+    Kredis.thread_namespace = "test-1"
     list = Kredis.list "mylist"
     list.append "one"
     assert_equal [ "one" ], list.elements
 
+    # Aliased to thread_namespace= for back-compat
     Kredis.namespace = "test-2"
     list = Kredis.list "mylist"
     assert_equal [], list.elements
diff --git a/test/test_helper.rb b/test/test_helper.rb
index e7a3cd9..8ae5cb7 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -19,7 +19,8 @@ def root() Pathname.new(".") end
 class ActiveSupport::TestCase
   extend Rails::LineFiltering
 
-  teardown { Kredis.clear_all }
+  setup { Kredis.global_namespace = "kredis-test" }
+  teardown { Kredis.global_namespace = nil; Kredis.clear_all }
 
   class RedisUnavailableProxy
     def multi; yield; end